Decompiled source of ContentWarningOWO v1.0.1

Bepinex/plugins/Connectnotconnect-CW_OWOHaptics/Connectnotconnect.CW_OWOHaptics.dll

Decompiled 8 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using OWOGame;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Connectnotconnect.CW_OWOHaptics")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("CW_OWOHaptics")]
[assembly: AssemblyTitle("Connectnotconnect.CW_OWOHaptics")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 CW_OWOHaptics
{
	[ContentWarningPlugin("Connectnotconnect.CW_OWOHaptics", "1.0.1", true)]
	[BepInPlugin("Connectnotconnect.CW_OWOHaptics", "CW_OWOHaptics", "1.0.1")]
	public class Loader : BaseUnityPlugin
	{
		public SuitInitialiser CW_OWOSuit;

		public static Loader Instance { get; private set; }

		internal static ManualLogSource Logger { get; private set; }

		internal static Harmony? Harmony { get; set; }

		private void Awake()
		{
			Logger = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			Patch();
			CW_OWOSuit = new SuitInitialiser();
			Logger.LogInfo((object)"Connectnotconnect.CW_OWOHaptics v1.0.1 has loaded!");
		}

		internal static void Patch()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			if (Harmony == null)
			{
				Harmony = new Harmony("Connectnotconnect.CW_OWOHaptics");
			}
			Logger.LogDebug((object)"Patching...");
			Harmony.PatchAll();
			Logger.LogDebug((object)"Finished patching!");
		}

		internal static void Unpatch()
		{
			Logger.LogDebug((object)"Unpatching...");
			Harmony? harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Logger.LogDebug((object)"Finished unpatching!");
		}
	}
	public class MyOWOVest
	{
		public bool suitDisabled = true;

		public bool systemInitialised;

		public Dictionary<Sensation, int> SensationPriorities = new Dictionary<Sensation, int>();

		public Sensation CurrentlyPlayingSensation;

		public long TimeLastSensationStarted;

		public Dictionary<string, Sensation> FeedbackMap = new Dictionary<string, Sensation>();

		public Dictionary<Muscle, Muscle> MuscleToOppositeFacingMuscleMap = new Dictionary<Muscle, Muscle>();

		public Random random = new Random();

		public MyOWOVest(Dictionary<string, int> SensationPriorities)
		{
			//IL_003f: 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_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_0069: 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_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: 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_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: 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_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)
			MuscleToOppositeFacingMuscleMap.Add(Muscle.Pectoral_L, Muscle.Dorsal_L);
			MuscleToOppositeFacingMuscleMap.Add(Muscle.Pectoral_R, Muscle.Dorsal_R);
			MuscleToOppositeFacingMuscleMap.Add(Muscle.Abdominal_L, Muscle.Lumbar_L);
			MuscleToOppositeFacingMuscleMap.Add(Muscle.Abdominal_R, Muscle.Lumbar_R);
			MuscleToOppositeFacingMuscleMap.Add(Muscle.Dorsal_L, Muscle.Pectoral_L);
			MuscleToOppositeFacingMuscleMap.Add(Muscle.Dorsal_R, Muscle.Pectoral_R);
			MuscleToOppositeFacingMuscleMap.Add(Muscle.Lumbar_L, Muscle.Abdominal_L);
			MuscleToOppositeFacingMuscleMap.Add(Muscle.Lumbar_R, Muscle.Abdominal_R);
			RegisterAllSensationFiles();
			InitialiseOWO(SensationPriorities);
		}

		private async void InitialiseOWO(Dictionary<string, int> SensationPriorities)
		{
			LOG("Initialising OWO suit");
			GameAuth val = GameAuth.Create(AllBakedSensations()).WithId("79123159");
			OWO.Configure(val);
			string[] iPsFromFile = GetIPsFromFile("OWO_Manual_IP.txt");
			if (iPsFromFile.Length != 0)
			{
				await OWO.Connect(iPsFromFile);
			}
			else
			{
				await OWO.AutoConnect();
			}
			LOG("Awaiting connection");
			if ((int)OWO.ConnectionState == 0)
			{
				suitDisabled = false;
				LOG("OWO Suit is connected");
			}
			if (suitDisabled)
			{
				LOG("Suit is not connected.");
			}
			LOG("Registering sensations now");
			foreach (KeyValuePair<string, int> SensationPriority in SensationPriorities)
			{
				if (FeedbackMap.ContainsKey(SensationPriority.Key))
				{
					RegisterSensationToPriority(FeedbackMap[SensationPriority.Key], SensationPriority.Value);
					LOG("Registered " + SensationPriority.Key + " priority");
				}
			}
			LOG("Registered sensation priorities");
		}

		public string[] GetIPsFromFile(string filename)
		{
			List<string> list = new List<string>();
			string text = Directory.GetCurrentDirectory() + "\\Mods\\" + filename;
			if (File.Exists(text))
			{
				LOG("Manual IP file found: " + text);
				IEnumerable<string> enumerable = File.ReadLines(text);
				foreach (string item in enumerable)
				{
					if (IPAddress.TryParse(item, out IPAddress _))
					{
						list.Add(item);
					}
					else
					{
						LOG("IP not valid? -----" + item + "-----");
					}
				}
			}
			return list.ToArray();
		}

		~MyOWOVest()
		{
			LOG("Destructor called");
			DisconnectOWO();
		}

		private BakedSensation[] AllBakedSensations()
		{
			List<BakedSensation> list = new List<BakedSensation>();
			foreach (Sensation value in FeedbackMap.Values)
			{
				BakedSensation val = (BakedSensation)(object)((value is BakedSensation) ? value : null);
				if (val != null)
				{
					LOG("Registered baked sensation " + val.name);
					list.Add(val);
				}
				else
				{
					LOG("Sensation not baked? " + Sensation.op_Implicit(value));
				}
			}
			return list.ToArray();
		}

		public void DisconnectOWO()
		{
			LOG("Disconnecting OWO suit");
			OWO.Disconnect();
		}

		public void LOG(string msg)
		{
			Loader.Logger.LogInfo((object)("Connectnotconnect.CW_OWOHaptics v1.0.1 " + msg));
		}

		private void RegisterAllSensationFiles()
		{
			string currentDirectory = Environment.CurrentDirectory;
			DirectoryInfo directoryInfo = new DirectoryInfo(currentDirectory);
			FileInfo[] files = directoryInfo.GetFiles("*.owo", SearchOption.AllDirectories);
			for (int i = 0; i < files.Length; i++)
			{
				string name = files[i].Name;
				string fullName = files[i].FullName;
				string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fullName);
				if (!(name == ".") && !(name == ".."))
				{
					string text = File.ReadAllText(fullName);
					try
					{
						Sensation value = Sensation.Parse(text);
						FeedbackMap.Add(fileNameWithoutExtension, value);
					}
					catch (Exception ex)
					{
						LOG(ex.ToString());
					}
					systemInitialised = true;
				}
			}
		}

		public void SetCurrentlyPlayingSensation(Sensation sensation)
		{
			CurrentlyPlayingSensation = sensation;
			TimeLastSensationStarted = DateTimeOffset.Now.ToUnixTimeMilliseconds();
		}

		public void StopCurrentlyPlayingSensation()
		{
			OWO.Stop();
			CurrentlyPlayingSensation = null;
		}

		public void RegisterSensationToPriority(Sensation sensation, int priority)
		{
			SensationPriorities.Add(sensation, priority);
		}

		public Sensation GetSensationFromName(string name)
		{
			return FeedbackMap[name];
		}

		public Sensation GetCurrentlyPlayingSensation(string name)
		{
			return CurrentlyPlayingSensation;
		}

		public bool IsSensationCurrentlyPlaying(string name)
		{
			Sensation sensationFromName = GetSensationFromName(name);
			if (sensationFromName == null)
			{
				LOG(name + " was not a found sensation in FeedbackMap!");
				return false;
			}
			return IsSensationCurrentlyPlaying(sensationFromName);
		}

		public string GetSensationName(Sensation sensation)
		{
			foreach (string key in FeedbackMap.Keys)
			{
				if (FeedbackMap[key] == sensation)
				{
					return key;
				}
			}
			return "null";
		}

		public bool IsSensationCurrentlyPlaying(Sensation sensation)
		{
			if (CurrentlyPlayingSensation == sensation)
			{
				return IsCurrentlyPlayingSensationPlaying();
			}
			return false;
		}

		public bool IsCurrentlyPlayingSensationPlaying()
		{
			long num = DateTimeOffset.Now.ToUnixTimeMilliseconds();
			if ((float)((num - TimeLastSensationStarted) / 1000) < CurrentlyPlayingSensation.Duration)
			{
				return true;
			}
			return false;
		}

		public bool CanPlaySensation(Sensation sensation)
		{
			if (!SensationPriorities.ContainsKey(sensation))
			{
				LOG("Sensation " + GetSensationName(sensation) + " does not have a priority!");
				return false;
			}
			if (CurrentlyPlayingSensation != null)
			{
				if (IsCurrentlyPlayingSensationPlaying())
				{
					int num = SensationPriorities[sensation];
					int num2 = ((CurrentlyPlayingSensation != null) ? SensationPriorities[CurrentlyPlayingSensation] : (-1));
					if (CurrentlyPlayingSensation == sensation)
					{
						return false;
					}
					if (num >= num2)
					{
						return true;
					}
					return false;
				}
				return true;
			}
			return true;
		}

		public KeyValuePair<float, float> GetAngleAndShift(Vector3 playerPosition, Vector3 hit, Quaternion playerRotation)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: 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_001e: 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)
			//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_002a: 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_0040: 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_004a: 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_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: 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_0094: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(0f, 0f, 1f);
			Vector3 val2 = hit - playerPosition;
			Quaternion val3 = playerRotation;
			Vector3 eulerAngles = ((Quaternion)(ref val3)).eulerAngles;
			Vector3 val4 = default(Vector3);
			((Vector3)(ref val4))..ctor(val2.x, 0f, val2.z);
			float num = Vector3.Angle(val4, val);
			Vector3 val5 = Vector3.Cross(val4, val);
			if (val5.y > 0f)
			{
				num *= -1f;
			}
			float num2 = num - eulerAngles.y;
			num2 *= -1f;
			if (num2 < 0f)
			{
				num2 = 360f + num2;
			}
			float y = val2.y;
			float num3 = 0.5f;
			float num4 = -0.5f;
			y = ((y > num3) ? 0.5f : ((!(y < num4)) ? ((y - num4) / (num3 - num4) - 0.5f) : (-0.5f)));
			return new KeyValuePair<float, float>(num2, y);
		}

		public Muscle GetMuscleFromAngleAndShift(float xzAngle, float yShift)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: 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)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if (xzAngle < 90f)
			{
				if (yShift >= 0f)
				{
					return Muscle.Pectoral_R;
				}
				return Muscle.Abdominal_R;
			}
			if (xzAngle > 90f && xzAngle < 180f)
			{
				if (yShift >= 0f)
				{
					return Muscle.Dorsal_R;
				}
				return Muscle.Lumbar_R;
			}
			if (xzAngle > 180f && xzAngle < 270f)
			{
				if (yShift >= 0f)
				{
					return Muscle.Dorsal_L;
				}
				return Muscle.Lumbar_L;
			}
			if (xzAngle > 270f)
			{
				if (yShift >= 0f)
				{
					return Muscle.Pectoral_L;
				}
				return Muscle.Abdominal_L;
			}
			if (random.Next(1, 2) != 1)
			{
				return Muscle.Pectoral_L;
			}
			return Muscle.Pectoral_R;
		}

		public Sensation GetShotWithExitSensation(Muscle entryMuscle)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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_005d: 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)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			LOG("Entry Muscle: " + entryMuscle.id);
			Muscle val = MuscleToOppositeFacingMuscleMap[entryMuscle];
			LOG("Exit Muscle: " + val.id);
			Sensation val2 = SensationExtensions.WithMuscles(FeedbackMap["ShotEntry"], (Muscle[])(object)new Muscle[1] { entryMuscle });
			Sensation val3 = SensationExtensions.WithMuscles(FeedbackMap["ShotExit"], (Muscle[])(object)new Muscle[1] { val });
			return SensationExtensions.Append(val2, val3);
		}

		public void PlayBackFeedback(string feedback)
		{
			if (FeedbackMap.ContainsKey(feedback))
			{
				if (CanPlaySensation(FeedbackMap[feedback]))
				{
					StopCurrentlyPlayingSensation();
					SetCurrentlyPlayingSensation(FeedbackMap[feedback]);
					OWO.Send(SensationExtensions.WithPriority(FeedbackMap[feedback], SensationPriorities[FeedbackMap[feedback]]), Array.Empty<Muscle>());
					LOG("Sending feedback " + feedback);
				}
			}
			else
			{
				LOG("Feedback not registered: " + feedback);
			}
		}

		public void PlayBackPresetOWOFeedback(string SensationName, Muscle EntryMuscle)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			Sensation val = FeedbackMap[SensationName];
			if (CanPlaySensation(val))
			{
				StopCurrentlyPlayingSensation();
				SetCurrentlyPlayingSensation(val);
				LOG(SensationName);
				Sensation val2 = val;
				if (SensationName == "Shot")
				{
					val2 = GetShotWithExitSensation(EntryMuscle);
				}
				OWO.Send(SensationExtensions.WithPriority(val2, SensationPriorities[val]), Array.Empty<Muscle>());
				LOG("Sending feedback " + SensationName);
			}
		}
	}
	public class SuitInitialiser : MonoBehaviour
	{
		[HarmonyPatch(typeof(Attack_Stab))]
		public class AttackStabPatch
		{
			[HarmonyPatch("RPCA_Hit", new Type[]
			{
				typeof(int),
				typeof(int),
				typeof(Vector3)
			})]
			[HarmonyPrefix]
			public static void Prefix(int viewID, int bodyPartID, Vector3 force)
			{
				Player val = PlayerHandler.instance.TryGetPlayerFromViewID(viewID);
				if (Object.op_Implicit((Object)(object)val) && val.IsLocal)
				{
					OWOVest.PlayBackFeedback("Stab");
				}
			}
		}

		[HarmonyPatch(typeof(Projectile))]
		public class ProjectilePatch
		{
			[HarmonyPatch("Hit", new Type[] { typeof(RaycastHit) })]
			[HarmonyPostfix]
			public static void Postfix(Projectile __instance, RaycastHit hit)
			{
				//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_0042: Unknown result type (might be due to invalid IL or missing references)
				//IL_0047: 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)
				//IL_0063: Unknown result type (might be due to invalid IL or missing references)
				//IL_0069: 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_006b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0090: Unknown result type (might be due to invalid IL or missing references)
				if (TestMode || (OWOVest != null && !OWOVest.suitDisabled))
				{
					Player componentInParent = ((Component)((RaycastHit)(ref hit)).transform).GetComponentInParent<Player>();
					if (Object.op_Implicit((Object)(object)componentInParent) && componentInParent.IsLocal)
					{
						Vector3 point = ((RaycastHit)(ref hit)).point;
						Vector3 playerPosition = componentInParent.HeadPosition();
						Quaternion rotation = componentInParent.refs.ragdoll.GetBodypart((BodypartType)0).rig.rotation;
						KeyValuePair<float, float> angleAndShift = OWOVest.GetAngleAndShift(playerPosition, point, rotation);
						OWOVest.PlayBackPresetOWOFeedback("Shot", OWOVest.GetMuscleFromAngleAndShift(angleAndShift.Key, angleAndShift.Value));
					}
				}
			}
		}

		[HarmonyPatch(typeof(Bot_BigSlap))]
		public class Bot_BigSlapPatch
		{
			[HarmonyPatch("RPCA_JumpAttackBigSlap", new Type[] { typeof(int) })]
			[HarmonyPrefix]
			public static void Prefix(Bot_BigSlap __instance, int targetID)
			{
				if (TestMode || (OWOVest != null && !OWOVest.suitDisabled))
				{
					Player val = PlayerHandler.instance.TryGetPlayerFromViewID(targetID);
					if (Object.op_Implicit((Object)(object)val) && (Object)(object)val == (Object)(object)Player.localPlayer)
					{
						OWOVest.PlayBackFeedback("InstantFear");
						OWOVest.LOG("Playing INSTANT FEAR sensation");
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player))]
		public class PlayerPatch
		{
			[HarmonyPatch("Update")]
			[HarmonyPostfix]
			public static void UpdatePostfix(Player __instance)
			{
				if (!TestMode && (OWOVest == null || OWOVest.suitDisabled))
				{
					return;
				}
				if (__instance.data.OxygenDisplayPercentage() <= 0.5f)
				{
					OWOVest.PlayBackFeedback("Drowning");
					OWOVest.LOG("Playing DROWN sensation");
				}
				else if (__instance.data.staminaDepleated)
				{
					if (!OWOVest.IsSensationCurrentlyPlaying("Exhaustion"))
					{
						OWOVest.PlayBackFeedback("Exhaustion");
					}
				}
				else if (Object.op_Implicit((Object)(object)__instance.data.currentItem))
				{
					OWOVest.PlayBackFeedback("HoldItem");
				}
				else if (OWOVest.IsSensationCurrentlyPlaying("HoldItem"))
				{
					OWOVest.StopCurrentlyPlayingSensation();
				}
			}

			[HarmonyPatch("Die")]
			[HarmonyPrefix]
			public static void DiePrefix(Player __instance)
			{
				if (__instance.refs.view.IsMine && !__instance.data.dead && !__instance.ai)
				{
					OWOVest.PlayBackFeedback("Die");
				}
			}

			[HarmonyPatch("TakeDamage")]
			[HarmonyPrefix]
			public static void Prefix(Player __instance)
			{
				if (!__instance.ai && !__instance.data.dead && __instance.refs.view.IsMine && __instance.refs.view.IsMine)
				{
					OWOVest.PlayBackFeedback("GeneralDamage");
				}
			}

			[HarmonyPatch("RPCA_CallTakeDamageAndTase", new Type[]
			{
				typeof(float),
				typeof(float)
			})]
			[HarmonyPrefix]
			public static void Prefix(Player __instance, float damage, float tase)
			{
				if ((TestMode || (OWOVest != null && !OWOVest.suitDisabled)) && tase > 0.01f && (Object)(object)__instance == (Object)(object)Player.localPlayer)
				{
					OWOVest.PlayBackFeedback("Electrocution");
					OWOVest.LOG("Playing ELECTOCUTION sensation");
				}
			}
		}

		[HarmonyPatch(typeof(UI_Feedback))]
		public class UI_FeedbackPatch
		{
			[HarmonyPatch("HealFeedback")]
			[HarmonyPostfix]
			public static void HealFeedbackPatchHandler(UI_Feedback __instance)
			{
				if (TestMode || (OWOVest != null && !OWOVest.suitDisabled))
				{
					OWOVest.PlayBackFeedback("Healing");
					OWOVest.LOG("Playing HEAL sensation");
				}
			}
		}

		[HarmonyPatch(typeof(DivingBellDoorButton))]
		public class DivingBellDoorButtonPatch
		{
			[HarmonyPatch("Interact")]
			[HarmonyPrefix]
			public static void Prefix(DivingBellDoorButton __instance)
			{
				if (TestMode || (OWOVest != null && !OWOVest.suitDisabled))
				{
					if (__instance.divingBell.onSurface)
					{
						OWOVest.PlayBackFeedback("PullLever");
						OWOVest.LOG("Playing PULL LEVER sensation");
					}
					else
					{
						OWOVest.PlayBackFeedback("PullLever");
						OWOVest.LOG("Playing PULL LEVER NOT ON SURFACE sensation");
					}
				}
			}
		}

		[HarmonyPatch(typeof(UseDivingBellButton), "Interact", new Type[] { typeof(Player) })]
		public class UseDivingBellButtonPatch
		{
			[HarmonyPrefix]
			public static void Prefix(UseDivingBellButton __instance, Player player)
			{
				if (!TestMode && (OWOVest == null || OWOVest.suitDisabled))
				{
					return;
				}
				if (__instance.onSurface)
				{
					if (SurfaceNetworkHandler.ReturningFromLostWorld)
					{
						return;
					}
					OWOVest.LOG("Playing SUBMERGE sensation");
					OWOVest.PlayBackFeedback("Submerge");
				}
				else
				{
					OWOVest.LOG("Playing RESURFACE sensation");
				}
				OWOVest.PlayBackFeedback("Resurface");
			}
		}

		[HarmonyPatch(typeof(Animator), "SetBool", new Type[]
		{
			typeof(string),
			typeof(bool)
		})]
		public class OWO_Animator_SetBool
		{
			public static bool IsGrounded = true;

			[HarmonyPrefix]
			public static void Prefix(Animator __instance, string name, bool value)
			{
				if ((TestMode || (OWOVest != null && !OWOVest.suitDisabled)) && name.Equals("Is Grounded") && !IsGrounded && value && (TestMode || (OWOVest != null && !OWOVest.suitDisabled)))
				{
					OWOVest.PlayBackFeedback("FallSoft");
				}
			}

			[HarmonyPostfix]
			public static void Postfix(Animator __instance, string name, bool value)
			{
				if ((TestMode || (OWOVest != null && !OWOVest.suitDisabled)) && name.Equals("Is Grounded"))
				{
					IsGrounded = value;
				}
			}
		}

		[HarmonyPatch(typeof(PlayerController), "RPCA_Jump")]
		public class OWO_Jump
		{
			[HarmonyPostfix]
			public static void Postfix(PlayerController __instance)
			{
				if ((TestMode || (OWOVest != null && !OWOVest.suitDisabled)) && __instance.player.IsLocal)
				{
					OWOVest.PlayBackFeedback("Jump");
				}
			}
		}

		public static bool TestMode;

		public static MyOWOVest OWOVest;

		public Dictionary<string, int> SensationPriorities = new Dictionary<string, int>();

		public SuitInitialiser()
		{
			SensationPriorities.Add("Jump", 2);
			SensationPriorities.Add("HoldItem", 3);
			SensationPriorities.Add("FallSoft", 4);
			SensationPriorities.Add("Exhaustion", 5);
			SensationPriorities.Add("PullLever", 6);
			SensationPriorities.Add("Healing", 6);
			SensationPriorities.Add("Submerge", 6);
			SensationPriorities.Add("Resurface", 6);
			SensationPriorities.Add("Stab", 6);
			SensationPriorities.Add("GeneralDamage", 6);
			SensationPriorities.Add("Die", 7);
			SensationPriorities.Add("InstantFear", 7);
			SensationPriorities.Add("Drowning", 7);
			SensationPriorities.Add("Electrocution", 8);
			SensationPriorities.Add("Shot", 9);
			OWOVest = new MyOWOVest(SensationPriorities);
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "Connectnotconnect.CW_OWOHaptics";

		public const string PLUGIN_NAME = "CW_OWOHaptics";

		public const string PLUGIN_VERSION = "1.0.1";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}

Bepinex/plugins/Connectnotconnect-CW_OWOHaptics/OWO.dll

Decompiled 8 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading.Tasks;
using OWOGame.Controller;
using OWOGame.Infraestructure;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: InternalsVisibleTo("Tests")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("OWO")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("OWO SDK for CSharp projects")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+c96c7a9964ae7f34d8fd25f10807bb974d16e4c0")]
[assembly: AssemblyProduct("OWO")]
[assembly: AssemblyTitle("OWO")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace OWOGame
{
	[ContentWarningPlugin("Connectnotconnect.CW_OWOHaptics", "1.0.0", true)]
	public class OWO
	{
		private static OWO instance;

		private readonly Client client;

		private readonly SendSensation send;

		private readonly StopSensation stop;

		private readonly Disconnect disconnect;

		private readonly Connect connect;

		private readonly RealTimeClock clock;

		private static OWO Instance
		{
			get
			{
				OWO result;
				if ((result = instance) == null)
				{
					result = (instance = new OWO(ClientFactory.Create(new UDPNetwork()), new RealTimeClock()));
				}
				return result;
			}
		}

		public static ConnectionState ConnectionState => Instance.client.State;

		public static string[] DiscoveredApps => Instance.client.DiscoveredServers.ToArray();

		internal OWO(Client client, RealTimeClock clock)
		{
			this.client = client;
			send = new SendSensation(client);
			stop = new StopSensation(client);
			connect = new Connect(client);
			disconnect = new Disconnect(client);
			this.clock = clock;
		}

		public static void Configure(GameAuth game)
		{
			Instance.ConfigureB(game);
		}

		internal void ConfigureB(GameAuth game)
		{
			connect.Configure(game);
			send.Configure(game);
			stop.Configure(game);
		}

		public static Task AutoConnect()
		{
			return Instance.AutoConnectB();
		}

		internal Task AutoConnectB()
		{
			return Task.Run((Func<Task?>)connect.AutoConnect);
		}

		public static void StartScan()
		{
			Instance.StartScanB();
		}

		internal void StartScanB()
		{
			Task.Run((Func<Task?>)connect.ScanServer);
		}

		public static Task Connect(params string[] ips)
		{
			return Instance.ConnectB(ips);
		}

		internal Task ConnectB(params string[] ips)
		{
			return Task.Run(() => connect.ManualConnect(ips));
		}

		public static void Stop()
		{
			Instance.StopB();
		}

		internal void StopB()
		{
			stop.Execute();
			send.ResetPriority();
		}

		public static void Send(Sensation sensation, params Muscle[] muscles)
		{
			Instance.SendB(sensation, muscles);
		}

		internal void SendB(Sensation sensation, params Muscle[] muscles)
		{
			send.Execute(sensation.WithMuscles(muscles), clock.TotalMilliseconds);
		}

		public static void Disconnect()
		{
			Instance.DisconnectB();
		}

		internal void DisconnectB()
		{
			disconnect.Execute();
		}
	}
	public class BakedSensation : Sensation
	{
		public readonly int id;

		public readonly string name;

		public readonly Family Family;

		public readonly Sensation reference;

		public readonly Icon icon;

		public override float Duration => reference.Duration;

		internal BakedSensation(int id, string name, Sensation reference, Icon icon, Family family)
		{
			this.id = id;
			this.name = name;
			this.reference = reference;
			this.icon = icon;
			Family = family;
		}

		public new static BakedSensation Parse(string message)
		{
			return ((Sensation)message) as BakedSensation;
		}

		public BakedSensation WithIcon(Icon icon)
		{
			return new BakedSensation(id, name, reference, icon, Family).WithPriority(base.Priority) as BakedSensation;
		}

		public BakedSensation BelongsTo(Family family)
		{
			return new BakedSensation(id, name, reference, icon, family).WithPriority(base.Priority) as BakedSensation;
		}

		public string Stringify()
		{
			return BakedSensationsBuilder.Stringify(this);
		}
	}
	internal static class BakedSensationsBuilder
	{
		private const string SEPARATOR = "~";

		public static string From(BakedSensation sensation)
		{
			int id = sensation.id;
			return id.ToString();
		}

		public static string Stringify(BakedSensation sensation)
		{
			string[] array = new string[9];
			int id = sensation.id;
			array[0] = id.ToString();
			array[1] = "~";
			array[2] = sensation.name;
			array[3] = "~";
			array[4] = sensation.reference;
			array[5] = "~";
			array[6] = sensation.icon;
			array[7] = "~";
			array[8] = sensation.Family;
			return string.Concat(array);
		}
	}
	internal static class GamesBuilder
	{
		private const string SEPARATOR = "#";

		public static string Build(GameAuth theGame)
		{
			if (theGame.sensations.Length == 0)
			{
				return string.Empty;
			}
			string text = theGame.sensations[0].Stringify();
			for (int i = 1; i < theGame.sensations.Length; i++)
			{
				text = text + "#\n" + theGame.sensations[i].Stringify();
			}
			return text;
		}
	}
	internal static class MusclesBuilder
	{
		public static string From(params Muscle[] muscles)
		{
			string text = From(muscles[0]);
			for (int i = 1; i < muscles.Length; i++)
			{
				text = text + "," + From(muscles[i]);
			}
			return text;
		}

		private static string From(Muscle muscle)
		{
			return $"{muscle.id}%{muscle.intensity}";
		}
	}
	internal static class SensationsBuilder
	{
		public static string From(Sensation sensation)
		{
			if (sensation is MicroSensation microsensation)
			{
				return From(microsensation);
			}
			if (sensation is SensationWithMuscles sensation2)
			{
				return From(sensation2);
			}
			if (sensation is SensationsSequence sequence)
			{
				return From(sequence);
			}
			return BakedSensationsBuilder.From(sensation as BakedSensation);
		}

		private static string From(SensationsSequence sequence)
		{
			string text = From(sequence.sensations[0]);
			for (int i = 1; i < sequence.sensations.Count; i++)
			{
				text = text + "&" + From(sequence.sensations[i]);
			}
			return text;
		}

		private static string From(SensationWithMuscles sensation)
		{
			return From(sensation.reference) + "|" + sensation.muscles.Stringify();
		}

		private static string From(MicroSensation microsensation)
		{
			return $"{microsensation.frequency},{(int)Math.Round(microsensation.duration * 10f)},{microsensation.intensity}," + $"{(int)Math.Round(microsensation.rampUp * 1000f)},{(int)Math.Round(microsensation.rampDown * 1000f)},{(int)Math.Round(microsensation.exitDelay * 10f)},{microsensation.name}";
		}
	}
	public static class MusclesExtensions
	{
		public static Muscle[] WithIntensity(this Muscle[] muscles, int intensity)
		{
			return muscles.Select((Muscle m) => m.WithIntensity(intensity)).ToArray();
		}

		public static Muscle Mirror(this Muscle of)
		{
			return new Muscle(MirrorOf(of.id), of.intensity);
		}

		public static Muscle[] Mirror(this Muscle[] of)
		{
			return of.Select(Mirror).ToArray();
		}

		private static int MirrorOf(int aPosition)
		{
			return (aPosition % 2 == 0) ? (aPosition + 1) : (aPosition - 1);
		}

		public static string Stringify(this Muscle muscle)
		{
			return MusclesBuilder.From(muscle);
		}

		public static string Stringify(this Muscle[] muscles)
		{
			return MusclesBuilder.From(muscles);
		}
	}
	public static class SensationExtensions
	{
		public static Sensation WithPriority(this Sensation source, int priority)
		{
			source.Priority = priority;
			return source;
		}

		public static Sensation Append(this Sensation source, Sensation addend)
		{
			return new SensationsSequence(source, addend).WithPriority(source.Priority);
		}

		public static BakedSensation Bake(this Sensation source, int id, string name)
		{
			if (source is BakedSensation result)
			{
				return result;
			}
			return new BakedSensation(id, name, source, Icon.Empty, Family.None).WithPriority(source.Priority) as BakedSensation;
		}

		public static Sensation WithMuscles(this Sensation source, params Muscle[] muscles)
		{
			if (muscles.Length == 0 || source is SensationWithMuscles)
			{
				return source;
			}
			if (source is SensationsSequence sensationsSequence)
			{
				return new SensationsSequence(sensationsSequence.sensations.Select((Sensation s) => s.WithMuscles(muscles)).ToArray()).WithPriority(source.Priority);
			}
			return new SensationWithMuscles(source, muscles).WithPriority(source.Priority);
		}
	}
	public struct Family
	{
		private readonly string name;

		public static Family None { get; } = "";


		private Family(string name)
		{
			this.name = name;
		}

		public static implicit operator string(Family family)
		{
			return family.name;
		}

		public static implicit operator Family(string name)
		{
			return new Family(name);
		}

		public override string ToString()
		{
			return name;
		}
	}
	public class GameAuth
	{
		public readonly string id;

		public readonly BakedSensation[] sensations = new BakedSensation[0];

		public static GameAuth Empty => Create().WithId("0");

		internal GameAuth()
		{
		}

		internal GameAuth(string id, params BakedSensation[] sensations)
		{
			this.id = id;
			this.sensations = sensations;
		}

		public GameAuth WithId(string id)
		{
			return new GameAuth(id, sensations);
		}

		public static GameAuth Parse(string auth)
		{
			return auth;
		}

		public static GameAuth Create(params BakedSensation[] sensations)
		{
			return new GameAuth("0", sensations);
		}

		public override string ToString()
		{
			return this;
		}

		public static implicit operator GameAuth(string auth)
		{
			return GamesParser.From(auth);
		}

		public static implicit operator string(GameAuth auth)
		{
			return GamesBuilder.Build(auth);
		}
	}
	public struct Icon
	{
		[StructLayout(LayoutKind.Sequential, Size = 1)]
		public struct Impact
		{
			private const string PREFIX = "Impact-";

			public static Icon Ball => new Icon("Impact-" + 0);

			public static Icon Dart => new Icon("Impact-" + 1);

			public static Icon Punch => new Icon("Impact-" + 2);

			public static Icon Bullet => new Icon("Impact-" + 3);
		}

		[StructLayout(LayoutKind.Sequential, Size = 1)]
		public struct Weapon
		{
			private const string PREFIX = "Weapon-";

			public static Icon Axe => new Icon("Weapon-" + 0);

			public static Icon Dagger => new Icon("Weapon-" + 1);

			public static Icon Gun => new Icon("Weapon-" + 2);

			public static Icon SubMachineGun => new Icon("Weapon-" + 3);
		}

		private readonly string name;

		public static Icon Empty => new Icon("0");

		public static Icon Death => new Icon("Death-0");

		public static Icon Spiders => new Icon("Spider-0");

		public static Icon Weight => new Icon("Weight-0");

		public static Icon Environment => new Icon("Environment-0");

		public static Icon Alert => new Icon("Alert-0");

		public static Icon Victory => new Icon("Victory-0");

		internal Icon(string name)
		{
			this.name = name;
		}

		public static implicit operator Icon(string message)
		{
			return new Icon(message);
		}

		public static implicit operator string(Icon icon)
		{
			return icon.name;
		}

		public override string ToString()
		{
			return this;
		}
	}
	internal static class Math
	{
		public static T Clamp<T>(T val, T min, T max) where T : IComparable<T>
		{
			if (val.CompareTo(min) < 0)
			{
				return min;
			}
			if (val.CompareTo(max) > 0)
			{
				return max;
			}
			return val;
		}

		public static float Round(float value, int decimals = 1)
		{
			return (float)System.Math.Round(value, decimals);
		}
	}
	public class MicroSensation : Sensation
	{
		public readonly int frequency;

		public readonly float duration;

		public readonly int intensity;

		public readonly float rampUp;

		public readonly float rampDown;

		public readonly float exitDelay;

		public readonly string name;

		public override float Duration => duration + exitDelay;

		internal MicroSensation(int frequency, float duration, int intensity, float rampUp, float rampDown, float exitDelay, string name = "")
		{
			this.frequency = Math.Clamp(frequency, 1, 100);
			this.duration = Math.Round(Math.Clamp(duration, 0.1f, 20f));
			this.intensity = Math.Clamp(intensity, 0, 100);
			this.rampUp = Math.Round(Math.Clamp(rampUp, 0f, 2f));
			this.rampDown = Math.Round(Math.Clamp(rampDown, 0f, 2f));
			this.exitDelay = Math.Round(Math.Clamp(exitDelay, 0f, 20f));
			this.name = name;
		}

		public MicroSensation WithName(string name)
		{
			return new MicroSensation(frequency, duration, intensity, rampUp, rampDown, exitDelay, name).WithPriority(base.Priority) as MicroSensation;
		}
	}
	public readonly struct Muscle
	{
		public readonly int id;

		public readonly int intensity;

		public static Muscle Pectoral_R = new Muscle(0);

		public static Muscle Pectoral_L = new Muscle(1);

		public static Muscle Abdominal_R = new Muscle(2);

		public static Muscle Abdominal_L = new Muscle(3);

		public static Muscle Arm_R = new Muscle(4);

		public static Muscle Arm_L = new Muscle(5);

		public static Muscle Dorsal_R = new Muscle(6);

		public static Muscle Dorsal_L = new Muscle(7);

		public static Muscle Lumbar_R = new Muscle(8);

		public static Muscle Lumbar_L = new Muscle(9);

		public static Muscle[] All => Front.Concat(Back).ToArray();

		public static Muscle[] Front => new Muscle[6] { Pectoral_R, Pectoral_L, Abdominal_R, Abdominal_L, Arm_R, Arm_L };

		public static Muscle[] Back => new Muscle[4] { Dorsal_R, Dorsal_L, Lumbar_R, Lumbar_L };

		internal Muscle(int id, int intensity = 100)
		{
			this.id = id;
			this.intensity = intensity;
		}

		public Muscle WithIntensity(int intensity)
		{
			return new Muscle(id, intensity);
		}

		public static Muscle[] Parse(string muscles)
		{
			return MusclesParser.Parse(muscles);
		}
	}
	internal static class BakedSensationsParser
	{
		private const char SEPARATOR = '~';

		public static bool CanParse(string message)
		{
			return (!Enumerable.Contains(message, ',') && !Enumerable.Contains(message, '|')) || Enumerable.Contains(message, '~');
		}

		public static BakedSensation From(string message)
		{
			if (!Enumerable.Contains(message, '~'))
			{
				return SensationsFactory.Create().Bake(int.Parse(message), "");
			}
			string[] array = message.Split(new char[1] { '~' });
			return new BakedSensation(int.Parse(array[0]), array[1], array[2], array[3], FamilyFrom(array));
		}

		private static Family FamilyFrom(string[] parameters)
		{
			return (parameters.Length < 5) ? Family.None : ((Family)parameters[4]);
		}
	}
	internal static class GamesParser
	{
		public static GameAuth From(string auth)
		{
			string[] array = auth.Split(new char[1] { '#' });
			if (int.TryParse(auth, out var _))
			{
				return new GameAuth().WithId(auth);
			}
			if (string.IsNullOrEmpty(array[0]))
			{
				return new GameAuth();
			}
			return new GameAuth("0", array.Select((string s) => BakedSensationsParser.From(s)).ToArray());
		}
	}
	internal static class MicrosensationsParser
	{
		public const char SEPARATOR = ',';

		public static MicroSensation From(string message)
		{
			string[] array = message.Split(new char[1] { ',' });
			return new MicroSensation(int.Parse(array[0]), float.Parse(array[1]) / 10f, int.Parse(array[2]), float.Parse(array[3]) / 1000f, float.Parse(array[4]) / 1000f, float.Parse(array[5]) / 10f, NameFrom(array));
		}

		private static string NameFrom(string[] parameters)
		{
			return (parameters.Length >= 7) ? parameters[6] : "";
		}
	}
	internal static class MusclesParser
	{
		public static Muscle[] Parse(string message)
		{
			string[] source = message.Split(new char[1] { ',' });
			return source.Select((string m) => ParseSingle(m)).ToArray();
		}

		public static Muscle ParseSingle(string message)
		{
			string[] array = message.Split(new char[1] { '%' });
			return new Muscle(int.Parse(array[0]), int.Parse(array[1]));
		}
	}
	internal static class SensationsParser
	{
		public static Sensation From(string message)
		{
			if (BakedSensationsParser.CanParse(message))
			{
				return BakedSensationsParser.From(message);
			}
			if (SequenceParser.CanParse(message))
			{
				return SequenceParser.From(message);
			}
			if (SensationWithMusclesParser.CanParse(message))
			{
				return SensationWithMusclesParser.From(message);
			}
			return MicrosensationsParser.From(message);
		}
	}
	internal static class SensationWithMusclesParser
	{
		public const char SEPARATOR = '|';

		public static bool CanParse(string message)
		{
			return Enumerable.Contains(message, '|');
		}

		public static SensationWithMuscles From(string message)
		{
			string[] array = message.Split(new char[1] { '|' });
			return new SensationWithMuscles(array[0], MusclesParser.Parse(array[1]));
		}
	}
	internal static class SequenceParser
	{
		public const char SEPARATOR = '&';

		public static bool CanParse(string message)
		{
			return Enumerable.Contains(message, '&');
		}

		public static Sensation From(string message)
		{
			string[] source = message.Split(new char[1] { '&' });
			Sensation[] sensations = ((IEnumerable<string>)source).Select((Func<string, Sensation>)((string s) => s)).ToArray();
			return new SensationsSequence(sensations);
		}
	}
	public abstract class Sensation
	{
		public int Priority { get; set; } = 0;


		public abstract float Duration { get; }

		public static Sensation Ball => SensationsFactory.Create();

		public static Sensation Dart => SensationsFactory.Create(10);

		public static Sensation Dagger => DaggerEntry.Append(DaggerMovement);

		public static Sensation DaggerEntry => SensationsFactory.Create(60, 0.2f);

		public static Sensation DaggerMovement => SensationsFactory.Create(100, 2f, 100, 0.3f, 0.1f);

		public static Sensation ShotWithExit => ShotEntry.Append(ShotExit).Append(ShotBleeding);

		public static Sensation ShotEntry => SensationsFactory.Create(30).WithMuscles(Muscle.Pectoral_R);

		public static Sensation ShotExit => SensationsFactory.Create(20).WithMuscles(Muscle.Dorsal_R);

		public static Sensation ShotBleeding => SensationsFactory.Create(50, 0.5f, 80, 0f, 0.3f).WithMuscles(Muscle.Pectoral_R, Muscle.Pectoral_L);

		public static Sensation Parse(string message)
		{
			return message;
		}

		public static implicit operator Sensation(string message)
		{
			return SensationsParser.From(message);
		}

		public static implicit operator string(Sensation sensation)
		{
			return SensationsBuilder.From(sensation);
		}

		public override string ToString()
		{
			return this;
		}
	}
	public static class SensationsFactory
	{
		public static MicroSensation Create(int frequency = 100, float durationSeconds = 0.1f, int intensityPercentage = 100, float rampUpMillis = 0f, float rampDownMillis = 0f, float exitDelaySeconds = 0f)
		{
			return new MicroSensation(frequency, durationSeconds, intensityPercentage, rampUpMillis, rampDownMillis, exitDelaySeconds);
		}
	}
	public class SensationsSequence : Sensation
	{
		public readonly List<Sensation> sensations;

		public override float Duration => sensations.Sum((Sensation s) => s.Duration);

		public SensationsSequence(params Sensation[] sensations)
		{
			this.sensations = new List<Sensation>(sensations);
		}
	}
	public class SensationWithMuscles : Sensation
	{
		public readonly Sensation reference;

		public readonly Muscle[] muscles;

		public override float Duration => reference.Duration;

		public SensationWithMuscles(Sensation reference, Muscle[] muscles)
		{
			this.reference = reference;
			this.muscles = muscles;
		}
	}
	public enum ConnectionState
	{
		Connected,
		Disconnected,
		Connecting
	}
	public interface Network
	{
		List<Address> ConnectedServers { get; }

		ConnectionState State { get; set; }

		bool IsConnecting { get; }

		bool IsConnected { get; }

		void SendTo(string message, string addressee);

		string Listen(out Address sender);

		void Connect(Address server);

		void Disconnect();

		void Close();

		void PortTo(int newPort);
	}
	public class Client
	{
		private readonly Network network;

		private readonly SendMessage sendMessage;

		private readonly FindServer findServer;

		private readonly ListenForDisconnection disconnection;

		private readonly CandidatesVault candidates;

		public ConnectionState State => network.State;

		public bool IsConnected => network.ConnectedServers != null && network.ConnectedServers.Count != 0;

		private bool CanScan => network.State == ConnectionState.Disconnected;

		public List<string> DiscoveredServers => candidates.StoredServers;

		internal Client(Network network, SendMessage sendMessage, FindServer findServer, ListenForDisconnection disconnection, CandidatesVault keys)
		{
			this.network = network;
			this.sendMessage = sendMessage;
			this.findServer = findServer;
			this.disconnection = disconnection;
			candidates = keys;
		}

		~Client()
		{
			Close();
		}

		internal Task ScanServer()
		{
			if (!CanScan)
			{
				return Task.CompletedTask;
			}
			candidates.Clean();
			return findServer.Scan();
		}

		internal Task FindServer(string auth, params string[] addresses)
		{
			if (!CanScan)
			{
				return Task.CompletedTask;
			}
			if (addresses[0] == "255.255.255.255")
			{
				candidates.Clean();
			}
			if (addresses.Length == 1)
			{
				return FindServer(addresses[0], auth);
			}
			return Task.Run(() => findServer.Execute(addresses, auth));
		}

		private async Task FindServer(string address, string auth)
		{
			await findServer.ExecuteWithAbscense(address, auth);
			ListenForDisconnection(address, auth);
		}

		private async Task ListenForDisconnection(string addressee, string auth)
		{
			if (await disconnection.Listen())
			{
				Disconnect();
				await FindServer(addressee, auth);
			}
		}

		public void Send(string message)
		{
			network.ConnectedServers.ForEach(delegate(Address server)
			{
				sendMessage.Execute(message, server);
			});
		}

		public void Disconnect()
		{
			network.Disconnect();
		}

		public void Close()
		{
			network.Close();
		}

		public void ChangeConnectionAttemptRate(int newRate)
		{
			findServer.DelayTime = newRate;
			disconnection.delayTime = newRate;
		}

		public void PortTo(int newPort)
		{
			network.PortTo(newPort);
		}
	}
	internal static class ClientFactory
	{
		public static Client Create(Network network, Message secretKey = default(Message), CandidatesVault keysVault = null, int scanDelayMs = 500)
		{
			if (keysVault == null)
			{
				keysVault = new CandidatesVault();
			}
			if (secretKey.addressee != null)
			{
				keysVault.Store(secretKey);
			}
			SendMessage sendMessage = new SendMessage(network);
			NotifyAbscense notifyAbscense = new NotifyAbscense(network, keysVault);
			SendAuthMessage sendAuth = new SendAuthMessage(sendMessage, keysVault);
			ReceiveAvailableApp receiveSecretKey = new ReceiveAvailableApp(keysVault, network);
			FindServer findServer = new FindServer(network, notifyAbscense, receiveSecretKey, sendAuth, keysVault)
			{
				DelayTime = scanDelayMs
			};
			ListenForDisconnection disconnection = new ListenForDisconnection(network);
			return new Client(network, sendMessage, findServer, disconnection, keysVault);
		}
	}
	internal class FindServer
	{
		private readonly Network network;

		private readonly NotifyAbscense notifyAbscense;

		private readonly ReceiveAvailableApp interpretAppMessage;

		private readonly SendAuthMessage sendAuth;

		private readonly CandidatesVault candidates;

		public int DelayTime = 500;

		private Task TimeBetweenAttempts => Task.Delay(DelayTime);

		public FindServer(Network network, NotifyAbscense notifyAbscense, ReceiveAvailableApp receiveSecretKey, SendAuthMessage sendAuth, CandidatesVault keys)
		{
			this.network = network;
			this.notifyAbscense = notifyAbscense;
			interpretAppMessage = receiveSecretKey;
			this.sendAuth = sendAuth;
			candidates = keys;
		}

		public async Task ExecuteWithAbscense(Address addressee, string auth)
		{
			await Execute(new string[1] { addressee }, auth);
			notifyAbscense.Execute(auth);
		}

		public async Task Execute(string[] addressees, string auth)
		{
			network.State = ConnectionState.Connecting;
			foreach (string address in addressees)
			{
				if (candidates.ContainsCandidate(address))
				{
					sendAuth.Execute(auth, address);
				}
			}
			do
			{
				Address sender;
				string lastMessage = ReceiveMessage(out sender);
				if (string.IsNullOrEmpty(lastMessage))
				{
					foreach (string address2 in addressees)
					{
						NotifyPresence(address2);
					}
				}
				if (lastMessage.Equals("okay"))
				{
					candidates.Store(new Message("", sender));
					sendAuth.Execute(auth, sender);
				}
				else if (IsConnectionVerification(addressees, sender, lastMessage))
				{
					network.Connect(sender);
				}
				await TimeBetweenAttempts;
				sender = default(Address);
			}
			while (network.ConnectedServers.Count != addressees.Count() && network.State != ConnectionState.Disconnected);
		}

		private bool IsConnectionVerification(string[] addresse, string sender, string lastMessage)
		{
			return lastMessage.Equals("pong") && (addresse.Contains(sender) || addresse[0] == "255.255.255.255");
		}

		private string ReceiveMessage(out Address sender)
		{
			return network.Listen(out sender);
		}

		public async Task Scan()
		{
			while (network.State == ConnectionState.Disconnected)
			{
				NotifyPresence("255.255.255.255");
				await interpretAppMessage.Execute();
				await TimeBetweenAttempts;
			}
		}

		private void NotifyPresence(string addressee)
		{
			network.SendTo("ping", addressee);
		}
	}
	internal class ListenForDisconnection
	{
		private readonly Network network;

		public int delayTime = 50;

		private Task ListenDelay => Task.Delay(delayTime);

		public ListenForDisconnection(Network network)
		{
			this.network = network;
		}

		public async Task<bool> Listen()
		{
			while (network.State == ConnectionState.Connected)
			{
				Address sender;
				string message = network.Listen(out sender);
				if (message.Equals("OWO_Close") && network.ConnectedServers.Contains(sender))
				{
					return true;
				}
				await ListenDelay;
				sender = default(Address);
			}
			return false;
		}
	}
	internal class NotifyAbscense
	{
		private readonly Network network;

		private readonly CandidatesVault candidates;

		public NotifyAbscense(Network network, CandidatesVault candidates)
		{
			this.network = network;
			this.candidates = candidates;
		}

		public void Execute(string authCommand)
		{
			string text = authCommand.Split(new char[1] { '*' })[0];
			foreach (string storedServer in candidates.StoredServers)
			{
				string message = text + "*GAMEUNAVAILABLE";
				network.SendTo(message, storedServer);
			}
		}
	}
	internal class ReceiveAvailableApp
	{
		private readonly Network network;

		private readonly CandidatesVault keys;

		public ReceiveAvailableApp(CandidatesVault secretKeys, Network network)
		{
			keys = secretKeys;
			this.network = network;
		}

		public Task Execute()
		{
			Address sender;
			string text = network.Listen(out sender);
			if (!text.Equals("okay"))
			{
				return Task.CompletedTask;
			}
			keys.Store(new Message("", sender));
			return Task.CompletedTask;
		}
	}
	internal class SendAuthMessage
	{
		private readonly SendMessage send;

		private readonly CandidatesVault keys;

		public SendAuthMessage(SendMessage send, CandidatesVault keys)
		{
			this.send = send;
			this.keys = keys;
		}

		public void Execute(string auth, Address addressee)
		{
			if (addressee.Equals(Address.Any))
			{
				foreach (string storedServer in keys.StoredServers)
				{
					send.Execute(auth, storedServer);
				}
				return;
			}
			if (keys.ContainsCandidate(addressee))
			{
				send.Execute(auth, addressee);
			}
		}
	}
	internal class SendMessage
	{
		private readonly Network network;

		public SendMessage(Network network)
		{
			this.network = network;
		}

		public void Execute(string message, Address addressee)
		{
			if (addressee.IsValid)
			{
				network.SendTo(message, addressee);
			}
		}
	}
	internal class ASCIIEncoder
	{
		public string Decode(byte[] buffer, int messageLength)
		{
			return Encoding.ASCII.GetString(buffer, 0, messageLength);
		}

		public byte[] Encode(string message)
		{
			return Encoding.ASCII.GetBytes(message);
		}
	}
	internal class UDPNetwork : Network
	{
		private readonly byte[] buffer;

		private readonly Socket socket;

		private readonly ASCIIEncoder encoding;

		private int PORT = 54020;

		public List<Address> ConnectedServers { get; private set; } = new List<Address>();


		public ConnectionState State { get; set; } = ConnectionState.Disconnected;


		public bool IsConnecting => State == ConnectionState.Connecting;

		public bool IsConnected => State == ConnectionState.Connected;

		public UDPNetwork()
		{
			buffer = new byte[1024];
			socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
			socket.EnableBroadcast = true;
			socket.ReceiveTimeout = 2500;
			socket.Blocking = false;
			encoding = new ASCIIEncoder();
		}

		public string Listen(out Address address)
		{
			try
			{
				EndPoint remoteEP = new IPEndPoint(0L, 0);
				string result = encoding.Decode(buffer, socket.ReceiveFrom(buffer, ref remoteEP));
				address = new Address((remoteEP as IPEndPoint).Address.ToString());
				return result;
			}
			catch
			{
				address = Address.Empty;
				return string.Empty;
			}
		}

		public void SendTo(string message, string addressee)
		{
			socket.SendTo(encoding.Encode(message), new IPEndPoint(IPAddress.Parse(addressee), PORT));
		}

		public void Connect(Address address)
		{
			if (!ConnectedServers.Contains(address))
			{
				ConnectedServers.Add(address);
			}
			State = ConnectionState.Connected;
		}

		public void Disconnect()
		{
			ConnectedServers.Clear();
			State = ConnectionState.Disconnected;
		}

		public void Close()
		{
			socket.Close();
		}

		public void PortTo(int newPort)
		{
			PORT = newPort;
		}
	}
	public struct Address
	{
		public readonly string value;

		public bool IsValid => !string.IsNullOrEmpty(value);

		public static Address Any => new Address("255.255.255.255");

		public static Address Empty => new Address(string.Empty);

		public static Address Null => new Address(null);

		public Address(string value)
		{
			this.value = value;
		}

		public static implicit operator string(Address addressee)
		{
			return addressee.value;
		}

		public static implicit operator Address(string value)
		{
			return new Address(value);
		}

		public static Address Create(string ip)
		{
			return new Address(ip);
		}
	}
	internal class CandidatesVault
	{
		private HashSet<Address> candidateServers = new HashSet<Address>();

		public Address LastApp => candidateServers.FirstOrDefault();

		public List<string> StoredServers => candidateServers.Select((Address candidate) => candidate.value).ToList();

		public void Store(Message message)
		{
			if (message.HasAddressee)
			{
				candidateServers.Add(message.addressee);
			}
		}

		public void Clean()
		{
			candidateServers.Clear();
		}

		public bool ContainsCandidate(Address address)
		{
			return candidateServers.Contains(address);
		}
	}
	internal struct Message
	{
		public readonly string value;

		public readonly string addressee;

		public bool IsEmpty => string.IsNullOrEmpty(value);

		public bool HasAddressee => !string.IsNullOrEmpty(addressee);

		public static Message Invalid => new Message(string.Empty, Address.Empty);

		public Message(string value, string addresseeIP)
		{
			this.value = value;
			addressee = addresseeIP;
		}
	}
}
namespace OWOGame.Infraestructure
{
	public class RealTimeClock
	{
		private readonly Stopwatch stopwatch;

		public long TotalMilliseconds => (long)stopwatch.Elapsed.TotalMilliseconds;

		public RealTimeClock()
		{
			stopwatch = new Stopwatch();
			stopwatch.Start();
		}
	}
}
namespace OWOGame.Controller
{
	internal class Connect
	{
		private GameAuth game = GameAuth.Empty;

		private readonly Client client;

		public Connect(Client client)
		{
			this.client = client;
		}

		public Task ScanServer()
		{
			return client.ScanServer();
		}

		public Task AutoConnect()
		{
			return client.FindServer($"{game.id}*AUTH*{game}", "255.255.255.255");
		}

		public Task ManualConnect(params string[] ips)
		{
			return client.FindServer($"{game.id}*AUTH*{game}", ips);
		}

		public void Configure(GameAuth game)
		{
			this.game = game;
		}
	}
	internal class Disconnect
	{
		private readonly Client client;

		public Disconnect(Client client)
		{
			this.client = client;
		}

		public void Execute()
		{
			if (client.State != ConnectionState.Disconnected)
			{
				client.Disconnect();
			}
		}
	}
	internal class SendMessage
	{
		private readonly Client client;

		public SendMessage(Client client)
		{
			this.client = client;
		}

		public void Execute(string message)
		{
			if (client.IsConnected)
			{
				client.Send(message);
			}
		}
	}
	internal class SendSensation : SendMessage
	{
		private long whenLastSensationEnds;

		private int lastPriority = -1;

		private GameAuth game = GameAuth.Empty;

		public SendSensation(Client network)
			: base(network)
		{
		}

		public void Execute(Sensation sensation, long currentTimeMs)
		{
			if (lastPriority <= sensation.Priority || currentTimeMs >= whenLastSensationEnds)
			{
				Execute($"{game.id}*SENSATION*{sensation}");
				whenLastSensationEnds = currentTimeMs + (int)(sensation.Duration * 1000f);
				lastPriority = sensation.Priority;
			}
		}

		public void Configure(GameAuth game)
		{
			this.game = game;
		}

		public void ResetPriority()
		{
			whenLastSensationEnds = 0L;
		}
	}
	internal class StopSensation : SendMessage
	{
		private GameAuth game = GameAuth.Empty;

		public StopSensation(Client network)
			: base(network)
		{
		}

		public void Execute()
		{
			Execute(game.id + "*STOP");
		}

		public void Configure(GameAuth game)
		{
			this.game = game;
		}
	}
}