Decompiled source of Scare Crow v1.0.0

TCF.dll

Decompiled 5 hours ago
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using GameNetcodeStuff;
using Unity.Netcode;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace TCF;

[BepInPlugin("com.tonysmoons.tcf", "TCF", "1.0.0")]
public class Plugin : BaseUnityPlugin
{
	public const string PluginGuid = "com.tonysmoons.tcf";

	public const string PluginName = "TCF";

	public const string PluginVersion = "1.0.0";

	internal static ManualLogSource Log;

	private void Awake()
	{
		Log = ((BaseUnityPlugin)this).Logger;
		Log.LogInfo((object)"TCF loaded.");
	}
}
public class ScarecrowAI : EnemyAI
{
	private const int StateRoaming = 0;

	private const int StateInvestigating = 1;

	private const int StateChasing = 2;

	private const int StateChokeHold = 3;

	private const int StateFleeing = 4;

	private const int StateWatching = 5;

	private const int StateDead = 6;

	[Header("Detection")]
	[Tooltip("Max distance (units) a player can be spotted at. Line-of-sight is always required on top of this - see the class summary.")]
	[Min(0f)]
	public float detectionRange = 35f;

	[Tooltip("Field-of-view cone width (degrees) used for spotting players, same meaning as EnemyAI.CheckLineOfSightForPlayer's Width parameter.")]
	[Min(0f)]
	public float detectionWidth = 65f;

	[Tooltip("AISearchRoutine search width for the random outdoor wander used while Roaming/Watching.")]
	[Min(0f)]
	public float wanderSearchWidth = 50f;

	[Header("Movement Speeds")]
	[Tooltip("NavMeshAgent speed while Roaming.")]
	[Min(0f)]
	public float roamSpeed = 3f;

	[Tooltip("NavMeshAgent speed while Investigating a lost target's last-known position.")]
	[Min(0f)]
	public float investigateSpeed = 4.5f;

	[Tooltip("NavMeshAgent speed while Chasing a clearly-seen player.")]
	[Min(0f)]
	public float chaseSpeed = 7f;

	[Tooltip("Roam speed used once Enraged (after the first successful kill).")]
	[Min(0f)]
	public float enragedRoamSpeed = 5f;

	[Tooltip("Chase speed used once Enraged.")]
	[Min(0f)]
	public float enragedChaseSpeed = 11f;

	[Header("Facing")]
	[Tooltip("Visual model transform rotated to face the current target while Chasing/Choke Holding. Falls back to this object's own transform if left unset.")]
	public Transform modelRoot;

	[Tooltip("Degrees/sec turned to face the target.")]
	[Min(0f)]
	public float turnSpeed = 260f;

	[Header("Investigating")]
	[Tooltip("Seconds spent moving toward a lost target's last-known position before giving up and resuming Roaming/Watching.")]
	[Min(0f)]
	public float investigateTimeout = 8f;

	[Header("Choke Hold Attack")]
	[Tooltip("Distance (units) at which the Scarecrow stops chasing and grabs its target.")]
	[Min(0f)]
	public float chokeHoldRange = 2.5f;

	[Tooltip("Seconds the choke hold sequence lasts before the kill. Not used once Enraged - the kill becomes instant instead.")]
	[Min(0f)]
	public float chokeHoldDuration = 15f;

	[Tooltip("REQUIRED for the hold to visually lock the victim in place. Exact position/rotation the victim's body is pinned to for the whole choke hold - assign a child Transform positioned in the Scarecrow's grip.")]
	public Transform chokeHoldVictimPosition;

	[Tooltip("Dedicated Animator driving the choke-hold attack animation. Assign any Animator (with its own Controller) here to swap the attack animation without touching code - can also point at the same Animator as Creature Animator if you'd rather drive it from one Controller.")]
	public Animator chokeHoldAnimator;

	[Tooltip("Bool parameter set on Choke Hold Animator for the duration of the hold (true = playing, false = idle/released). Change this if your Animator Controller names the parameter differently.")]
	public string chokeHoldAnimatorBoolParam = "ChokeHold";

	[Tooltip("Cause of death reported for the choke-hold / enraged instant kill.")]
	public CauseOfDeath killCauseOfDeath = (CauseOfDeath)17;

	[Tooltip("Death ragdoll animation index passed to KillPlayer (base game's playerRagdolls array index) - pick whichever built-in ragdoll looks best for a decapitation.")]
	[Min(0f)]
	public int killDeathAnimation;

	[Header("Enraged (after the first kill)")]
	[Tooltip("Trigger fired once on Creature Animator the moment the Scarecrow becomes Enraged.")]
	public string enragedTriggerParam = "Enraged";

	[Header("Fleeing / Watching (after being stunned)")]
	[Tooltip("NavMeshAgent speed while Fleeing from whoever just stunned it.")]
	[Min(0f)]
	public float fleeSpeed = 9f;

	[Tooltip("Seconds spent Fleeing before settling into Watching.")]
	[Min(0f)]
	public float fleeDuration = 5f;

	[Tooltip("While Watching, the minimum distance the Scarecrow tries to keep from any player who has stunned it before.")]
	[Min(0f)]
	public float watchKeepDistance = 20f;

	[Header("Audio")]
	[Tooltip("Possible sounds played once (for everyone) the moment this instance spawns - one is picked at random.")]
	public List<AudioClip> spawnSFX = new List<AudioClip>();

	[Tooltip("AudioSource the spawn sound plays on. Falls back to creatureSFX if left unset.")]
	public AudioSource spawnAudioSource;

	[Tooltip("Played once (for everyone) the moment a choke hold begins.")]
	public AudioClip chokeHoldStartSFX;

	[Tooltip("Played once (for everyone) the moment a kill (choke hold or enraged instant kill) lands.")]
	public AudioClip killSFX;

