using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mirror;
using Newtonsoft.Json;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("GameAssembly")]
[assembly: IgnoresAccessChecksTo("SharedAssembly")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("com.github.addzeey.SbgShock")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("com.github.addzeey.SbgShock")]
[assembly: AssemblyTitle("Super Battle Shock")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[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]
[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]
[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 SbgShock
{
internal interface IShockController : IDisposable
{
void EnqueueShock(int intensity, int duration, string? code = null);
}
internal static class NotificationOverlay
{
private readonly struct OverlayEntry
{
internal SbgNotification Notification { get; }
internal float ExpiresAt { get; }
internal OverlayEntry(SbgNotification notification, float expiresAt)
{
Notification = notification;
ExpiresAt = expiresAt;
}
}
private const int MaximumVisibleNotifications = 5;
private const float CardWidth = 400f;
private const float CardHeight = 48f;
private const float CardGap = 7f;
private const float ScreenMargin = 24f;
private const float FadeDuration = 0.35f;
private static readonly List<OverlayEntry> Entries = new List<OverlayEntry>();
private static GUIStyle? _cardStyle;
private static GUIStyle? _messageStyle;
private static Texture2D? _cardTexture;
private static Texture2D? _lightningTexture;
internal static void Enqueue(SbgNotification notification)
{
float num = Mathf.Clamp(Plugin.NotificationDurationSeconds.Value, 1f, 15f);
Entries.Add(new OverlayEntry(notification, Time.realtimeSinceStartup + num));
if (Entries.Count > 5)
{
Entries.RemoveAt(0);
}
}
internal static void Draw()
{
//IL_0078: Unknown result type (might be due to invalid IL or missing references)
//IL_007e: Invalid comparison between Unknown and I4
//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
//IL_0137: 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_0166: Unknown result type (might be due to invalid IL or missing references)
if (Plugin.NotificationDisplay == null || Plugin.NotificationDisplay.Value != Plugin.NotificationDisplayMode.Overlay || Entries.Count == 0)
{
return;
}
float realtimeSinceStartup = Time.realtimeSinceStartup;
for (int num = Entries.Count - 1; num >= 0; num--)
{
if (Entries[num].ExpiresAt <= realtimeSinceStartup)
{
Entries.RemoveAt(num);
}
}
if (Entries.Count != 0 && (int)Event.current.type == 7)
{
EnsureResources();
float num2 = Mathf.Min(400f, (float)Screen.width - 48f);
float num3 = (float)Entries.Count * 48f + (float)(Entries.Count - 1) * 7f;
float num4 = (float)Screen.width - num2 - 24f;
float num5 = Mathf.Max(24f, ((float)Screen.height - num3) * 0.5f);
Color color = GUI.color;
Color contentColor = GUI.contentColor;
for (int num6 = Entries.Count - 1; num6 >= 0; num6--)
{
OverlayEntry overlayEntry = Entries[num6];
float alpha = Mathf.Clamp01((overlayEntry.ExpiresAt - realtimeSinceStartup) / 0.35f);
DrawCard(new Rect(num4, num5, num2, 48f), overlayEntry.Notification, alpha);
num5 += 55f;
}
GUI.color = color;
GUI.contentColor = contentColor;
}
}
internal static void Dispose()
{
Entries.Clear();
DestroyTexture(ref _cardTexture);
DestroyTexture(ref _lightningTexture);
_cardStyle = null;
_messageStyle = null;
}
private static void DrawCard(Rect rect, SbgNotification notification, float alpha)
{
//IL_0016: 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_006c: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_008d: Unknown result type (might be due to invalid IL or missing references)
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
//IL_0093: 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_009f: Unknown result type (might be due to invalid IL or missing references)
//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
//IL_0133: Unknown result type (might be due to invalid IL or missing references)
GUI.color = new Color(0f, 0f, 0f, 0.28f * alpha);
GUI.Box(new Rect(((Rect)(ref rect)).x + 2f, ((Rect)(ref rect)).y + 3f, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height), GUIContent.none, _cardStyle);
GUI.color = new Color(1f, 1f, 1f, alpha);
GUI.Box(rect, GUIContent.none, _cardStyle);
Color accentColor = GetAccentColor(notification.Kind);
GUI.color = new Color(accentColor.r, accentColor.g, accentColor.b, alpha);
GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + 17f, ((Rect)(ref rect)).y + 12f, 17f, 24f), (Texture)(object)_lightningTexture, (ScaleMode)2, true);
GUI.contentColor = new Color(1f, 1f, 1f, alpha);
GUI.Label(new Rect(((Rect)(ref rect)).x + 47f, ((Rect)(ref rect)).y + 6f, ((Rect)(ref rect)).width - 63f, ((Rect)(ref rect)).height - 12f), notification.Message, _messageStyle);
}
private static void EnsureResources()
{
//IL_0035: 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_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Unknown result type (might be due to invalid IL or missing references)
//IL_005d: Expected O, but got Unknown
//IL_0062: Expected O, but got Unknown
//IL_0073: 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)
//IL_007f: 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_008e: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: 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_00a4: Expected O, but got Unknown
//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
//IL_00b7: Expected O, but got Unknown
//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)_cardTexture == (Object)null)
{
_cardTexture = CreateRoundedCardTexture();
}
if ((Object)(object)_lightningTexture == (Object)null)
{
_lightningTexture = CreateLightningTexture();
}
if (_cardStyle == null)
{
GUIStyle val = new GUIStyle();
val.normal.background = _cardTexture;
val.border = new RectOffset(15, 15, 15, 15);
_cardStyle = val;
}
if (_messageStyle == null)
{
_messageStyle = new GUIStyle(GUI.skin.label)
{
alignment = (TextAnchor)3,
clipping = (TextClipping)1,
fontSize = 15,
fontStyle = (FontStyle)1,
padding = new RectOffset(0, 0, 0, 0),
richText = false,
wordWrap = false
};
_messageStyle.normal.textColor = Color.white;
}
}
private static Texture2D CreateRoundedCardTexture()
{
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_0089: Unknown result type (might be due to invalid IL or missing references)
//IL_00bb: 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_00bc: Unknown result type (might be due to invalid IL or missing references)
Color val = default(Color);
((Color)(ref val))..ctor(0.22f, 0.2f, 0.23f, 0.9f);
Color val2 = default(Color);
((Color)(ref val2))..ctor(0.11f, 0.1f, 0.12f, 0.86f);
Color[] array = (Color[])(object)new Color[2048];
Vector2 point = default(Vector2);
for (int i = 0; i < 32; i++)
{
for (int j = 0; j < 64; j++)
{
((Vector2)(ref point))..ctor((float)j + 0.5f, (float)i + 0.5f);
if (IsInsideRoundedRect(point, 0f, 0f, 64f, 32f, 14f))
{
bool flag = IsInsideRoundedRect(point, 2f, 2f, 60f, 28f, 12f);
array[i * 64 + j] = (flag ? val2 : val);
}
}
}
Texture2D val3 = CreateTexture(64, 32, (FilterMode)1);
val3.SetPixels(array);
val3.Apply(false, true);
return val3;
}
private static Texture2D CreateLightningTexture()
{
//IL_0012: 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_0028: 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)
//IL_003e: 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_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_006a: 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_0080: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: 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_00c6: Unknown result type (might be due to invalid IL or missing references)
//IL_00db: Unknown result type (might be due to invalid IL or missing references)
//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
Vector2[] polygon = (Vector2[])(object)new Vector2[7]
{
new Vector2(10f, 27f),
new Vector2(18f, 27f),
new Vector2(13f, 17f),
new Vector2(19f, 17f),
new Vector2(5f, 0f),
new Vector2(9f, 12f),
new Vector2(2f, 12f)
};
Color[] array = (Color[])(object)new Color[560];
for (int i = 0; i < 28; i++)
{
for (int j = 0; j < 20; j++)
{
if (IsPointInPolygon(new Vector2((float)j + 0.5f, (float)i + 0.5f), polygon))
{
array[i * 20 + j] = Color.white;
}
}
}
Texture2D val = CreateTexture(20, 28, (FilterMode)0);
val.SetPixels(array);
val.Apply(false, true);
return val;
}
private static Texture2D CreateTexture(int width, int height, FilterMode filterMode)
{
//IL_0004: 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_000a: 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_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Expected O, but got Unknown
return new Texture2D(width, height, (TextureFormat)4, false)
{
filterMode = filterMode,
hideFlags = (HideFlags)61,
wrapMode = (TextureWrapMode)1
};
}
private static bool IsInsideRoundedRect(Vector2 point, float x, float y, float width, float height, float radius)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: 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)
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
float num = Mathf.Clamp(point.x, x + radius, x + width - radius);
float num2 = Mathf.Clamp(point.y, y + radius, y + height - radius);
float num3 = point.x - num;
float num4 = point.y - num2;
return num3 * num3 + num4 * num4 <= radius * radius;
}
private static bool IsPointInPolygon(Vector2 point, IReadOnlyList<Vector2> polygon)
{
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: 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_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: 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_003f: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: 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_0053: 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_0061: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
bool flag = false;
int num = 0;
int index = polygon.Count - 1;
while (num < polygon.Count)
{
Vector2 val = polygon[num];
Vector2 val2 = polygon[index];
if (val.y > point.y != val2.y > point.y && point.x < (val2.x - val.x) * (point.y - val.y) / (val2.y - val.y) + val.x)
{
flag = !flag;
}
index = num++;
}
return flag;
}
private static Color GetAccentColor(NotificationKind kind)
{
//IL_0019: 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_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: 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)
return (Color)(kind switch
{
NotificationKind.Warning => new Color(1f, 0.72f, 0.25f),
NotificationKind.Error => new Color(1f, 0.55f, 0.62f),
_ => new Color(0.82f, 0.9f, 0.25f),
});
}
private static void DestroyTexture(ref Texture2D? texture)
{
if ((Object)(object)texture != (Object)null)
{
Object.Destroy((Object)(object)texture);
}
texture = null;
}
}
internal enum NotificationKind
{
Info,
Warning,
Error
}
internal readonly struct SbgNotification
{
internal string Message { get; }
internal NotificationKind Kind { get; }
internal SbgNotification(string message, NotificationKind kind)
{
Message = message;
Kind = kind;
}
}
internal static class NotificationService
{
private const int MaximumPendingMessages = 20;
private static readonly ConcurrentQueue<SbgNotification> PendingMessages = new ConcurrentQueue<SbgNotification>();
private static readonly object QueueGate = new object();
private static int _pendingCount;
internal static void Publish(string message, bool showInGame, string? displayMessage = null)
{
Publish(message, NotificationKind.Info, showInGame, displayMessage);
}
internal static void PublishWarning(string message, bool showInGame, string? displayMessage = null)
{
Publish(message, NotificationKind.Warning, showInGame, displayMessage);
}
internal static void PublishError(string message, string displayMessage = "Shock failed. Check LogOutput.log.")
{
Publish(message, NotificationKind.Error, showInGame: true, displayMessage);
}
internal static void FlushToGame()
{
if (Plugin.NotificationDisplay == null || Plugin.NotificationDisplay.Value == Plugin.NotificationDisplayMode.Disabled)
{
ClearPendingMessages();
}
else if (Plugin.NotificationDisplay.Value == Plugin.NotificationDisplayMode.ChatFeed)
{
if (SingletonBehaviour<TextChatUi>.HasInstance)
{
SbgNotification notification;
while (TryDequeue(out notification))
{
TextChatUi.ShowMessage("[Super Battle Shock] " + GameManager.RichTextNoParse(notification.Message));
}
}
}
else
{
SbgNotification notification2;
while (TryDequeue(out notification2))
{
NotificationOverlay.Enqueue(notification2);
}
}
}
internal static void Clear()
{
ClearPendingMessages();
}
private static void Publish(string message, NotificationKind kind, bool showInGame, string? displayMessage)
{
string text = "[Super Battle Shock] " + message;
switch (kind)
{
case NotificationKind.Warning:
Plugin.Log.LogWarning((object)text);
break;
case NotificationKind.Error:
Plugin.Log.LogError((object)text);
break;
default:
Plugin.Log.LogInfo((object)text);
break;
}
if (!showInGame || Plugin.NotificationDisplay == null || Plugin.NotificationDisplay.Value == Plugin.NotificationDisplayMode.Disabled)
{
return;
}
lock (QueueGate)
{
PendingMessages.Enqueue(new SbgNotification(displayMessage ?? message, kind));
_pendingCount++;
if (_pendingCount > 20 && PendingMessages.TryDequeue(out var _))
{
_pendingCount--;
}
}
}
private static void ClearPendingMessages()
{
SbgNotification notification;
while (TryDequeue(out notification))
{
}
}
private static bool TryDequeue(out SbgNotification notification)
{
lock (QueueGate)
{
if (!PendingMessages.TryDequeue(out notification))
{
return false;
}
_pendingCount--;
return true;
}
}
}
internal sealed class OpenShockController : ShockControllerBase
{
public override void EnqueueShock(int intensity, int duration, string? code = null)
{
intensity = ShockControllerBase.ClampIntensity(intensity);
duration = ShockControllerBase.ClampDuration(duration);
string apiUrl = Plugin.OpenShockApiUrl.Value.TrimEnd('/');
string apiKey = Plugin.OpenShockApiKey.Value;
string deviceId = (string.IsNullOrWhiteSpace(code) ? Plugin.OpenShockDeviceId.Value : code);
if (string.IsNullOrWhiteSpace(apiUrl) || string.IsNullOrWhiteSpace(apiKey) || string.IsNullOrWhiteSpace(deviceId))
{
NotificationService.PublishWarning($"Dry run: would send OpenShock intensity={intensity}, duration={duration}s. Configure ApiUrl, ApiKey, and DeviceId.", showInGame: true, "Dry run: " + ShockControllerBase.FormatShockSummary(intensity, duration));
return;
}
EnqueueRequest("OpenShock", intensity, duration, (CancellationToken cancellationToken) => SendShockAsync(apiUrl, apiKey, deviceId, intensity, duration, cancellationToken));
}
private async Task SendShockAsync(string apiUrl, string apiKey, string deviceId, int intensity, int duration, CancellationToken cancellationToken)
{
int duration2 = duration * 1000;
var anon = new
{
Shocks = new[]
{
new
{
Id = deviceId,
Type = 1,
Intensity = intensity,
Duration = duration2
}
},
CustomName = "Integrations.SuperBattleShock"
};
try
{
using StringContent content = new StringContent(JsonConvert.SerializeObject((object)anon), Encoding.UTF8, "application/json");
using HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, apiUrl + "/2/shockers/control")
{
Content = content
};
request.Headers.Add("OpenShockToken", apiKey);
request.Headers.Add("User-Agent", "SuperBattleShock/1.0.0");
using HttpResponseMessage response = await base.Client.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
if (response.IsSuccessStatusCode)
{
NotificationService.Publish($"OpenShock accepted intensity={intensity}, duration={duration}s.", showInGame: true, ShockControllerBase.FormatShockSummary(intensity, duration) + " sent.");
return;
}
string responseBody = await response.Content.ReadAsStringAsync();
NotificationService.PublishError($"OpenShock rejected shock: {(int)response.StatusCode} {response.StatusCode}. " + "Response: " + ShockControllerBase.FormatResponseBody(responseBody));
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
}
catch (Exception ex2)
{
NotificationService.PublishError("OpenShock request failed: " + ex2.Message);
Plugin.Log.LogError((object)ex2);
}
}
}
internal sealed class PiShockController : ShockControllerBase
{
private const string PiShockIdLookupUrl = "https://pishock-lookup.addzeey.com/";
private const string PiShockV3DocsUrl = "https://docs.pishock.com/pishock/pishock-v3-documentation.html";
public override void EnqueueShock(int intensity, int duration, string? code = null)
{
intensity = ShockControllerBase.ClampIntensity(intensity);
duration = ShockControllerBase.ClampDuration(duration);
string apiUrl = Plugin.PiShockApiUrl.Value.TrimEnd('/');
string apiKey = Plugin.PiShockApiKey.Value;
string shockerId = (string.IsNullOrWhiteSpace(code) ? Plugin.PiShockShockerId.Value : code);
if (string.IsNullOrWhiteSpace(apiUrl) || string.IsNullOrWhiteSpace(apiKey) || string.IsNullOrWhiteSpace(shockerId))
{
NotificationService.PublishWarning(string.Format("Dry run: would send PiShock intensity={0}, duration={1}s. Configure ApiKey and ShockerId; lookup: {2}", intensity, duration, "https://pishock-lookup.addzeey.com/"), showInGame: true, "Dry run: " + ShockControllerBase.FormatShockSummary(intensity, duration));
return;
}
EnqueueRequest("PiShock", intensity, duration, (CancellationToken cancellationToken) => SendShockAsync(apiUrl, apiKey, shockerId, intensity, duration, cancellationToken));
}
private async Task SendShockAsync(string apiUrl, string apiKey, string shockerId, int intensity, int duration, CancellationToken cancellationToken)
{
string content = JsonConvert.SerializeObject((object)new
{
AgentName = "Super Battle Shock Mod",
Intensity = intensity,
Duration = duration * 1000,
Operation = 0
});
try
{
using StringContent content2 = new StringContent(content, Encoding.UTF8, "application/json");
using HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, apiUrl + "/Shockers/" + Uri.EscapeDataString(shockerId))
{
Content = content2
};
request.Headers.Add("X-PiShock-Api-Key", apiKey);
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using HttpResponseMessage response = await base.Client.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken);
if (response.IsSuccessStatusCode)
{
NotificationService.Publish($"PiShock accepted intensity={intensity}, duration={duration}s.", showInGame: true, ShockControllerBase.FormatShockSummary(intensity, duration) + " sent.");
return;
}
string responseBody = await response.Content.ReadAsStringAsync();
NotificationService.PublishError($"PiShock rejected shock: {(int)response.StatusCode} {response.StatusCode}. " + GetStatusExplanation(response.StatusCode) + " Response: " + ShockControllerBase.FormatResponseBody(responseBody));
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
}
catch (Exception ex2)
{
NotificationService.PublishError("PiShock request failed: " + ex2.Message);
Plugin.Log.LogError((object)ex2);
}
}
private static string GetStatusExplanation(HttpStatusCode statusCode)
{
return statusCode switch
{
HttpStatusCode.Unauthorized => "Verify the API key.",
HttpStatusCode.Forbidden => "Account access was rejected.",
HttpStatusCode.NotFound => "Verify the shocker ID with https://pishock-lookup.addzeey.com/",
HttpStatusCode.MethodNotAllowed => "This share does not permit shocks.",
HttpStatusCode.NotAcceptable => "Device must use PiShock V3 firmware: https://docs.pishock.com/pishock/pishock-v3-documentation.html",
HttpStatusCode.Gone => "The share is locked.",
HttpStatusCode.PreconditionFailed => "Intensity is outside share limits.",
HttpStatusCode.RequestedRangeNotSatisfiable => "Duration exceeds PiShock limits.",
HttpStatusCode.ServiceUnavailable => "The share or shocker is paused.",
_ => "Check PiShock status and configuration.",
};
}
}
[BepInPlugin("com.github.addzeey.SbgShock", "Super Battle Shock", "1.0.0")]
public class Plugin : BaseUnityPlugin
{
public enum ShockProvider
{
PiShock,
OpenShock
}
public enum HitTriggerMode
{
Disabled,
AnyHit,
KnockoutOnly
}
public enum NotificationDisplayMode
{
Disabled,
Overlay,
ChatFeed
}
public const string PluginGuid = "com.github.addzeey.SbgShock";
public const string PluginName = "Super Battle Shock";
public const string PluginVersion = "1.0.0";
private static readonly object ShockControllerGate = new object();
private static IShockController? _shockController;
private Harmony? _harmony;
internal static ManualLogSource Log { get; private set; } = null;
internal static ConfigFile CFG { get; private set; } = null;
internal static ConfigEntry<ShockProvider> ShockProviderType { get; private set; } = null;
internal static ConfigEntry<float> ShockCooldownSeconds { get; private set; } = null;
internal static ConfigEntry<bool> EnableShocksInLobby { get; private set; } = null;
internal static ConfigEntry<string> PiShockApiUrl { get; private set; } = null;
internal static ConfigEntry<string> PiShockApiKey { get; private set; } = null;
internal static ConfigEntry<string> PiShockShockerId { get; private set; } = null;
internal static ConfigEntry<string> OpenShockApiUrl { get; private set; } = null;
internal static ConfigEntry<string> OpenShockDeviceId { get; private set; } = null;
internal static ConfigEntry<string> OpenShockApiKey { get; private set; } = null;
internal static ConfigEntry<bool> EnableFailedHoleShock { get; private set; } = null;
internal static ConfigEntry<int> FailedHoleIntensity { get; private set; } = null;
internal static ConfigEntry<int> FailedHoleDuration { get; private set; } = null;
internal static ConfigEntry<bool> EnableWaterDeathShock { get; private set; } = null;
internal static ConfigEntry<int> WaterDeathIntensity { get; private set; } = null;
internal static ConfigEntry<int> WaterDeathDuration { get; private set; } = null;
internal static ConfigEntry<bool> EnableFogDeathShock { get; private set; } = null;
internal static ConfigEntry<int> FogDeathIntensity { get; private set; } = null;
internal static ConfigEntry<int> FogDeathDuration { get; private set; } = null;
internal static ConfigEntry<HitTriggerMode> ClubHitMode { get; private set; } = null;
internal static ConfigEntry<int> ClubHitIntensity { get; private set; } = null;
internal static ConfigEntry<int> ClubHitDuration { get; private set; } = null;
internal static ConfigEntry<HitTriggerMode> GolfBallHitMode { get; private set; } = null;
internal static ConfigEntry<int> GolfBallHitIntensity { get; private set; } = null;
internal static ConfigEntry<int> GolfBallHitDuration { get; private set; } = null;
internal static ConfigEntry<HitTriggerMode> DuelingPistolHitMode { get; private set; } = null;
internal static ConfigEntry<HitTriggerMode> ElephantGunHitMode { get; private set; } = null;
internal static ConfigEntry<HitTriggerMode> RailgunHitMode { get; private set; } = null;
internal static ConfigEntry<int> WeaponHitIntensity { get; private set; } = null;
internal static ConfigEntry<int> WeaponHitDuration { get; private set; } = null;
internal static ConfigEntry<HitTriggerMode> RocketHitMode { get; private set; } = null;
internal static ConfigEntry<HitTriggerMode> RocketBackBlastHitMode { get; private set; } = null;
internal static ConfigEntry<HitTriggerMode> LandmineHitMode { get; private set; } = null;
internal static ConfigEntry<HitTriggerMode> OrbitalLaserHitMode { get; private set; } = null;
internal static ConfigEntry<HitTriggerMode> ThunderstormHitMode { get; private set; } = null;
internal static ConfigEntry<HitTriggerMode> ElectromagnetExplosionHitMode { get; private set; } = null;
internal static ConfigEntry<int> ExplosiveHitIntensity { get; private set; } = null;
internal static ConfigEntry<int> ExplosiveHitDuration { get; private set; } = null;
internal static ConfigEntry<HitTriggerMode> GolfCartHitMode { get; private set; } = null;
internal static ConfigEntry<int> GolfCartIntensity { get; private set; } = null;
internal static ConfigEntry<int> GolfCartDuration { get; private set; } = null;
internal static ConfigEntry<HitTriggerMode> RocketDriverHitMode { get; private set; } = null;
internal static ConfigEntry<HitTriggerMode> JumboBurgerHitMode { get; private set; } = null;
internal static ConfigEntry<HitTriggerMode> TrafficVehicleHitMode { get; private set; } = null;
internal static ConfigEntry<bool> EnableUnmappedKnockoutShock { get; private set; } = null;
internal static ConfigEntry<int> UnmappedKnockoutIntensity { get; private set; } = null;
internal static ConfigEntry<int> UnmappedKnockoutDuration { get; private set; } = null;
internal static ConfigEntry<NotificationDisplayMode> NotificationDisplay { get; private set; } = null;
internal static ConfigEntry<bool> ShowSkippedNotifications { get; private set; } = null;
internal static ConfigEntry<float> NotificationDurationSeconds { get; private set; } = null;
private void Awake()
{
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
CFG = ((BaseUnityPlugin)this).Config;
BindConfig();
InitializeShockController();
ShockProviderType.SettingChanged += OnShockProviderChanged;
_harmony = new Harmony("com.github.addzeey.SbgShock");
try
{
_harmony.PatchAll(Assembly.GetExecutingAssembly());
}
catch (Exception arg)
{
_harmony.UnpatchSelf();
Log.LogError((object)$"[Super Battle Shock] Failed to apply gameplay hooks: {arg}");
NotificationService.Publish("Hook setup failed; check LogOutput.log.", showInGame: true);
return;
}
Log.LogInfo((object)"Plugin Super Battle Shock is loaded!");
NotificationService.Publish("Loaded v1.0.0. Event logging is active.", showInGame: true);
}
private void Update()
{
NotificationService.FlushToGame();
}
private void OnGUI()
{
NotificationOverlay.Draw();
}
private void OnDestroy()
{
ShockProviderType.SettingChanged -= OnShockProviderChanged;
Harmony? harmony = _harmony;
if (harmony != null)
{
harmony.UnpatchSelf();
}
DisposeShockController();
ShockControllerBase.ResetCooldown();
EliminationShockHandler.ClearPending();
NotificationService.Clear();
NotificationOverlay.Dispose();
ShockDispatcher.Reset();
}
private static void BindConfig()
{
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Expected O, but got Unknown
//IL_053c: Unknown result type (might be due to invalid IL or missing references)
//IL_0546: Expected O, but got Unknown
ShockProviderType = CFG.Bind<ShockProvider>("Shock", "Provider", ShockProvider.PiShock, "Choose PiShock or OpenShock.");
ShockCooldownSeconds = CFG.Bind<float>("Shock", "ShockCooldownSeconds", 2f, new ConfigDescription("Minimum seconds between outgoing requests.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), Array.Empty<object>()));
EnableShocksInLobby = CFG.Bind<bool>("Shock", "EnableShocksInLobby", false, "Allow shocks in the multiplayer Driving Range lobby. Disabled by default.");
PiShockApiUrl = CFG.Bind<string>("PiShock", "ApiUrl", "https://api.pishock.com", "PiShock API URL.");
PiShockApiKey = CFG.Bind<string>("PiShock", "ApiKey", string.Empty, "Your PiShock API key.");
PiShockShockerId = CFG.Bind<string>("PiShock", "ShockerId", string.Empty, "Your PiShock V3 shocker ID.");
OpenShockApiUrl = CFG.Bind<string>("OpenShock", "ApiUrl", "https://api.openshock.app", "OpenShock API URL.");
OpenShockDeviceId = CFG.Bind<string>("OpenShock", "DeviceId", string.Empty, "OpenShock device ID or share ID.");
OpenShockApiKey = CFG.Bind<string>("OpenShock", "ApiKey", string.Empty, "OpenShock API key.");
EnableFailedHoleShock = CFG.Bind<bool>("Events", "EnableFailedHoleShock", true, "Shock when the local player times out instead of finishing the hole.");
FailedHoleIntensity = BindIntensity("Events", "FailedHoleIntensity", 55, "Intensity for a failed hole timeout shock.");
FailedHoleDuration = BindDuration("Events", "FailedHoleDuration", 2, "Duration for a failed hole timeout shock.");
EnableWaterDeathShock = CFG.Bind<bool>("Deaths", "EnableWaterDeathShock", true, "Shock when the local player falls into water and starts waiting to respawn.");
WaterDeathIntensity = BindIntensity("Deaths", "WaterDeathIntensity", 10, "Intensity for falling into water.");
WaterDeathDuration = BindDuration("Deaths", "WaterDeathDuration", 1, "Duration for falling into water.");
EnableFogDeathShock = CFG.Bind<bool>("Deaths", "EnableFogDeathShock", true, "Shock when the local player falls into fog and starts waiting to respawn.");
FogDeathIntensity = BindIntensity("Deaths", "FogDeathIntensity", 10, "Intensity for falling into fog.");
FogDeathDuration = BindDuration("Deaths", "FogDeathDuration", 1, "Duration for falling into fog.");
ClubHitMode = BindHitMode("ClubHitMode", "club swings");
ClubHitIntensity = BindIntensity("Events", "ClubHitIntensity", 25, "Intensity for club hit shocks.");
ClubHitDuration = BindDuration("Events", "ClubHitDuration", 1, "Duration for club hit shocks.");
GolfBallHitMode = BindHitMode("GolfBallHitMode", "club-launched or reflected golf balls");
GolfBallHitIntensity = BindIntensity("Events", "GolfBallHitIntensity", 25, "Intensity for golf ball hit shocks.");
GolfBallHitDuration = BindDuration("Events", "GolfBallHitDuration", 1, "Duration for golf ball hit shocks.");
DuelingPistolHitMode = BindHitMode("DuelingPistolHitMode", "dueling pistol hits");
ElephantGunHitMode = BindHitMode("ElephantGunHitMode", "elephant gun hits");
RailgunHitMode = BindHitMode("RailgunHitMode", "railgun hits");
WeaponHitIntensity = BindIntensity("Events", "WeaponHitIntensity", 35, "Intensity for direct weapon hit shocks.");
WeaponHitDuration = BindDuration("Events", "WeaponHitDuration", 1, "Duration for direct weapon hit shocks.");
RocketHitMode = BindHitMode("RocketHitMode", "rocket hits");
RocketBackBlastHitMode = BindHitMode("RocketBackBlastHitMode", "rocket backblast hits");
LandmineHitMode = BindHitMode("LandmineHitMode", "landmine hits");
OrbitalLaserHitMode = BindHitMode("OrbitalLaserHitMode", "orbital laser hits");
ThunderstormHitMode = BindHitMode("ThunderstormHitMode", "thunderstorm hits");
ElectromagnetExplosionHitMode = BindHitMode("ElectromagnetExplosionHitMode", "electromagnet shield explosions");
ExplosiveHitIntensity = BindIntensity("Events", "ExplosiveHitIntensity", 50, "Intensity for explosive hit shocks.");
ExplosiveHitDuration = BindDuration("Events", "ExplosiveHitDuration", 2, "Duration for explosive hit shocks.");
GolfCartHitMode = BindHitMode("GolfCartHitMode", "golf cart hits");
GolfCartIntensity = BindIntensity("Events", "GolfCartIntensity", 45, "Intensity for golf cart shocks.");
GolfCartDuration = BindDuration("Events", "GolfCartDuration", 2, "Duration for golf cart shocks.");
RocketDriverHitMode = BindHitMode("RocketDriverHitMode", "Rocket Driver hits");
JumboBurgerHitMode = BindHitMode("JumboBurgerHitMode", "Jumbo Burger giant hits");
TrafficVehicleHitMode = BindHitMode("TrafficVehicleHitMode", "traffic vehicle hits");
EnableUnmappedKnockoutShock = CFG.Bind<bool>("Events", "EnableUnmappedKnockoutShock", false, "Optional shock for new knockout causes without a dedicated hit setting.");
UnmappedKnockoutIntensity = BindIntensity("Events", "UnmappedKnockoutIntensity", 30, "Intensity for unmapped knockout shocks.");
UnmappedKnockoutDuration = BindDuration("Events", "UnmappedKnockoutDuration", 1, "Duration for unmapped knockout shocks.");
NotificationDisplay = CFG.Bind<NotificationDisplayMode>("Notifications", "DisplayMode", NotificationDisplayMode.Overlay, "Where local Super Battle Shock status messages appear: Disabled, Overlay, or ChatFeed.");
ShowSkippedNotifications = CFG.Bind<bool>("Notifications", "ShowSkippedNotifications", false, "Show cooldown and disabled-event skips in game. All skips are still logged.");
NotificationDurationSeconds = CFG.Bind<float>("Notifications", "DurationSeconds", 4f, new ConfigDescription("How long overlay notifications remain visible.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 15f), Array.Empty<object>()));
static ConfigEntry<int> BindDuration(string section, string key, int defaultValue, string description)
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Expected O, but got Unknown
return CFG.Bind<int>(section, key, defaultValue, new ConfigDescription(description + " Range: 1-15 seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 15), Array.Empty<object>()));
}
static ConfigEntry<HitTriggerMode> BindHitMode(string key, string cause)
{
return CFG.Bind<HitTriggerMode>("Events", key, HitTriggerMode.KnockoutOnly, "When " + cause + " trigger a shock: Disabled, AnyHit, or KnockoutOnly.");
}
static ConfigEntry<int> BindIntensity(string section, string key, int defaultValue, string description)
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: Expected O, but got Unknown
return CFG.Bind<int>(section, key, defaultValue, new ConfigDescription(description + " Range: 1-100.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
}
}
private static void InitializeShockController()
{
IShockController shockController2;
if (ShockProviderType.Value != ShockProvider.PiShock)
{
IShockController shockController = new OpenShockController();
shockController2 = shockController;
}
else
{
IShockController shockController = new PiShockController();
shockController2 = shockController;
}
IShockController shockController3 = shockController2;
IShockController shockController4;
lock (ShockControllerGate)
{
shockController4 = _shockController;
_shockController = null;
}
shockController4?.Dispose();
lock (ShockControllerGate)
{
_shockController = shockController3;
}
Log.LogInfo((object)$"[Super Battle Shock] Initialized {ShockProviderType.Value} controller.");
}
internal static bool EnqueueShock(int intensity, int duration)
{
lock (ShockControllerGate)
{
if (_shockController == null)
{
return false;
}
_shockController.EnqueueShock(intensity, duration);
return true;
}
}
private static void DisposeShockController()
{
IShockController shockController;
lock (ShockControllerGate)
{
shockController = _shockController;
_shockController = null;
}
shockController?.Dispose();
}
private static void OnShockProviderChanged(object sender, EventArgs args)
{
InitializeShockController();
NotificationService.Publish($"Provider changed to {ShockProviderType.Value}.", showInGame: true);
}
}
internal abstract class ShockControllerBase : IShockController, IDisposable
{
private const int MaximumResponseBodyLength = 512;
private const int MaximumResponseBufferLength = 4096;
private static readonly TimeSpan RequestTimeout = TimeSpan.FromSeconds(10.0);
private static readonly object CooldownGate = new object();
private static double _lastRequestTime = double.NegativeInfinity;
private readonly ShockRequestQueue _queue = new ShockRequestQueue();
protected HttpClient Client { get; } = new HttpClient();
protected ShockControllerBase()
{
Client.Timeout = RequestTimeout;
Client.MaxResponseContentBufferSize = 4096L;
}
public abstract void EnqueueShock(int intensity, int duration, string? code = null);
protected void EnqueueRequest(string providerName, int intensity, int duration, Func<CancellationToken, Task> sendRequest)
{
if (_queue.TryEnqueue(async delegate(CancellationToken cancellationToken)
{
if (!TryReserveCooldown(out var remainingSeconds))
{
NotificationService.Publish($"{providerName} request skipped by cooldown ({remainingSeconds:0.0}s remaining).", Plugin.ShowSkippedNotifications.Value, "Shock skipped: cooldown active.");
}
else
{
await sendRequest(cancellationToken);
}
}))
{
NotificationService.Publish($"{providerName} request queued: intensity={intensity}, duration={duration}s.", showInGame: false);
}
else
{
NotificationService.PublishWarning(providerName + " request skipped because another request is already in progress.", Plugin.ShowSkippedNotifications.Value, "Shock skipped: request already in progress.");
}
}
protected static int ClampIntensity(int intensity)
{
return Math.Clamp(intensity, 1, 100);
}
protected static int ClampDuration(int duration)
{
return Math.Clamp(duration, 1, 15);
}
protected static string FormatShockSummary(int intensity, int duration)
{
return $"{duration} second shock at {intensity}%";
}
protected static string FormatResponseBody(string responseBody)
{
if (string.IsNullOrWhiteSpace(responseBody))
{
return "<empty>";
}
int num = Math.Min(responseBody.Length, 512);
StringBuilder stringBuilder = new StringBuilder(num + 3);
for (int i = 0; i < num; i++)
{
char c = responseBody[i];
stringBuilder.Append((char.IsControl(c) && !char.IsWhiteSpace(c)) ? '?' : c);
}
if (responseBody.Length > num)
{
stringBuilder.Append("...");
}
return stringBuilder.ToString();
}
internal static void ResetCooldown()
{
lock (CooldownGate)
{
_lastRequestTime = double.NegativeInfinity;
}
}
private bool TryReserveCooldown(out double remainingSeconds)
{
lock (CooldownGate)
{
double num = (double)Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
double num2 = Plugin.ShockCooldownSeconds.Value;
double num3 = num - _lastRequestTime;
if (num3 < num2)
{
remainingSeconds = num2 - num3;
return false;
}
_lastRequestTime = num;
remainingSeconds = 0.0;
return true;
}
}
public void Dispose()
{
_queue.Dispose();
Client.Dispose();
}
}
internal static class ShockDispatcher
{
private const double LocalDedupWindowSeconds = 0.75;
private static readonly Dictionary<string, double> RecentEvents = new Dictionary<string, double>();
private static readonly Dictionary<string, double> RecentDuplicateLogs = new Dictionary<string, double>();
private static readonly object Gate = new object();
internal static void Trigger(string eventKey, int intensity, int duration, string reason)
{
if (!Plugin.EnableShocksInLobby.Value && SingletonBehaviour<DrivingRangeManager>.HasInstance)
{
NotificationService.Publish("Ignored " + reason + ": shocks are disabled in the Driving Range lobby.", Plugin.ShowSkippedNotifications.Value, "Shock skipped: disabled in lobby.");
return;
}
intensity = Math.Clamp(intensity, 1, 100);
duration = Math.Clamp(duration, 1, 15);
double num = (double)Stopwatch.GetTimestamp() / (double)Stopwatch.Frequency;
bool flag = false;
bool flag2;
lock (Gate)
{
flag2 = RecentEvents.TryGetValue(eventKey, out var value) && num - value < 0.75;
RecentEvents[eventKey] = num;
if (flag2 && (!RecentDuplicateLogs.TryGetValue(eventKey, out var value2) || num - value2 >= 0.75))
{
RecentDuplicateLogs[eventKey] = num;
flag = true;
}
}
if (flag2)
{
if (flag)
{
NotificationService.Publish($"Ignored repeated {reason} within {750.0:0}ms.", Plugin.ShowSkippedNotifications.Value, "Shock skipped: repeated event.");
}
}
else
{
NotificationService.Publish($"Detected {reason}: intensity={intensity}, duration={duration}s.", showInGame: false);
Plugin.EnqueueShock(intensity, duration);
}
}
internal static void Reset()
{
lock (Gate)
{
RecentEvents.Clear();
RecentDuplicateLogs.Clear();
}
}
}
[HarmonyPatch]
internal static class ShockHooks
{
[HarmonyPatch(typeof(PlayerMovement), "TryKnockOut")]
[HarmonyPostfix]
private static void TryKnockOutPostfix(PlayerMovement __instance, KnockoutType knockoutType, bool __result, bool isNewKnockout)
{
//IL_0009: 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_0058: Unknown result type (might be due to invalid IL or missing references)
if (LocalPlayerGuard.IsLocal(__instance))
{
if (ShockHitHandler.TryGetSettings(knockoutType, out var settings))
{
ShockHitHandler.Handle(settings, __result, isNewKnockout);
}
else if (__result && isNewKnockout && Plugin.EnableUnmappedKnockoutShock.Value)
{
ShockDispatcher.Trigger($"ragdoll-{knockoutType}", Plugin.UnmappedKnockoutIntensity.Value, Plugin.UnmappedKnockoutDuration.Value, $"unmapped knockout via {knockoutType}");
}
}
}
[HarmonyPatch(typeof(PlayerMovement), "OnLocalPlayerWillApplyItemHitPhysics")]
[HarmonyPostfix]
private static void OnLocalPlayerWillApplyItemHitPhysicsPostfix(PlayerMovement __instance, ItemType itemType, float distance)
{
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Invalid comparison between Unknown and I4
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0013: Invalid comparison between Unknown and I4
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Invalid comparison between Unknown and I4
if (!LocalPlayerGuard.IsLocal(__instance))
{
return;
}
HitSettings settings;
if ((int)itemType != 10)
{
if ((int)itemType != 15)
{
if ((int)itemType != 16)
{
return;
}
settings = ShockHitHandler.GetRailgunSettings();
}
else
{
if (!(distance <= GameManager.ItemSettings.ThunderstormLightningStrikeVaporizationRadius))
{
return;
}
settings = ShockHitHandler.GetThunderstormSettings();
}
}
else
{
if (!(distance <= GameManager.ItemSettings.OrbitalLaserExplosionCenterRadius))
{
return;
}
settings = ShockHitHandler.GetOrbitalLaserSettings();
}
ShockHitHandler.HandleAnyHit(settings);
}
[HarmonyPatch(typeof(PlayerGolfer), "OnLocalPlayerWillBeEliminated")]
[HarmonyPostfix]
private static void OnLocalPlayerWillBeEliminatedPostfix(PlayerGolfer __instance, EliminationReason immediateEliminationReason)
{
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Invalid comparison between Unknown and I4
//IL_0052: 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)
if (!LocalPlayerGuard.IsLocal(__instance))
{
return;
}
EliminationShockHandler.ClearPending();
HitSettings settings;
if ((int)immediateEliminationReason == 7)
{
if (Plugin.EnableFailedHoleShock.Value)
{
ShockDispatcher.Trigger("timed-out", Plugin.FailedHoleIntensity.Value, Plugin.FailedHoleDuration.Value, "timed out before finishing hole");
}
else
{
ShockHitHandler.LogSkipped("hole timeout", "disabled");
}
}
else if (ShockHitHandler.TryGetInstantEliminationSettings(immediateEliminationReason, out settings))
{
if (settings.Mode == Plugin.HitTriggerMode.KnockoutOnly)
{
ShockDispatcher.Trigger(settings.EventKey, settings.Intensity, settings.Duration, settings.Reason + " elimination");
}
}
else
{
EliminationShockHandler.HandleElimination(__instance.PlayerInfo, immediateEliminationReason);
}
}
[HarmonyPatch(typeof(PlayerMovement), "OnRespawnStateChanged")]
[HarmonyPostfix]
private static void OnRespawnStateChangedPostfix(PlayerMovement __instance, RespawnState previousState, RespawnState currentState)
{
//IL_0009: 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_000e: Invalid comparison between Unknown and I4
if (LocalPlayerGuard.IsLocal(__instance) && (int)previousState == 0 && (int)currentState == 1)
{
EliminationShockHandler.HandleRespawnStarted(__instance.PlayerInfo);
}
}
[HarmonyPatch(typeof(PlayerMovement), "LocalPlayerBeginRespawn")]
[HarmonyPrefix]
private static void LocalPlayerBeginRespawnPrefix(PlayerMovement __instance, bool isRestart)
{
if (isRestart && LocalPlayerGuard.IsLocal(__instance))
{
EliminationShockHandler.ClearPending();
}
}
}
internal static class EliminationShockHandler
{
private static PlayerInfo? _pendingPlayer;
private static EliminationReason _pendingReason;
internal static void HandleElimination(PlayerInfo player, EliminationReason reason)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Invalid comparison between Unknown and I4
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_0004: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Invalid comparison between Unknown and I4
//IL_0032: 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)
if ((int)reason == 6 || (int)reason == 23)
{
EliminationSettings settings = GetSettings(reason);
if (!settings.Enabled)
{
ShockHitHandler.LogSkipped(settings.Reason, "death shock disabled");
return;
}
_pendingPlayer = player;
_pendingReason = reason;
NotificationService.Publish("Detected " + settings.Reason + "; waiting for confirmed local respawn.", Plugin.ShowSkippedNotifications.Value, "Waiting for respawn to start.");
}
}
internal static void HandleRespawnStarted(PlayerInfo player)
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)_pendingPlayer == (Object)null || player != _pendingPlayer)
{
ClearPending();
return;
}
EliminationSettings settings = GetSettings(_pendingReason);
ClearPending();
if (settings.Enabled)
{
ShockDispatcher.Trigger(settings.EventKey, settings.Intensity, settings.Duration, settings.Reason + " (respawn started)");
}
}
internal static void ClearPending()
{
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
_pendingPlayer = null;
_pendingReason = (EliminationReason)0;
}
private static EliminationSettings GetSettings(EliminationReason reason)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0002: Invalid comparison between Unknown and I4
//IL_0004: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Invalid comparison between Unknown and I4
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
if ((int)reason != 6)
{
if ((int)reason == 23)
{
return new EliminationSettings("fog-death", "fell into fog", Plugin.EnableFogDeathShock.Value, Plugin.FogDeathIntensity.Value, Plugin.FogDeathDuration.Value);
}
throw new ArgumentOutOfRangeException("reason", reason, "Unsupported elimination reason.");
}
return new EliminationSettings("water-death", "fell into water", Plugin.EnableWaterDeathShock.Value, Plugin.WaterDeathIntensity.Value, Plugin.WaterDeathDuration.Value);
}
}
internal static class LocalPlayerGuard
{
internal static bool IsLocal(PlayerMovement movement)
{
if ((Object)(object)movement != (Object)null && ((NetworkBehaviour)movement).isLocalPlayer && (Object)(object)GameManager.LocalPlayerInfo != (Object)null)
{
return movement.PlayerInfo == GameManager.LocalPlayerInfo;
}
return false;
}
internal static bool IsLocal(PlayerGolfer golfer)
{
if ((Object)(object)golfer != (Object)null && ((NetworkBehaviour)golfer).isLocalPlayer && (Object)(object)GameManager.LocalPlayerInfo != (Object)null)
{
return golfer.PlayerInfo == GameManager.LocalPlayerInfo;
}
return false;
}
}
internal readonly struct EliminationSettings
{
internal string EventKey { get; }
internal string Reason { get; }
internal bool Enabled { get; }
internal int Intensity { get; }
internal int Duration { get; }
internal EliminationSettings(string eventKey, string reason, bool enabled, int intensity, int duration)
{
EventKey = eventKey;
Reason = reason;
Enabled = enabled;
Intensity = intensity;
Duration = duration;
}
}
internal static class ShockHitHandler
{
internal static void HandleAnyHit(HitSettings settings)
{
if (settings.Mode == Plugin.HitTriggerMode.AnyHit)
{
Handle(settings, wasApplied: false, isNewKnockout: false);
}
else if (settings.Mode == Plugin.HitTriggerMode.Disabled)
{
LogSkipped(settings.Reason, "disabled");
}
}
internal static void Handle(HitSettings settings, bool wasApplied, bool isNewKnockout)
{
if (settings.Mode == Plugin.HitTriggerMode.Disabled)
{
LogSkipped(settings.Reason, "disabled");
return;
}
if (settings.Mode == Plugin.HitTriggerMode.KnockoutOnly && (!wasApplied || !isNewKnockout))
{
LogSkipped(settings.Reason, "no new knockout");
return;
}
int intensity = Mathf.Clamp(settings.Intensity, 1, 100);
string text = ((wasApplied && isNewKnockout) ? "new knockout" : "hit without new knockout");
ShockDispatcher.Trigger(settings.EventKey, intensity, settings.Duration, settings.Reason + " (" + text + ")");
}
internal static bool TryGetSettings(KnockoutType knockoutType, out HitSettings settings)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_009e: Expected I4, but got Unknown
switch ((int)knockoutType)
{
case 0:
settings = new HitSettings("club-hit", "club hit", Plugin.ClubHitMode.Value, Plugin.ClubHitIntensity.Value, Plugin.ClubHitDuration.Value);
return true;
case 1:
case 13:
settings = new HitSettings("golf-ball-hit", "golf ball hit", Plugin.GolfBallHitMode.Value, Plugin.GolfBallHitIntensity.Value, Plugin.GolfBallHitDuration.Value);
return true;
case 4:
case 15:
settings = new HitSettings("dueling-pistol", "dueling pistol hit", Plugin.DuelingPistolHitMode.Value, Plugin.WeaponHitIntensity.Value, Plugin.WeaponHitDuration.Value);
return true;
case 5:
case 16:
settings = new HitSettings("elephant-gun", "elephant gun hit", Plugin.ElephantGunHitMode.Value, Plugin.WeaponHitIntensity.Value, Plugin.WeaponHitDuration.Value);
return true;
case 26:
case 28:
case 37:
settings = GetRailgunSettings();
return true;
case 7:
case 14:
settings = new HitSettings("rocket", "rocket hit", Plugin.RocketHitMode.Value, Plugin.ExplosiveHitIntensity.Value, Plugin.ExplosiveHitDuration.Value);
return true;
case 8:
settings = new HitSettings("rocket-backblast", "rocket backblast hit", Plugin.RocketBackBlastHitMode.Value, Plugin.ExplosiveHitIntensity.Value, Plugin.ExplosiveHitDuration.Value);
return true;
case 12:
settings = new HitSettings("landmine", "landmine hit", Plugin.LandmineHitMode.Value, Plugin.ExplosiveHitIntensity.Value, Plugin.ExplosiveHitDuration.Value);
return true;
case 17:
case 25:
case 32:
case 35:
settings = GetOrbitalLaserSettings();
return true;
case 23:
case 24:
case 33:
case 36:
settings = GetThunderstormSettings();
return true;
case 34:
settings = new HitSettings("electromagnet-explosion", "electromagnet shield explosion", Plugin.ElectromagnetExplosionHitMode.Value, Plugin.ExplosiveHitIntensity.Value, Plugin.ExplosiveHitDuration.Value);
return true;
case 6:
settings = new HitSettings("golf-cart", "golf cart hit", Plugin.GolfCartHitMode.Value, Plugin.GolfCartIntensity.Value, Plugin.GolfCartDuration.Value);
return true;
case 18:
settings = new HitSettings("rocket-driver-swing", "Rocket Driver swing", Plugin.RocketDriverHitMode.Value, Plugin.ClubHitIntensity.Value, Plugin.ClubHitDuration.Value);
return true;
case 19:
settings = new HitSettings("rocket-driver-spin", "Rocket Driver spin hit", Plugin.RocketDriverHitMode.Value, Plugin.ClubHitIntensity.Value, Plugin.ClubHitDuration.Value);
return true;
case 20:
settings = new HitSettings("rocket-driver-projectile", "Rocket Driver projectile hit", Plugin.RocketDriverHitMode.Value, Plugin.ClubHitIntensity.Value, Plugin.ClubHitDuration.Value);
return true;
case 29:
settings = new HitSettings("jumbo-burger-swing", "Jumbo Burger giant swing", Plugin.JumboBurgerHitMode.Value, Plugin.ClubHitIntensity.Value, Plugin.ClubHitDuration.Value);
return true;
case 30:
settings = new HitSettings("jumbo-burger-projectile", "Jumbo Burger giant projectile hit", Plugin.JumboBurgerHitMode.Value, Plugin.ClubHitIntensity.Value, Plugin.ClubHitDuration.Value);
return true;
case 31:
settings = new HitSettings("jumbo-burger-collision", "Jumbo Burger giant collision", Plugin.JumboBurgerHitMode.Value, Plugin.ClubHitIntensity.Value, Plugin.ClubHitDuration.Value);
return true;
case 27:
settings = new HitSettings("traffic-vehicle", "traffic vehicle hit", Plugin.TrafficVehicleHitMode.Value, Plugin.GolfCartIntensity.Value, Plugin.GolfCartDuration.Value);
return true;
default:
settings = default(HitSettings);
return false;
}
}
internal static bool TryGetInstantEliminationSettings(EliminationReason reason, out HitSettings settings)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0003: Invalid comparison between Unknown and I4
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_0008: Invalid comparison between Unknown and I4
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Invalid comparison between Unknown and I4
if ((int)reason != 24)
{
if ((int)reason != 32)
{
if ((int)reason == 34)
{
settings = GetRailgunSettings();
return true;
}
settings = default(HitSettings);
return false;
}
settings = GetThunderstormSettings();
return true;
}
settings = GetOrbitalLaserSettings();
return true;
}
internal static HitSettings GetOrbitalLaserSettings()
{
return new HitSettings("orbital-laser", "orbital laser hit", Plugin.OrbitalLaserHitMode.Value, Plugin.ExplosiveHitIntensity.Value, Plugin.ExplosiveHitDuration.Value);
}
internal static HitSettings GetThunderstormSettings()
{
return new HitSettings("thunderstorm", "thunderstorm hit", Plugin.ThunderstormHitMode.Value, Plugin.ExplosiveHitIntensity.Value, Plugin.ExplosiveHitDuration.Value);
}
internal static HitSettings GetRailgunSettings()
{
return new HitSettings("railgun", "railgun hit", Plugin.RailgunHitMode.Value, Plugin.WeaponHitIntensity.Value, Plugin.WeaponHitDuration.Value);
}
internal static void LogSkipped(string reason, string explanation)
{
NotificationService.Publish("Ignored " + reason + ": " + explanation + ".", Plugin.ShowSkippedNotifications.Value, "Shock skipped: " + explanation + ".");
}
}
internal readonly struct HitSettings
{
internal string EventKey { get; }
internal string Reason { get; }
internal Plugin.HitTriggerMode Mode { get; }
internal int Intensity { get; }
internal int Duration { get; }
internal HitSettings(string eventKey, string reason, Plugin.HitTriggerMode mode, int intensity, int duration)
{
EventKey = eventKey;
Reason = reason;
Mode = mode;
Intensity = intensity;
Duration = duration;
}
}
internal sealed class ShockRequestQueue : IDisposable
{
private const int MaximumPendingRequests = 1;
private readonly CancellationTokenSource _cancellation = new CancellationTokenSource();
private readonly object _gate = new object();
private readonly SemaphoreSlim _signal = new SemaphoreSlim(0);
private readonly ConcurrentQueue<Func<CancellationToken, Task>> _tasks = new ConcurrentQueue<Func<CancellationToken, Task>>();
private readonly Task _worker;
private int _pendingCount;
private bool _stopping;
internal ShockRequestQueue()
{
_worker = Task.Run((Func<Task?>)ProcessQueueAsync);
}
internal bool TryEnqueue(Func<CancellationToken, Task> task)
{
lock (_gate)
{
if (_stopping || _pendingCount >= 1)
{
return false;
}
_tasks.Enqueue(task);
_pendingCount++;
_signal.Release();
return true;
}
}
private async Task ProcessQueueAsync()
{
_ = 1;
try
{
while (true)
{
await _signal.WaitAsync(_cancellation.Token);
if (!_tasks.TryDequeue(out Func<CancellationToken, Task> result))
{
continue;
}
try
{
await result(_cancellation.Token);
}
catch (OperationCanceledException) when (_cancellation.IsCancellationRequested)
{
break;
}
catch (Exception ex2)
{
NotificationService.PublishError("Queued shock request failed: " + ex2.Message);
Plugin.Log.LogError((object)ex2);
}
finally
{
lock (_gate)
{
_pendingCount--;
}
}
}
}
catch (OperationCanceledException) when (_cancellation.IsCancellationRequested)
{
}
}
public void Dispose()
{
lock (_gate)
{
if (_stopping)
{
return;
}
_stopping = true;
_cancellation.Cancel();
}
try
{
_worker.GetAwaiter().GetResult();
}
catch (OperationCanceledException)
{
}
finally
{
_cancellation.Dispose();
_signal.Dispose();
Func<CancellationToken, Task> result;
while (_tasks.TryDequeue(out result))
{
}
}
}
}
}
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class ConstantExpectedAttribute : Attribute
{
public object? Min { get; set; }
public object? Max { get; set; }
}
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class ExperimentalAttribute : Attribute
{
public string DiagnosticId { get; }
public string? UrlFormat { get; set; }
public ExperimentalAttribute(string diagnosticId)
{
DiagnosticId = diagnosticId;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
[ExcludeFromCodeCoverage]
internal sealed class MemberNotNullAttribute : Attribute
{
public string[] Members { get; }
public MemberNotNullAttribute(string member)
{
Members = new string[1] { member };
}
public MemberNotNullAttribute(params string[] members)
{
Members = members;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
[ExcludeFromCodeCoverage]
internal sealed class MemberNotNullWhenAttribute : Attribute
{
public bool ReturnValue { get; }
public string[] Members { get; }
public MemberNotNullWhenAttribute(bool returnValue, string member)
{
ReturnValue = returnValue;
Members = new string[1] { member };
}
public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
{
ReturnValue = returnValue;
Members = members;
}
}
[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class SetsRequiredMembersAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class StringSyntaxAttribute : Attribute
{
public const string CompositeFormat = "CompositeFormat";
public const string DateOnlyFormat = "DateOnlyFormat";
public const string DateTimeFormat = "DateTimeFormat";
public const string EnumFormat = "EnumFormat";
public const string GuidFormat = "GuidFormat";
public const string Json = "Json";
public const string NumericFormat = "NumericFormat";
public const string Regex = "Regex";
public const string TimeOnlyFormat = "TimeOnlyFormat";
public const string TimeSpanFormat = "TimeSpanFormat";
public const string Uri = "Uri";
public const string Xml = "Xml";
public string Syntax { get; }
public object?[] Arguments { get; }
public StringSyntaxAttribute(string syntax)
{
Syntax = syntax;
Arguments = new object[0];
}
public StringSyntaxAttribute(string syntax, params object?[] arguments)
{
Syntax = syntax;
Arguments = arguments;
}
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class UnscopedRefAttribute : Attribute
{
}
}
namespace System.Runtime.Versioning
{
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class RequiresPreviewFeaturesAttribute : Attribute
{
public string? Message { get; }
public string? Url { get; set; }
public RequiresPreviewFeaturesAttribute()
{
}
public RequiresPreviewFeaturesAttribute(string? message)
{
Message = message;
}
}
}
namespace System.Runtime.CompilerServices
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
internal sealed class IgnoresAccessChecksToAttribute : Attribute
{
public IgnoresAccessChecksToAttribute(string assemblyName)
{
}
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class CallerArgumentExpressionAttribute : Attribute
{
public string ParameterName { get; }
public CallerArgumentExpressionAttribute(string parameterName)
{
ParameterName = parameterName;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class CollectionBuilderAttribute : Attribute
{
public Type BuilderType { get; }
public string MethodName { get; }
public CollectionBuilderAttribute(Type builderType, string methodName)
{
BuilderType = builderType;
MethodName = methodName;
}
}
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class CompilerFeatureRequiredAttribute : Attribute
{
public const string RefStructs = "RefStructs";
public const string RequiredMembers = "RequiredMembers";
public string FeatureName { get; }
public bool IsOptional { get; set; }
public CompilerFeatureRequiredAttribute(string featureName)
{
FeatureName = featureName;
}
}
[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class InterpolatedStringHandlerArgumentAttribute : Attribute
{
public string[] Arguments { get; }
public InterpolatedStringHandlerArgumentAttribute(string argument)
{
Arguments = new string[1] { argument };
}
public InterpolatedStringHandlerArgumentAttribute(params string[] arguments)
{
Arguments = arguments;
}
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class InterpolatedStringHandlerAttribute : Attribute
{
}
[EditorBrowsable(EditorBrowsableState.Never)]
[ExcludeFromCodeCoverage]
internal static class IsExternalInit
{
}
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class ModuleInitializerAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class OverloadResolutionPriorityAttribute : Attribute
{
public int Priority { get; }
public OverloadResolutionPriorityAttribute(int priority)
{
Priority = priority;
}
}
[AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)]
[ExcludeFromCodeCoverage]
internal sealed class ParamCollectionAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class RequiredMemberAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
[EditorBrowsable(EditorBrowsableState.Never)]
[ExcludeFromCodeCoverage]
internal sealed class RequiresLocationAttribute : Attribute
{
}
[AttributeUsage(AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Interface, Inherited = false)]
[ExcludeFromCodeCoverage]
internal sealed class SkipLocalsInitAttribute : Attribute
{
}
}