	private AISearchRoutine wanderSearch;

	private int lockedTargetId = -1;

	private Vector3 investigatePosition;

	private float investigateTimer;

	private bool isChokeHolding;

	private bool chokeHoldKillTriggered;

	private float chokeHoldTimer;

	private PlayerControllerB chokeHoldVictim;

	private bool isEnraged;

	private bool hasBeenStunned;

	private float fleeTimer;

	private readonly HashSet<int> blacklistedPlayerIds = new HashSet<int>();

	private Transform FacingTransform
	{
		get
		{
			if (!((Object)(object)modelRoot != (Object)null))
			{
				return ((Component)this).transform;
			}
			return modelRoot;
		}
	}

	public override void Start()
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Expected O, but got Unknown
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		((EnemyAI)this).Start();
		if (!base.isOutside)
		{
			Debug.LogWarning((object)(base.enemyType.enemyName + ": Scarecrow's EnemyType asset must have \"Is Outside Enemy\" checked - it is designed to never operate inside the facility."));
		}
		wanderSearch = new AISearchRoutine
		{
			searchWidth = wanderSearchWidth
		};
		base.agent.speed = roamSpeed;
		PlaySpawnSound();
		if (((NetworkBehaviour)this).IsOwner)
		{
			((EnemyAI)this).StartSearch(((Component)this).transform.position, wanderSearch);
		}
	}

	private void PlaySpawnSound()
	{
		AudioSource val = (((Object)(object)spawnAudioSource != (Object)null) ? spawnAudioSource : base.creatureSFX);
		if (spawnSFX != null && spawnSFX.Count != 0 && !((Object)(object)val == (Object)null))
		{
			AudioClip val2 = spawnSFX[Random.Range(0, spawnSFX.Count)];
			val.PlayOneShot(val2);
			WalkieTalkie.TransmitOneShotAudio(val, val2, 1f);
		}
	}

	public override void Update()
	{
		((EnemyAI)this).Update();
		if (base.isEnemyDead)
		{
			return;
		}
		if (isChokeHolding)
		{
			HoldVictimInPlace();
			if (((NetworkBehaviour)this).IsOwner && !chokeHoldKillTriggered && (Object)(object)chokeHoldVictim != (Object)null)
			{
				chokeHoldTimer -= Time.deltaTime;
				if (chokeHoldTimer <= 0f)
				{
					chokeHoldKillTriggered = true;
					PerformKillServerRpc((int)chokeHoldVictim.playerClientId);
				}
			}
		}
		if (base.currentBehaviourStateIndex == 2 || base.currentBehaviourStateIndex == 3)
		{
			FaceTarget(base.targetPlayer);
		}
	}

	private void HoldVictimInPlace()
	{
		//IL_0052: 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)
		if (!((Object)(object)chokeHoldVictim == (Object)null) && !((Object)(object)chokeHoldVictimPosition == (Object)null))
		{
			if ((Object)(object)chokeHoldVictim.thisController != (Object)null)
			{
				((Collider)chokeHoldVictim.thisController).enabled = false;
			}
			((Component)chokeHoldVictim).transform.position = chokeHoldVictimPosition.position;
			((Component)chokeHoldVictim).transform.rotation = chokeHoldVictimPosition.rotation;
		}
	}

	private void FaceTarget(PlayerControllerB target)
	{
		//IL_0017: 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_0022: 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_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: 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)
		if (!((Object)(object)target == (Object)null))
		{
			Transform facingTransform = FacingTransform;
			Vector3 val = ((Component)target).transform.position - facingTransform.position;
			val.y = 0f;
			if (!(((Vector3)(ref val)).sqrMagnitude < 0.0001f))
			{
				Quaternion val2 = Quaternion.LookRotation(val);
				facingTransform.rotation = Quaternion.RotateTowards(facingTransform.rotation, val2, turnSpeed * Time.deltaTime);
			}
		}
	}

	public override void DoAIInterval()
	{
		((EnemyAI)this).DoAIInterval();
		if (base.isEnemyDead || !((NetworkBehaviour)this).IsOwner)
		{
			return;
		}
		switch (base.currentBehaviourStateIndex)
		{
		case 0:
			DoRoamingInterval();
			break;
		case 1:
			DoInvestigatingInterval();
			break;
		case 2:
			DoChasingInterval();
			break;
		case 3:
			if ((Object)(object)chokeHoldVictim == (Object)null || chokeHoldVictim.isPlayerDead)
			{
				ReleaseChokeHoldLocal();
				ReturnToDefaultState();
			}
			break;
		case 4:
			DoFleeingInterval();
			break;
		case 5:
			DoWatchingInterval();
			break;
		}
	}

	private void ReturnToDefaultState()
	{
		((EnemyAI)this).SwitchToBehaviourState(hasBeenStunned ? 5 : 0);
	}

	private void DoRoamingInterval()
	{
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		base.agent.speed = (isEnraged ? enragedRoamSpeed : roamSpeed);
		PlayerControllerB val = FindVisiblePlayer(detectionRange, detectionWidth);
		if ((Object)(object)val != (Object)null)
		{
			BeginChase(val);
		}
		else if (!wanderSearch.inProgress)
		{
			((EnemyAI)this).StartSearch(((Component)this).transform.position, wanderSearch);
		}
	}

	private void DoInvestigatingInterval()
	{
		//IL_0071: 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)
		base.agent.speed = investigateSpeed;
		if ((Object)(object)base.targetPlayer != (Object)null && HasLineOfSightTo(base.targetPlayer) && ((EnemyAI)this).PlayerIsTargetable(base.targetPlayer, false, false, true))
		{
			BeginChase(base.targetPlayer);
			return;
		}
		investigateTimer -= base.AIIntervalTime;
		if (investigateTimer <= 0f || Vector3.Distance(((Component)this).transform.position, investigatePosition) < 1.5f)
		{
			SetTargetPlayer(-1);
			ReturnToDefaultState();
			return;
		}
		PlayerControllerB val = FindVisiblePlayer(detectionRange, detectionWidth);
		if ((Object)(object)val != (Object)null)
		{
			BeginChase(val);
		}
	}

	private void DoChasingInterval()
	{
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)base.targetPlayer == (Object)null || base.targetPlayer.isPlayerDead || !((EnemyAI)this).PlayerIsTargetable(base.targetPlayer, false, false, true))
		{
			SetTargetPlayer(-1);
			ReturnToDefaultState();
			return;
		}
		base.agent.speed = (isEnraged ? enragedChaseSpeed : chaseSpeed);
		if (!HasLineOfSightTo(base.targetPlayer))
		{
			BeginInvestigate(((Component)base.targetPlayer).transform.position);
			return;
		}
		((EnemyAI)this).SetDestinationToPosition(((Component)base.targetPlayer).transform.position, true);
		if (Vector3.Distance(((Component)this).transform.position, ((Component)base.targetPlayer).transform.position) <= chokeHoldRange)
		{
			if (isEnraged)
			{
				TriggerInstantKill(base.targetPlayer);
			}
			else
			{
				BeginChokeHold(base.targetPlayer);
			}
		}
	}

	private void DoFleeingInterval()
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: 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_008f: Unknown result type (might be due to invalid IL or missing references)
		base.agent.speed = fleeSpeed;
		fleeTimer -= base.AIIntervalTime;
		if (fleeTimer <= 0f)
		{
			((EnemyAI)this).SwitchToBehaviourState(5);
			return;
		}
		PlayerControllerB val = FindNearestBlacklistedPlayer();
		if ((Object)(object)val != (Object)null && Vector3.Distance(((Component)this).transform.position, ((Component)val).transform.position) < watchKeepDistance)
		{
			Transform val2 = ((EnemyAI)this).ChooseFarthestNodeFromPosition(((Component)val).transform.position, true, 0, false, 50, -1);
			if ((Object)(object)val2 != (Object)null)
			{
				((EnemyAI)this).SetDestinationToPosition(val2.position, true);
			}
		}
	}

	private void DoWatchingInterval()
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: 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_005b: 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)
		base.agent.speed = (isEnraged ? enragedRoamSpeed : roamSpeed);
		PlayerControllerB val = FindNearestBlacklistedPlayer();
		if ((Object)(object)val != (Object)null && Vector3.Distance(((Component)this).transform.position, ((Component)val).transform.position) < watchKeepDistance)
		{
			Transform val2 = ((EnemyAI)this).ChooseFarthestNodeFromPosition(((Component)val).transform.position, true, 0, false, 50, -1);
			if ((Object)(object)val2 != (Object)null)
			{
				((EnemyAI)this).SetDestinationToPosition(val2.position, true);
			}
		}
		else if (!wanderSearch.inProgress)
		{
			((EnemyAI)this).StartSearch(((Component)this).transform.position, wanderSearch);
		}
		PlayerControllerB val3 = FindVisiblePlayer(detectionRange, detectionWidth);
		if ((Object)(object)val3 != (Object)null)
		{
			BeginChase(val3);
		}
	}

	private PlayerControllerB FindNearestBlacklistedPlayer()
	{
		//IL_0044: 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)
		PlayerControllerB result = null;
		float num = float.MaxValue;
		for (int i = 0; i < StartOfRound.Instance.allPlayerScripts.Length; i++)
		{
			PlayerControllerB val = StartOfRound.Instance.allPlayerScripts[i];
			if (!((Object)(object)val == (Object)null) && !val.isPlayerDead && blacklistedPlayerIds.Contains((int)val.playerClientId))
			{
				float num2 = Vector3.Distance(((Component)this).transform.position, ((Component)val).transform.position);
				if (num2 < num)
				{
					num = num2;
					result = val;
				}
			}
		}
		return result;
	}

	private PlayerControllerB FindVisiblePlayer(float range, float width)
	{
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: 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_006f: 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_0088: 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_0098: 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_009c: 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_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: 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)
		PlayerControllerB result = null;
		float num = float.MaxValue;
		for (int i = 0; i < StartOfRound.Instance.allPlayerScripts.Length; i++)
		{
			PlayerControllerB val = StartOfRound.Instance.allPlayerScripts[i];
			if ((Object)(object)val == (Object)null || !((EnemyAI)this).PlayerIsTargetable(val, false, false, true) || blacklistedPlayerIds.Contains((int)val.playerClientId))
			{
				continue;
			}
			Vector3 position = base.eye.position;
			Vector3 val2 = (((Object)(object)val.gameplayCamera != (Object)null) ? ((Component)val.gameplayCamera).transform.position : ((Component)val).transform.position);
			float num2 = Vector3.Distance(position, val2);
			if (!(num2 > range))
			{
				Vector3 val3 = val2 - position;
				if (!(Vector3.Angle(base.eye.forward, val3) > width) && !Physics.Linecast(position, val2, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1) && num2 < num)
				{
					num = num2;
					result = val;
				}
			}
		}
		return result;
	}

	private bool HasLineOfSightTo(PlayerControllerB player)
	{
		//IL_0011: 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_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null)
		{
			return false;
		}
		Vector3 position = base.eye.position;
		Vector3 val = (((Object)(object)player.gameplayCamera != (Object)null) ? ((Component)player.gameplayCamera).transform.position : ((Component)player).transform.position);
		return !Physics.Linecast(position, val, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1);
	}

	private void BeginChase(PlayerControllerB player)
	{
		((EnemyAI)this).StopSearch(wanderSearch, false);
		((EnemyAI)this).SetMovingTowardsTargetPlayer(player);
		SetTargetPlayer((int)player.playerClientId);
		((EnemyAI)this).SwitchToBehaviourState(2);
	}

	private void BeginInvestigate(Vector3 lastKnownPosition)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: 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)
		base.movingTowardsTargetPlayer = false;
		investigatePosition = lastKnownPosition;
		investigateTimer = investigateTimeout;
		((EnemyAI)this).SetDestinationToPosition(investigatePosition, true);
		((EnemyAI)this).SwitchToBehaviourState(1);
	}

	private void SetTargetPlayer(int playerObjectId)
	{
		if (lockedTargetId != playerObjectId)
		{
			SetTargetPlayerServerRpc(playerObjectId);
		}
	}

	[ServerRpc(RequireOwnership = false)]
	private void SetTargetPlayerServerRpc(int playerObjectId)
	{
		SetTargetPlayerClientRpc(playerObjectId);
	}

	[ClientRpc]
	private void SetTargetPlayerClientRpc(int playerObjectId)
	{
		lockedTargetId = playerObjectId;
		base.targetPlayer = ((playerObjectId >= 0 && (Object)(object)StartOfRound.Instance != (Object)null && playerObjectId < StartOfRound.Instance.allPlayerScripts.Length) ? StartOfRound.Instance.allPlayerScripts[playerObjectId] : null);
	}

	private void BeginChokeHold(PlayerControllerB target)
	{
		StartChokeHoldServerRpc((int)target.playerClientId);
	}

	[ServerRpc(RequireOwnership = false)]
	private void StartChokeHoldServerRpc(int playerObjectId)
	{
		StartChokeHoldClientRpc(playerObjectId);
	}

	[ClientRpc]
	private void StartChokeHoldClientRpc(int playerObjectId)
	{
		if ((Object)(object)StartOfRound.Instance == (Object)null || playerObjectId < 0 || playerObjectId >= StartOfRound.Instance.allPlayerScripts.Length)
		{
			return;
		}
		PlayerControllerB val = StartOfRound.Instance.allPlayerScripts[playerObjectId];
		if (!((Object)(object)val == (Object)null) && !val.isPlayerDead)
		{
			chokeHoldVictim = val;
			chokeHoldTimer = chokeHoldDuration;
			chokeHoldKillTriggered = false;
			isChokeHolding = true;
			base.inSpecialAnimation = true;
			base.inSpecialAnimationWithPlayer = val;
			val.inAnimationWithEnemy = (EnemyAI)(object)this;
			val.inSpecialInteractAnimation = true;
			val.snapToServerPosition = true;
			if ((Object)(object)chokeHoldAnimator != (Object)null && !string.IsNullOrEmpty(chokeHoldAnimatorBoolParam))
			{
				chokeHoldAnimator.SetBool(chokeHoldAnimatorBoolParam, true);
			}
			if ((Object)(object)chokeHoldStartSFX != (Object)null && (Object)(object)base.creatureSFX != (Object)null)
			{
				base.creatureSFX.PlayOneShot(chokeHoldStartSFX);
				WalkieTalkie.TransmitOneShotAudio(base.creatureSFX, chokeHoldStartSFX, 1f);
			}
			if (((NetworkBehaviour)this).IsOwner)
			{
				base.movingTowardsTargetPlayer = false;
				base.moveTowardsDestination = false;
				base.agent.speed = 0f;
			}
			((EnemyAI)this).SwitchToBehaviourStateOnLocalClient(3);
		}
	}

	private void TriggerInstantKill(PlayerControllerB target)
	{
		PerformKillServerRpc((int)target.playerClientId);
	}

	[ServerRpc(RequireOwnership = false)]
	private void PerformKillServerRpc(int playerObjectId)
	{
		PerformKillClientRpc(playerObjectId);
	}

	[ClientRpc]
	private void PerformKillClientRpc(int playerObjectId)
	{
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		ReleaseChokeHoldLocal();
		if ((Object)(object)StartOfRound.Instance == (Object)null || playerObjectId < 0 || playerObjectId >= StartOfRound.Instance.allPlayerScripts.Length)
		{
			return;
		}
		PlayerControllerB val = StartOfRound.Instance.allPlayerScripts[playerObjectId];
		if ((Object)(object)val == (Object)null || val.isPlayerDead)
		{
			return;
		}
		if ((Object)(object)killSFX != (Object)null && (Object)(object)base.creatureSFX != (Object)null)
		{
			base.creatureSFX.PlayOneShot(killSFX);
			WalkieTalkie.TransmitOneShotAudio(base.creatureSFX, killSFX, 1f);
		}
		val.KillPlayer(Vector3.zero, true, killCauseOfDeath, killDeathAnimation, default(Vector3), false);
		if (!isEnraged)
		{
			isEnraged = true;
			if ((Object)(object)base.creatureAnimator != (Object)null && !string.IsNullOrEmpty(enragedTriggerParam))
			{
				base.creatureAnimator.SetTrigger(enragedTriggerParam);
			}
		}
		if (((NetworkBehaviour)this).IsOwner)
		{
			SetTargetPlayer(-1);
			base.movingTowardsTargetPlayer = false;
			base.agent.speed = 0f;
			ReturnToDefaultState();
		}
	}

	private void ReleaseChokeHoldLocal()
	{
		if (!isChokeHolding)
		{
			return;
		}
		isChokeHolding = false;
		if ((Object)(object)chokeHoldAnimator != (Object)null && !string.IsNullOrEmpty(chokeHoldAnimatorBoolParam))
		{
			chokeHoldAnimator.SetBool(chokeHoldAnimatorBoolParam, false);
		}
		if ((Object)(object)chokeHoldVictim != (Object)null)
		{
			if ((Object)(object)chokeHoldVictim.thisController != (Object)null)
			{
				((Collider)chokeHoldVictim.thisController).enabled = true;
			}
			chokeHoldVictim.inSpecialInteractAnimation = false;
			chokeHoldVictim.snapToServerPosition = false;
			if ((Object)(object)chokeHoldVictim.inAnimationWithEnemy == (Object)(object)this)
			{
				chokeHoldVictim.inAnimationWithEnemy = null;
			}
		}
		if ((Object)(object)base.inSpecialAnimationWithPlayer == (Object)(object)chokeHoldVictim)
		{
			base.inSpecialAnimationWithPlayer = null;
		}
		base.inSpecialAnimation = false;
		chokeHoldVictim = null;
	}

	public override void SetEnemyStunned(bool setToStunned, float setToStunTime = 1f, PlayerControllerB setStunnedByPlayer = null)
	{
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: 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_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		((EnemyAI)this).SetEnemyStunned(setToStunned, setToStunTime, setStunnedByPlayer);
		if (!setToStunned || base.isEnemyDead)
		{
			return;
		}
		hasBeenStunned = true;
		if ((Object)(object)setStunnedByPlayer != (Object)null)
		{
			blacklistedPlayerIds.Add((int)setStunnedByPlayer.playerClientId);
		}
		ReleaseChokeHoldLocal();
		if (((NetworkBehaviour)this).IsOwner)
		{
			SetTargetPlayer(-1);
			base.movingTowardsTargetPlayer = false;
			base.moveTowardsDestination = false;
			Vector3 val = (((Object)(object)setStunnedByPlayer != (Object)null) ? ((Component)setStunnedByPlayer).transform.position : ((Component)this).transform.position);
			Transform val2 = ((EnemyAI)this).ChooseFarthestNodeFromPosition(val, true, 0, false, 50, -1);
			if ((Object)(object)val2 != (Object)null)
			{
				((EnemyAI)this).SetDestinationToPosition(val2.position, true);
			}
			fleeTimer = fleeDuration;
			((EnemyAI)this).SwitchToBehaviourState(4);
		}
	}

	public override void HitEnemy(int force = 1, PlayerControllerB playerWhoHit = null, bool playHitSFX = false, int hitID = -1)
	{
		((EnemyAI)this).HitEnemy(force, playerWhoHit, playHitSFX, hitID);
		if (!base.isEnemyDead)
		{
			base.enemyHP -= force;
			if (base.enemyHP <= 0 && ((NetworkBehaviour)this).IsOwner)
			{
				((EnemyAI)this).KillEnemyOnOwnerClient(false);
			}
		}
	}

	public override void KillEnemy(bool destroy = false)
	{
		ReleaseChokeHoldLocal();
		((EnemyAI)this).KillEnemy(destroy);
		base.agent.speed = 0f;
		((EnemyAI)this).SwitchToBehaviourStateOnLocalClient(6);
	}

	public override void OnDestroy()
	{
		ReleaseChokeHoldLocal();
		((EnemyAI)this).OnDestroy();
	}
}
public class SludgeFiendAI : EnemyAI
{
	[Header("Scrap Collection")]
	[Tooltip("Body sockets carried scrap is parented onto, one per carried item slot (index 0 = first item picked up). Needs at least Scrap Needed To Attack entries - extra items beyond the array length re-use the last socket.")]
	public Transform[] scrapAttachPoints;

	[Tooltip("Distance (units) to a targeted scrap item at which it's actually picked up.")]
	[Min(0f)]
	public float scrapPickupRange = 1.5f;

	[Tooltip("Max distance (units) away a piece of scrap can be for this instance to notice and go for it.")]
	[Min(0f)]
	public float scrapDetectRange = 25f;

	[Tooltip("How many scrap items need to be carried before it stops collecting and starts hunting for a player to launch them at.")]
	[Min(1f)]
	public int scrapNeededToAttack = 5;

	[Tooltip("Possible sounds played once (for everyone) each time a piece of scrap is picked up.")]
	public List<AudioClip> pickupSFX = new List<AudioClip>();

	[Tooltip("AudioSource the pickup sound plays on. Falls back to creatureSFX if left unset.")]
	public AudioSource pickupAudioSource;

	[Header("Detection / Fleeing")]
	[Tooltip("How wide a cone (degrees) counts as \"seeing\"/\"being seen by\" a player - same meaning as CheckLineOfSightForPlayer's own Width parameter.")]
	[Min(0f)]
	public float playerDetectWidth = 60f;

	[Tooltip("Max distance (units) a player can be spotted at while Searching, triggering an immediate flee.")]
	[Min(0f)]
	public float playerDetectRange = 25f;

	[Tooltip("NavMeshAgent speed while running away, either from being spotted or right after launching scrap.")]
	[Min(0f)]
	public float fleeSpeed = 8f;

	[Tooltip("Seconds spent fleeing before it settles back down and resumes searching for scrap.")]
	[Min(0f)]
	public float fleeDuration = 6f;

	[Header("Wandering")]
	[Tooltip("NavMeshAgent speed while searching for scrap.")]
	[Min(0f)]
	public float wanderSpeed = 4f;

	[Tooltip("Search width passed to the wander AISearchRoutine when no scrap is currently known about.")]
	[Min(0f)]
	public float wanderSearchWidth = 60f;

	[Header("Attack")]
	[Tooltip("Max distance (units) to a player at which it will stop and launch its carried scrap.")]
	[Min(0f)]
	public float launchRange = 15f;

	[Tooltip("Seconds spent standing still and facing the target before the scrap actually launches.")]
	[Min(0f)]
	public float launchWindupTime = 1f;

	[Tooltip("Damage dealt if the launch connects.")]
	[Min(0f)]
	public int launchDamage = 95;

	[Tooltip("Speed (units/sec) the thrown scrap meshes fly toward the target - cosmetic only, the hit/miss result is decided separately at launch time.")]
	[Min(0f)]
	public float launchThrowSpeed = 20f;

	[Tooltip("Extra upward velocity added to each thrown piece so they arc instead of flying flat.")]
	[Min(0f)]
	public float launchArcForce = 4f;

	[Tooltip("Cause of death reported if a launch brings the player's health to 0.")]
	public CauseOfDeath launchCauseOfDeath;

	[Tooltip("Possible sounds played once (for everyone) the moment scrap is launched.")]
	public List<AudioClip> launchSFX = new List<AudioClip>();

	[Tooltip("AudioSource the launch sound plays on. Falls back to creatureSFX if left unset.")]
	public AudioSource launchAudioSource;

	[Header("Facing")]
	[Tooltip("Visual model transform rotated to face the target while winding up an attack. Falls back to this object's own transform if left unset.")]
	public Transform modelRoot;

	[Tooltip("Degrees/sec turned to face the target player during the attack windup.")]
	[Min(0f)]
	public float turnSpeed = 240f;

	[Header("Stun Death (explode)")]
	[Tooltip("VFX prefab instantiated at this instance's position the moment it's stunned/tased and explodes. Purely cosmetic, not networked - every client independently spawns its own local copy the same way R2AI's spawn lightning does.")]
	public GameObject explosionEffectPrefab;

	[Tooltip("Seconds before the spawned explosion VFX instance is destroyed again.")]
	[Min(0f)]
	public float explosionEffectLifetime = 3f;

	[Tooltip("Played once (for everyone) the moment it explodes.")]
	public AudioClip explosionSFX;

	[Tooltip("AudioSource the explosion sound plays on. Falls back to creatureSFX if left unset.")]
	public AudioSource explosionAudioSource;

	[Header("Spawn")]
	[Tooltip("Possible sounds played once (for everyone) the moment this instance spawns.")]
	public List<AudioClip> spawnSFX = new List<AudioClip>();

	[Tooltip("AudioSource the spawn sound plays on. Falls back to creatureSFX if left unset.")]
	public AudioSource spawnAudioSource;

	private static readonly List<GameObject> grabbableObjectsInMap = new List<GameObject>();

	private readonly List<GrabbableObject> carriedScrap = new List<GrabbableObject>();

	private AISearchRoutine searchForItems;

	private GrabbableObject targetItem;

	private float fleeTimer;

	private PlayerControllerB attackTargetPlayer;

	private bool isWindingUpLaunch;

	private float launchTimer;

	private bool sendingCollectRpc;

	private bool hasExploded;

	public override void Start()
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Expected O, but got Unknown
		((EnemyAI)this).Start();
		searchForItems = new AISearchRoutine
		{
			searchWidth = wanderSearchWidth
		};
		RefreshGrabbableObjectsInMap();
		AudioSource val = (((Object)(object)spawnAudioSource != (Object)null) ? spawnAudioSource : base.creatureSFX);
		if (spawnSFX != null && spawnSFX.Count > 0 && (Object)(object)val != (Object)null)
		{
			AudioClip val2 = spawnSFX[Random.Range(0, spawnSFX.Count)];
			val.PlayOneShot(val2);
			WalkieTalkie.TransmitOneShotAudio(val, val2, 1f);
		}
	}

	private static void RefreshGrabbableObjectsInMap()
	{
		grabbableObjectsInMap.Clear();
		GrabbableObject[] array = Object.FindObjectsOfType<GrabbableObject>();
		for (int i = 0; i < array.Length; i++)
		{
			if (array[i].itemProperties.isScrap && array[i].grabbableToEnemies && !array[i].deactivated)
			{
				grabbableObjectsInMap.Add(((Component)array[i]).gameObject);
			}
		}
	}

	public override void Update()
	{
		((EnemyAI)this).Update();
		if (base.isEnemyDead)
		{
			return;
		}
		switch (base.currentBehaviourStateIndex)
		{
		case 0:
			base.agent.speed = wanderSpeed;
			break;
		case 1:
			base.agent.speed = fleeSpeed;
			if (((NetworkBehaviour)this).IsOwner)
			{
				fleeTimer -= Time.deltaTime;
			}
			break;
		case 2:
			base.agent.speed = 0f;
			FaceTargetPlayer(attackTargetPlayer);
			if (((NetworkBehaviour)this).IsOwner && isWindingUpLaunch)
			{
				launchTimer -= Time.deltaTime;
				if (launchTimer <= 0f)
				{
					isWindingUpLaunch = false;
					LaunchScrapAtTarget(attackTargetPlayer);
				}
			}
			break;
		}
	}

	public override void DoAIInterval()
	{
		((EnemyAI)this).DoAIInterval();
		if (!base.isEnemyDead && ((NetworkBehaviour)this).IsOwner)
		{
			switch (base.currentBehaviourStateIndex)
			{
			case 0:
				DoSearchingInterval();
				break;
			case 1:
				DoFleeingInterval();
				break;
			}
		}
	}

	private void DoSearchingInterval()
	{
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_013e: 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_00ba: 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_00fc: Unknown result type (might be due to invalid IL or missing references)
		PlayerControllerB val = ((EnemyAI)this).CheckLineOfSightForPlayer(playerDetectWidth, (int)playerDetectRange, -1);
		if ((Object)(object)val != (Object)null)
		{
			BeginFlee(((Component)val).transform.position);
			return;
		}
		if (carriedScrap.Count >= scrapNeededToAttack)
		{
			PlayerControllerB val2 = ((EnemyAI)this).CheckLineOfSightForPlayer(playerDetectWidth, (int)launchRange, -1);
			if ((Object)(object)val2 != (Object)null)
			{
				BeginAttack(val2);
				return;
			}
		}
		if ((Object)(object)targetItem != (Object)null)
		{
			if (!targetItem.isHeld && !targetItem.deactivated && grabbableObjectsInMap.Contains(((Component)targetItem).gameObject))
			{
				if (Vector3.Distance(((Component)this).transform.position, ((Component)targetItem).transform.position) <= scrapPickupRange)
				{
					CollectScrap(targetItem);
					targetItem = null;
				}
				else
				{
					((EnemyAI)this).SetDestinationToPosition(((Component)targetItem).transform.position, true);
				}
				return;
			}
			targetItem = null;
		}
		GameObject val3 = FindClosestScrap();
		if ((Object)(object)val3 != (Object)null)
		{
			targetItem = val3.GetComponent<GrabbableObject>();
			((EnemyAI)this).StopSearch(searchForItems, false);
			((EnemyAI)this).SetDestinationToPosition(((Component)targetItem).transform.position, true);
		}
		else if (!searchForItems.inProgress)
		{
			((EnemyAI)this).StartSearch(((Component)this).transform.position, searchForItems);
		}
	}

	private GameObject FindClosestScrap()
	{
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		GameObject result = null;
		float num = scrapDetectRange;
		for (int num2 = grabbableObjectsInMap.Count - 1; num2 >= 0; num2--)
		{
			GameObject val = grabbableObjectsInMap[num2];
			if ((Object)(object)val == (Object)null)
			{
				grabbableObjectsInMap.RemoveAt(num2);
			}
			else
			{
				GrabbableObject component = val.GetComponent<GrabbableObject>();
				if (!((Object)(object)component == (Object)null) && !component.isHeld && !component.deactivated && component.grabbableToEnemies)
				{
					float num3 = Vector3.Distance(((Component)this).transform.position, val.transform.position);
					if (num3 < num)
					{
						num = num3;
						result = val;
					}
				}
			}
		}
		return result;
	}

	private void CollectScrap(GrabbableObject item)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		NetworkObject component = ((Component)item).GetComponent<NetworkObject>();
		AttachScrapLocally(item);
		sendingCollectRpc = true;
		CollectScrapServerRpc(NetworkObjectReference.op_Implicit(component));
	}

	[ServerRpc(RequireOwnership = false)]
	private void CollectScrapServerRpc(NetworkObjectReference itemRef)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		CollectScrapClientRpc(itemRef);
	}

	[ClientRpc]
	private void CollectScrapClientRpc(NetworkObjectReference itemRef)
	{
		NetworkObject val = default(NetworkObject);
		if (sendingCollectRpc)
		{
			sendingCollectRpc = false;
		}
		else if (((NetworkObjectReference)(ref itemRef)).TryGet(ref val, (NetworkManager)null))
		{
			GrabbableObject component = ((Component)val).GetComponent<GrabbableObject>();
			if ((Object)(object)component != (Object)null)
			{
				AttachScrapLocally(component);
			}
		}
	}

	private void AttachScrapLocally(GrabbableObject item)
	{
		if (!((Object)(object)item == (Object)null) && !carriedScrap.Contains(item) && scrapAttachPoints != null && scrapAttachPoints.Length != 0)
		{
			int num = Mathf.Min(carriedScrap.Count, scrapAttachPoints.Length - 1);
			Transform parentObject = scrapAttachPoints[num];
			item.parentObject = parentObject;
			item.hasHitGround = false;
			item.GrabItemFromEnemy((EnemyAI)(object)this);
			item.EnablePhysics(false);
			carriedScrap.Add(item);
			grabbableObjectsInMap.Remove(((Component)item).gameObject);
			AudioSource val = (((Object)(object)pickupAudioSource != (Object)null) ? pickupAudioSource : base.creatureSFX);
			if (pickupSFX != null && pickupSFX.Count > 0 && (Object)(object)val != (Object)null)
			{
				AudioClip val2 = pickupSFX[Random.Range(0, pickupSFX.Count)];
				val.PlayOneShot(val2);
				WalkieTalkie.TransmitOneShotAudio(val, val2, 1f);
			}
		}
	}

	private void BeginFlee(Vector3 threatPosition)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		targetItem = null;
		((EnemyAI)this).StopSearch(searchForItems, false);
		Transform val = ((EnemyAI)this).ChooseFarthestNodeFromPosition(threatPosition, true, 0, false, 50, -1);
		if ((Object)(object)val != (Object)null)
		{
			((EnemyAI)this).SetDestinationToPosition(val.position, true);
		}
		fleeTimer = fleeDuration;
		((EnemyAI)this).SwitchToBehaviourState(1);
	}

	private void DoFleeingInterval()
	{
		if (fleeTimer <= 0f)
		{
			((EnemyAI)this).SwitchToBehaviourState(0);
		}
	}

	private void BeginAttack(PlayerControllerB target)
	{
		targetItem = null;
		((EnemyAI)this).StopSearch(searchForItems, false);
		attackTargetPlayer = target;
		isWindingUpLaunch = true;
		launchTimer = launchWindupTime;
		((EnemyAI)this).SwitchToBehaviourState(2);
	}

	private void FaceTargetPlayer(PlayerControllerB target)
	{
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: 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_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: 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)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: 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)
		if (!((Object)(object)target == (Object)null))
		{
			Transform val = (((Object)(object)modelRoot != (Object)null) ? modelRoot : ((Component)this).transform);
			Vector3 val2 = ((Component)target).transform.position - val.position;
			val2.y = 0f;
			if (!(((Vector3)(ref val2)).sqrMagnitude < 0.0001f))
			{
				Quaternion val3 = Quaternion.LookRotation(val2);
				val.rotation = Quaternion.RotateTowards(val.rotation, val3, turnSpeed * Time.deltaTime);
			}
		}
	}

	private void LaunchScrapAtTarget(PlayerControllerB target)
	{
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: 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_0028: 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_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)target != (Object)null && carriedScrap.Count > 0)
		{
			float num = Vector3.Distance(((Component)this).transform.position, ((Component)target).transform.position);
			bool flag = !Physics.Linecast(base.eye.position, ((Component)target.gameplayCamera).transform.position, StartOfRound.Instance.collidersAndRoomMaskAndDefault, (QueryTriggerInteraction)1);
			bool willHit = num <= launchRange && flag;
			LaunchScrapServerRpc((int)target.playerClientId, willHit);
		}
		Vector3 threatPosition = (((Object)(object)target != (Object)null) ? ((Component)target).transform.position : ((Component)this).transform.position);
		attackTargetPlayer = null;
		BeginFlee(threatPosition);
	}

	[ServerRpc(RequireOwnership = false)]
	private void LaunchScrapServerRpc(int targetPlayerId, bool willHit)
	{
		LaunchScrapClientRpc(targetPlayerId, willHit);
	}

	[ClientRpc]
	private void LaunchScrapClientRpc(int targetPlayerId, bool willHit)
	{
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: 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)
		PlayerControllerB val = null;
		if ((Object)(object)StartOfRound.Instance != (Object)null && targetPlayerId >= 0 && targetPlayerId < StartOfRound.Instance.allPlayerScripts.Length)
		{
			val = StartOfRound.Instance.allPlayerScripts[targetPlayerId];
		}
		ThrowCarriedScrapVisual(val);
		AudioSource val2 = (((Object)(object)launchAudioSource != (Object)null) ? launchAudioSource : base.creatureSFX);
		if (launchSFX != null && launchSFX.Count > 0 && (Object)(object)val2 != (Object)null)
		{
			AudioClip val3 = launchSFX[Random.Range(0, launchSFX.Count)];
			val2.PlayOneShot(val3);
			WalkieTalkie.TransmitOneShotAudio(val2, val3, 1f);
		}
		if (willHit && (Object)(object)val != (Object)null && !val.isPlayerDead)
		{
			val.DamagePlayer(launchDamage, true, true, launchCauseOfDeath, 0, false, default(Vector3));
		}
	}

	private void ThrowCarriedScrapVisual(PlayerControllerB target)
	{
		//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_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: 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_0046: 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_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: 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)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: 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_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)
		Vector3 val = (((Object)(object)target != (Object)null) ? (((Component)target).transform.position + Vector3.up) : (((Component)this).transform.position + ((Component)this).transform.forward * launchRange));
		foreach (GrabbableObject item in carriedScrap)
		{
			if (!((Object)(object)item == (Object)null))
			{
				item.parentObject = null;
				item.EnablePhysics(true);
				item.hasHitGround = false;
				if ((Object)(object)item.propBody != (Object)null)
				{
					Vector3 val2 = val - ((Component)item).transform.position;
					Vector3 normalized = ((Vector3)(ref val2)).normalized;
					item.propBody.velocity = normalized * launchThrowSpeed + Vector3.up * launchArcForce;
				}
				item.DiscardItemFromEnemy();
				grabbableObjectsInMap.Add(((Component)item).gameObject);
			}
		}
		carriedScrap.Clear();
	}

	public override void HitEnemy(int force = 1, PlayerControllerB playerWhoHit = null, bool playHitSFX = false, int hitID = -1)
	{
		((EnemyAI)this).HitEnemy(force, playerWhoHit, playHitSFX, hitID);
		if (!base.isEnemyDead)
		{
			base.enemyHP -= force;
			if (base.enemyHP <= 0 && ((NetworkBehaviour)this).IsOwner)
			{
				((EnemyAI)this).KillEnemyOnOwnerClient(false);
			}
		}
	}

	public override void SetEnemyStunned(bool setToStunned, float setToStunTime = 1f, PlayerControllerB setStunnedByPlayer = null)
	{
		((EnemyAI)this).SetEnemyStunned(setToStunned, setToStunTime, setStunnedByPlayer);
		if (setToStunned && !base.isEnemyDead && !hasExploded)
		{
			hasExploded = true;
			PlayExplosionEffect();
			if (((NetworkBehaviour)this).IsOwner)
			{
				((EnemyAI)this).KillEnemyOnOwnerClient(true);
			}
		}
	}

	private void PlayExplosionEffect()
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)explosionEffectPrefab != (Object)null)
		{
			Object.Destroy((Object)(object)Object.Instantiate<GameObject>(explosionEffectPrefab, ((Component)this).transform.position, Quaternion.identity), explosionEffectLifetime);
		}
		AudioSource val = (((Object)(object)explosionAudioSource != (Object)null) ? explosionAudioSource : base.creatureSFX);
		if ((Object)(object)explosionSFX != (Object)null && (Object)(object)val != (Object)null)
		{
			val.PlayOneShot(explosionSFX);
			WalkieTalkie.TransmitOneShotAudio(val, explosionSFX, 1f);
		}
	}

	public override void KillEnemy(bool destroy = false)
	{
		((EnemyAI)this).KillEnemy(false);
		base.agent.speed = 0f;
		DropAllCarriedScrap();
	}

	private void DropAllCarriedScrap()
	{
		foreach (GrabbableObject item in carriedScrap)
		{
			if (!((Object)(object)item == (Object)null))
			{
				item.parentObject = null;
				item.EnablePhysics(true);
				item.hasHitGround = false;
				item.DiscardItemFromEnemy();
				grabbableObjectsInMap.Add(((Component)item).gameObject);
			}
		}
		carriedScrap.Clear();
	}
}