From f9c083926d5feb318b8222df32b7fdaf588b640f Mon Sep 17 00:00:00 2001
From: sky <99112969+skyyt15@users.noreply.github.com>
Date: Tue, 6 Aug 2024 21:15:15 +0200
Subject: [PATCH 01/39] Update README.md to match with new repo (#41)
* Update README.md to match with new repo
* Update .github/README.md
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Update .github/README.md
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Update .github/README.md
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Update .github/README.md
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Update .github/README.md
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Update .github/README.md
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Update .github/README.md
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Update .github/README.md
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Update .github/README.md
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Update .github/README.md
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Update .github/README.md
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Update .github/README.md
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Update .github/README.md
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Update .github/README.md
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Fix yamato skill issue
---------
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
Co-authored-by: Misaka-ZeroTwo <45165615+Misaka-ZeroTwo@users.noreply.github.com>
---
.github/README.md | 34 +++++++++++++++++-----------------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/.github/README.md b/.github/README.md
index 4da34bd0b..b901da96d 100644
--- a/.github/README.md
+++ b/.github/README.md
@@ -10,10 +10,10 @@
-[![Version](https://img.shields.io/github/v/release/Exiled-Official/EXILED?sort=semver&style=flat-square&color=8DBBE9&label=Version)]()
+[![Version](https://img.shields.io/github/v/release/ExMod-Team/EXILED?sort=semver&style=flat-square&color=8DBBE9&label=Version)]()
[![License](https://img.shields.io/badge/License-CC%20BY%E2%80%93SA%203.0-df967f?style=flat-square)]()
-[![Contributors](https://img.shields.io/github/contributors-anon/Exiled-Official/EXILED?color=90E59A&style=flat-square&label=Contributors)]()
-[![GitHub Issues](https://img.shields.io/github/issues/Exiled-Official/EXILED.svg?style=flat-square&label=Issues&color=d77982)](https://github.com/Exiled-Official/EXILED/issues)
+[![Contributors](https://img.shields.io/github/contributors-anon/ExMod-Team/EXILED?color=90E59A&style=flat-square&label=Contributors)]()
+[![GitHub Issues](https://img.shields.io/github/issues/ExMod-Team/EXILED.svg?style=flat-square&label=Issues&color=d77982)](https://github.com/ExMod-Team/EXILED/issues)
[![Discord](https://img.shields.io/discord/656673194693885975?color=738adb&label=Discord&logo=discord&logoColor=white&style=flat-square)](https://discord.gg/PyUkWTg)
@@ -30,85 +30,85 @@ Localized READMEs
From a7354b70d57cc9eb4a6e86e40502b66658e1c300 Mon Sep 17 00:00:00 2001
From: x3rt
Date: Tue, 6 Aug 2024 14:59:04 -0600
Subject: [PATCH 02/39] Offline mode support (#19)
* Fix Offline-mode breaking everything
* Add `offline` authentication type and append `@offline` to UserIds during offline mode
* Add offline id support to Player.Get
* Comment transpilers
---------
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
---
EXILED/Exiled.API/Enums/AuthenticationType.cs | 5 +
.../Exiled.API/Extensions/StringExtensions.cs | 6 +-
EXILED/Exiled.API/Features/Player.cs | 3 +-
.../Patches/Events/Player/Verified.cs | 56 +++++-
.../Patches/Generic/OfflineModeIds.cs | 164 ++++++++++++++++++
5 files changed, 226 insertions(+), 8 deletions(-)
create mode 100644 EXILED/Exiled.Events/Patches/Generic/OfflineModeIds.cs
diff --git a/EXILED/Exiled.API/Enums/AuthenticationType.cs b/EXILED/Exiled.API/Enums/AuthenticationType.cs
index 9de49f902..0590ea097 100644
--- a/EXILED/Exiled.API/Enums/AuthenticationType.cs
+++ b/EXILED/Exiled.API/Enums/AuthenticationType.cs
@@ -42,5 +42,10 @@ public enum AuthenticationType
/// Indicates that the player has been authenticated as DedicatedServer.
///
DedicatedServer,
+
+ ///
+ /// Indicates that the player has been authenticated during Offline mode.
+ ///
+ Offline,
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Extensions/StringExtensions.cs b/EXILED/Exiled.API/Extensions/StringExtensions.cs
index 69b184bc6..37d1a6777 100644
--- a/EXILED/Exiled.API/Extensions/StringExtensions.cs
+++ b/EXILED/Exiled.API/Extensions/StringExtensions.cs
@@ -161,7 +161,11 @@ public static string GetBefore(this string input, char symbol)
///
/// The user id.
/// Returns the raw user id.
- public static string GetRawUserId(this string userId) => userId.Substring(0, userId.LastIndexOf('@'));
+ public static string GetRawUserId(this string userId)
+ {
+ int index = userId.IndexOf('@');
+ return index == -1 ? userId : userId.Substring(0, index);
+ }
///
/// Gets a SHA256 hash of a player's user id without the authentication.
diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs
index 9172aab3e..f7403cdb6 100644
--- a/EXILED/Exiled.API/Features/Player.cs
+++ b/EXILED/Exiled.API/Features/Player.cs
@@ -292,6 +292,7 @@ public AuthenticationType AuthenticationType
"northwood" => AuthenticationType.Northwood,
"localhost" => AuthenticationType.LocalHost,
"ID_Dedicated" => AuthenticationType.DedicatedServer,
+ "offline" => AuthenticationType.Offline,
_ => AuthenticationType.Unknown,
};
}
@@ -1310,7 +1311,7 @@ public static Player Get(string args)
if (int.TryParse(args, out int id))
return Get(id);
- if (args.EndsWith("@steam") || args.EndsWith("@discord") || args.EndsWith("@northwood"))
+ if (args.EndsWith("@steam") || args.EndsWith("@discord") || args.EndsWith("@northwood") || args.EndsWith("@offline"))
{
foreach (Player player in Dictionary.Values)
{
diff --git a/EXILED/Exiled.Events/Patches/Events/Player/Verified.cs b/EXILED/Exiled.Events/Patches/Events/Player/Verified.cs
index 585457a43..ce6697b5d 100644
--- a/EXILED/Exiled.Events/Patches/Events/Player/Verified.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Player/Verified.cs
@@ -7,16 +7,20 @@
namespace Exiled.Events.Patches.Events.Player
{
+#pragma warning disable SA1402 // File may only contain a single type
+#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
using System;
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
using API.Features;
+ using API.Features.Pools;
using CentralAuth;
using Exiled.API.Extensions;
using Exiled.Events.EventArgs.Player;
-
using HarmonyLib;
-#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
+ using static HarmonyLib.AccessTools;
///
/// Patches .
@@ -25,12 +29,16 @@ namespace Exiled.Events.Patches.Events.Player
[HarmonyPatch(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.FinalizeAuthentication))]
internal static class Verified
{
- private static void Postfix(PlayerAuthenticationManager __instance)
+ ///
+ /// Called after the player has been verified.
+ ///
+ /// The player's hub.
+ internal static void PlayerVerified(ReferenceHub hub)
{
- if (!Player.UnverifiedPlayers.TryGetValue(__instance._hub.gameObject, out Player player))
- Joined.CallEvent(__instance._hub, out player);
+ if (!Player.UnverifiedPlayers.TryGetValue(hub.gameObject, out Player player))
+ Joined.CallEvent(hub, out player);
- Player.Dictionary.Add(__instance._hub.gameObject, player);
+ Player.Dictionary.Add(hub.gameObject, player);
player.IsVerified = true;
player.RawUserId = player.UserId.GetRawUserId();
@@ -39,5 +47,41 @@ private static void Postfix(PlayerAuthenticationManager __instance)
Handlers.Player.OnVerified(new VerifiedEventArgs(player));
}
+
+ private static void Postfix(PlayerAuthenticationManager __instance)
+ {
+ PlayerVerified(__instance._hub);
+ }
+ }
+
+ ///
+ /// Patches .
+ /// Adds the event during offline mode.
+ ///
+ [HarmonyPatch(typeof(NicknameSync), nameof(NicknameSync.UserCode_CmdSetNick__String))]
+ internal static class VerifiedOfflineMode
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ const int offset = 1;
+ int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Callvirt && x.OperandIs(Method(typeof(CharacterClassManager), nameof(CharacterClassManager.SyncServerCmdBinding)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // Verified.PlayerVerified(this._hub);
+ new CodeInstruction(OpCodes.Ldarg_0),
+ new CodeInstruction(OpCodes.Ldfld, Field(typeof(NicknameSync), nameof(NicknameSync._hub))),
+ new CodeInstruction(OpCodes.Call, Method(typeof(Verified), nameof(Verified.PlayerVerified))),
+ });
+
+ for (int i = 0; i < newInstructions.Count; i++)
+ yield return newInstructions[i];
+
+ ListPool.Pool.Return(newInstructions);
+ }
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Generic/OfflineModeIds.cs b/EXILED/Exiled.Events/Patches/Generic/OfflineModeIds.cs
new file mode 100644
index 000000000..46c412dc3
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Generic/OfflineModeIds.cs
@@ -0,0 +1,164 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Generic
+{
+#pragma warning disable SA1402 // File may only contain a single type
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using API.Features.Pools;
+ using CentralAuth;
+ using HarmonyLib;
+ using PluginAPI.Core.Interfaces;
+ using PluginAPI.Events;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches to add an @offline suffix to UserIds in Offline Mode.
+ ///
+ [HarmonyPatch(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.Start))]
+ internal static class OfflineModeIds
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ const int offset = -1;
+ int index = newInstructions.FindLastIndex(instruction => instruction.opcode == OpCodes.Call && instruction.OperandIs(PropertySetter(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.UserId)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ new CodeInstruction(OpCodes.Call, Method(typeof(OfflineModeIds), nameof(BuildUserId))),
+ });
+
+ for (int i = 0; i < newInstructions.Count; i++)
+ yield return newInstructions[i];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+
+ private static string BuildUserId(string userId) => $"{userId}@offline";
+ }
+
+ ///
+ /// Patches to add the player's UserId to the dictionary.
+ ///
+ [HarmonyPatch(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.Start))]
+ internal static class OfflineModePlayerIds
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ Label skipLabel = generator.DefineLabel();
+
+ const int offset = 1;
+ int index = newInstructions.FindLastIndex(instruction => instruction.opcode == OpCodes.Call && instruction.OperandIs(PropertySetter(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.UserId)))) + offset;
+
+ // if (!Player.PlayersUserIds.ContainsKey(this.UserId))
+ // Player.PlayersUserIds.Add(this.UserId, this._hub);
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // if (Player.PlayersUserIds.ContainsKey(this.UserId)) goto skip;
+ new(OpCodes.Ldsfld, Field(typeof(PluginAPI.Core.Player), nameof(PluginAPI.Core.Player.PlayersUserIds))),
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Call, PropertyGetter(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.UserId))),
+ new(OpCodes.Callvirt, Method(typeof(Dictionary), nameof(Dictionary.ContainsKey))),
+ new(OpCodes.Brtrue_S, skipLabel),
+
+ // Player.PlayersUserIds.Add(this.UserId, this._hub);
+ new(OpCodes.Ldsfld, Field(typeof(PluginAPI.Core.Player), nameof(PluginAPI.Core.Player.PlayersUserIds))),
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Call, PropertyGetter(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.UserId))),
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Ldfld, Field(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager._hub))),
+ new(OpCodes.Callvirt, Method(typeof(Dictionary), nameof(Dictionary.Add))),
+
+ // skip:
+ new CodeInstruction(OpCodes.Nop).WithLabels(skipLabel),
+ });
+
+ for (int i = 0; i < newInstructions.Count; i++)
+ yield return newInstructions[i];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+
+ ///
+ /// Patches to prevent it from executing the event when the server is in offline mode.
+ ///
+ [HarmonyPatch(typeof(ReferenceHub), nameof(ReferenceHub.Start))]
+ internal static class OfflineModeReferenceHub
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ const int offset = 1;
+ int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Callvirt) + offset;
+
+ Label returnLabel = generator.DefineLabel();
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ new CodeInstruction(OpCodes.Br_S, returnLabel),
+ });
+
+ newInstructions[newInstructions.Count - 1].WithLabels(returnLabel);
+
+ for (int i = 0; i < newInstructions.Count; i++)
+ yield return newInstructions[i];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+
+ ///
+ /// Patches to execute the event when the server is in offline mode.
+ ///
+ [HarmonyPatch(typeof(NicknameSync), nameof(NicknameSync.UserCode_CmdSetNick__String))]
+ internal static class OfflineModeJoin
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ const int offset = 1;
+ int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Callvirt && x.OperandIs(Method(typeof(CharacterClassManager), nameof(CharacterClassManager.SyncServerCmdBinding)))) + offset;
+
+ // EventManager.ExecuteEvent(new PlayerJoinedEvent(this._hub));
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // EventManager.ExecuteEvent(new PlayerJoinedEvent(this._hub));
+ new CodeInstruction(OpCodes.Ldarg_0),
+ new CodeInstruction(OpCodes.Ldfld, Field(typeof(NicknameSync), nameof(NicknameSync._hub))),
+ new CodeInstruction(OpCodes.Call, Method(typeof(OfflineModeJoin), nameof(ExecuteNwEvent))),
+ });
+
+ for (int i = 0; i < newInstructions.Count; i++)
+ yield return newInstructions[i];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+
+ private static void ExecuteNwEvent(ReferenceHub hub)
+ {
+ EventManager.ExecuteEvent(new PlayerJoinedEvent(hub));
+ }
+ }
+}
\ No newline at end of file
From 259cabb149861216517fde655fed91095e99f951 Mon Sep 17 00:00:00 2001
From: sky <99112969+skyyt15@users.noreply.github.com>
Date: Tue, 6 Aug 2024 23:00:44 +0200
Subject: [PATCH 03/39] Fix NW bugs (#32)
* Fix Armor Drop
from https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/230
* add comments
* fix scp173 and adding bug report link to summary class
fix https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/143
* Moved patches and fixed Scp173FirstKillPatch
* Add Slowness Fix
Avoid values more than 100 for effect slowness to fix https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/378
* skill issue
* skill issue (again=
---------
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
---
.../Patches/Fixes/ArmorDropPatch.cs | 55 +++++++++++++++++++
.../Patches/Fixes/Scp173FirstKillPatch.cs | 55 +++++++++++++++++++
.../Patches/Fixes/Scp173SecondKillPatch.cs | 54 ++++++++++++++++++
.../Patches/Fixes/SlownessFix.cs | 2 +-
4 files changed, 165 insertions(+), 1 deletion(-)
create mode 100644 EXILED/Exiled.Events/Patches/Fixes/ArmorDropPatch.cs
create mode 100644 EXILED/Exiled.Events/Patches/Fixes/Scp173FirstKillPatch.cs
create mode 100644 EXILED/Exiled.Events/Patches/Fixes/Scp173SecondKillPatch.cs
diff --git a/EXILED/Exiled.Events/Patches/Fixes/ArmorDropPatch.cs b/EXILED/Exiled.Events/Patches/Fixes/ArmorDropPatch.cs
new file mode 100644
index 000000000..a924f6808
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Fixes/ArmorDropPatch.cs
@@ -0,0 +1,55 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Fixes
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using Exiled.API.Features.Pools;
+ using HarmonyLib;
+ using InventorySystem;
+ using InventorySystem.Items;
+ using InventorySystem.Items.Armor;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches to fix https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/230 bug.
+ ///
+ [HarmonyPatch(typeof(BodyArmorUtils), nameof(BodyArmorUtils.RemoveEverythingExceedingLimits))]
+ internal static class ArmorDropPatch
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ Label continueLabel = generator.DefineLabel();
+ int continueIndex = newInstructions.FindIndex(x => x.Is(OpCodes.Call, Method(typeof(Dictionary.Enumerator), nameof(Dictionary.Enumerator.MoveNext)))) - 1;
+ newInstructions[continueIndex].WithLabels(continueLabel);
+
+ // before: if (keyValuePair.Value.Category != ItemCategory.Armor)
+ // after: if (keyValuePair.Value.Category != ItemCategory.Armor && keyValuePair.Value.Category != ItemCategory.None)
+ int index = newInstructions.FindIndex(x => x.Is(OpCodes.Ldc_I4_S, 9));
+
+ newInstructions.InsertRange(index, new CodeInstruction[]
+ {
+ // && keyValuePair.Value.Category != ItemCategory.None)
+ new(OpCodes.Ldloca_S, 4),
+ new(OpCodes.Call, PropertyGetter(typeof(KeyValuePair), nameof(KeyValuePair.Value))),
+ new(OpCodes.Ldfld, Field(typeof(ItemBase), nameof(ItemBase.Category))),
+ new(OpCodes.Ldc_I4_0),
+ new(OpCodes.Beq_S, continueLabel),
+ });
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Fixes/Scp173FirstKillPatch.cs b/EXILED/Exiled.Events/Patches/Fixes/Scp173FirstKillPatch.cs
new file mode 100644
index 000000000..f66d35058
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Fixes/Scp173FirstKillPatch.cs
@@ -0,0 +1,55 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Fixes
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using API.Features.Pools;
+ using CustomPlayerEffects;
+ using HarmonyLib;
+ using PlayerRoles.PlayableScps.Scp173;
+ using UnityEngine;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches to fix https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/143 bug.
+ ///
+ [HarmonyPatch(typeof(Scp173SnapAbility), nameof(Scp173SnapAbility.TryHitTarget))]
+ internal static class Scp173FirstKillPatch
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ Label continueLabel = generator.DefineLabel();
+
+ int index = newInstructions.FindLastIndex(x => x.opcode == OpCodes.Ldc_I4_0);
+ newInstructions[index].WithLabels(continueLabel);
+
+ newInstructions.InsertRange(index, new CodeInstruction[]
+ {
+ // if (hitboxIdentity.TargetHub.playerEffectController.GetEffect().IsEnabled) return false;
+ new(OpCodes.Ldloc_2),
+ new(OpCodes.Callvirt, PropertyGetter(typeof(HitboxIdentity), nameof(HitboxIdentity.TargetHub))),
+ new(OpCodes.Ldfld, Field(typeof(ReferenceHub), nameof(ReferenceHub.playerEffectsController))),
+ new(OpCodes.Callvirt, Method(typeof(PlayerEffectsController), nameof(PlayerEffectsController.GetEffect), generics: new[] { typeof(SpawnProtected) })),
+ new(OpCodes.Callvirt, PropertyGetter(typeof(StatusEffectBase), nameof(StatusEffectBase.IsEnabled))),
+ new(OpCodes.Brfalse_S, continueLabel),
+ new(OpCodes.Ldc_I4_0),
+ new(OpCodes.Ret),
+ });
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
diff --git a/EXILED/Exiled.Events/Patches/Fixes/Scp173SecondKillPatch.cs b/EXILED/Exiled.Events/Patches/Fixes/Scp173SecondKillPatch.cs
new file mode 100644
index 000000000..2b6dcb028
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Fixes/Scp173SecondKillPatch.cs
@@ -0,0 +1,54 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Fixes
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using API.Features.Pools;
+ using CustomPlayerEffects;
+ using HarmonyLib;
+ using Mirror;
+ using PlayerRoles.PlayableScps.Scp173;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches to fix https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/143 bug.
+ ///
+ [HarmonyPatch(typeof(Scp173TeleportAbility), nameof(Scp173TeleportAbility.ServerProcessCmd))]
+ internal static class Scp173SecondKillPatch
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ Label returnLabel = generator.DefineLabel();
+
+ newInstructions[newInstructions.Count - 1].WithLabels(returnLabel);
+
+ int offset = -5;
+ int index = newInstructions.FindIndex(x => x.Is(OpCodes.Callvirt, Method(typeof(MovementTracer), nameof(MovementTracer.GenerateBounds)))) + offset;
+
+ newInstructions.InsertRange(index, new[]
+ {
+ // if (hub.playerEffectController.GetEffect().IsEnabled) return;
+ new CodeInstruction(OpCodes.Ldloc_S, 5).MoveLabelsFrom(newInstructions[index]),
+ new(OpCodes.Ldfld, Field(typeof(ReferenceHub), nameof(ReferenceHub.playerEffectsController))),
+ new(OpCodes.Callvirt, Method(typeof(PlayerEffectsController), nameof(PlayerEffectsController.GetEffect), generics: new[] { typeof(SpawnProtected) })),
+ new(OpCodes.Callvirt, PropertyGetter(typeof(StatusEffectBase), nameof(StatusEffectBase.IsEnabled))),
+ new(OpCodes.Brtrue_S, returnLabel),
+ });
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
diff --git a/EXILED/Exiled.Events/Patches/Fixes/SlownessFix.cs b/EXILED/Exiled.Events/Patches/Fixes/SlownessFix.cs
index e2e69b756..5a05c31a0 100644
--- a/EXILED/Exiled.Events/Patches/Fixes/SlownessFix.cs
+++ b/EXILED/Exiled.Events/Patches/Fixes/SlownessFix.cs
@@ -62,4 +62,4 @@ private static IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions);
}
}
-}
+}
\ No newline at end of file
From 26d1fcc0d791fd036d4e28fdeb1f63235aeb26ec Mon Sep 17 00:00:00 2001
From: x3rt
Date: Wed, 7 Aug 2024 01:54:44 -0600
Subject: [PATCH 04/39] Useless Event (939 Placed Amnestic Cloud) (#40)
---
.../Scp939/PlacedAmnesticCloudEventArgs.cs | 51 +++++++++++++
EXILED/Exiled.Events/Handlers/Scp939.cs | 11 +++
.../Events/Scp939/PlacedAmnesticCloud.cs | 73 +++++++++++++++++++
3 files changed, 135 insertions(+)
create mode 100644 EXILED/Exiled.Events/EventArgs/Scp939/PlacedAmnesticCloudEventArgs.cs
create mode 100644 EXILED/Exiled.Events/Patches/Events/Scp939/PlacedAmnesticCloud.cs
diff --git a/EXILED/Exiled.Events/EventArgs/Scp939/PlacedAmnesticCloudEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp939/PlacedAmnesticCloudEventArgs.cs
new file mode 100644
index 000000000..107a7c869
--- /dev/null
+++ b/EXILED/Exiled.Events/EventArgs/Scp939/PlacedAmnesticCloudEventArgs.cs
@@ -0,0 +1,51 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.EventArgs.Scp939
+{
+ using API.Features;
+ using API.Features.Hazards;
+ using Interfaces;
+ using PlayerRoles.PlayableScps.Scp939;
+
+ using Scp939Role = API.Features.Roles.Scp939Role;
+
+ ///
+ /// Contains all information after SCP-939 used its amnestic cloud ability.
+ ///
+ public class PlacedAmnesticCloudEventArgs : IScp939Event
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public PlacedAmnesticCloudEventArgs(ReferenceHub hub, Scp939AmnesticCloudInstance cloud)
+ {
+ Player = Player.Get(hub);
+ AmnesticCloud = new AmnesticCloudHazard(cloud);
+ Scp939 = Player.Role.As();
+ }
+
+ ///
+ /// Gets the player who's controlling SCP-939.
+ ///
+ public Player Player { get; }
+
+ ///
+ /// Gets the instance.
+ ///
+ public AmnesticCloudHazard AmnesticCloud { get; }
+
+ ///
+ public Scp939Role Scp939 { get; }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Handlers/Scp939.cs b/EXILED/Exiled.Events/Handlers/Scp939.cs
index 14d1245c6..e82e94a80 100644
--- a/EXILED/Exiled.Events/Handlers/Scp939.cs
+++ b/EXILED/Exiled.Events/Handlers/Scp939.cs
@@ -32,6 +32,11 @@ public static class Scp939
///
public static Event PlacingAmnesticCloud { get; set; } = new();
+ ///
+ /// Invoked after SCP-939 used its amnestic cloud ability.
+ ///
+ public static Event PlacedAmnesticCloud { get; set; } = new();
+
///
/// Invoked before SCP-939 plays a stolen voice.
///
@@ -81,6 +86,12 @@ public static class Scp939
/// The instance.
public static void OnPlacingAmnesticCloud(PlacingAmnesticCloudEventArgs ev) => PlacingAmnesticCloud.InvokeSafely(ev);
+ ///
+ /// Called after SCP-939 used its amnestic cloud ability.
+ ///
+ /// The instance.
+ public static void OnPlacedAmnesticCloud(PlacedAmnesticCloudEventArgs ev) => PlacedAmnesticCloud.InvokeSafely(ev);
+
///
/// Called before SCP-939 plays a stolen voice.
///
diff --git a/EXILED/Exiled.Events/Patches/Events/Scp939/PlacedAmnesticCloud.cs b/EXILED/Exiled.Events/Patches/Events/Scp939/PlacedAmnesticCloud.cs
new file mode 100644
index 000000000..ac224990f
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Events/Scp939/PlacedAmnesticCloud.cs
@@ -0,0 +1,73 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Events.Scp939
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using Exiled.API.Features.Pools;
+ using Exiled.Events.Attributes;
+ using Exiled.Events.EventArgs.Scp939;
+ using Exiled.Events.Handlers;
+ using HarmonyLib;
+ using PlayerRoles.PlayableScps.Scp939;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches
+ /// to add the event.
+ ///
+ [EventPatch(typeof(Scp939), nameof(Scp939.PlacedAmnesticCloud))]
+ [HarmonyPatch(typeof(Scp939AmnesticCloudAbility), nameof(Scp939AmnesticCloudAbility.OnStateEnabled))]
+ internal static class PlacedAmnesticCloud
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ LocalBuilder cloud = generator.DeclareLocal(typeof(Scp939AmnesticCloudInstance));
+
+ const int offset = -2;
+ int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Callvirt && x.OperandIs(Method(typeof(Scp939AmnesticCloudInstance), nameof(Scp939AmnesticCloudInstance.ServerSetup)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // Scp939AmnesticCloudInstance cloud = Object.Instantiate(this._instancePrefab)
+ new CodeInstruction(OpCodes.Dup),
+ new CodeInstruction(OpCodes.Stloc_S, cloud),
+ });
+
+ index = newInstructions.Count - 1;
+
+ // Scp939.OnPlacedAmnesticCloud(new PlacedAmnesticCloudEventArgs(this.Owner, cloud));
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // this.Owner
+ new CodeInstruction(OpCodes.Ldarg_0),
+ new(OpCodes.Callvirt, PropertyGetter(typeof(Scp939AmnesticCloudAbility), nameof(Scp939AmnesticCloudAbility.Owner))),
+
+ // cloud
+ new CodeInstruction(OpCodes.Ldloc_S, cloud),
+
+ // Scp939.OnPlacedAmnesticCloud(new PlacedAmnesticCloudEventArgs(this.Owner, cloud));
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(PlacedAmnesticCloudEventArgs))[0]),
+ new(OpCodes.Call, Method(typeof(Scp939), nameof(Scp939.OnPlacedAmnesticCloud))),
+ });
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
\ No newline at end of file
From 03d6bd7c06b55d132a6b58e8a3c3e6c0f4509dc4 Mon Sep 17 00:00:00 2001
From: TtroubleTT <121741230+TtroubleTT@users.noreply.github.com>
Date: Wed, 7 Aug 2024 13:23:59 -0700
Subject: [PATCH 05/39] Fix custom role classes giving custom ammo when they
are not suppose to (#24)
* Fix custom role classes giving custom ammmo when they are not suppose to
* change to using EnumUtils
* Moved Ammo additions into Inventory call delayed
---
.../API/Features/CustomRole.cs | 29 +++++++------------
1 file changed, 10 insertions(+), 19 deletions(-)
diff --git a/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs b/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs
index 51e4ac008..61f88ec2c 100644
--- a/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs
+++ b/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs
@@ -537,6 +537,16 @@ public virtual void AddRole(Player player)
Log.Debug($"{Name}: Adding {itemName} to inventory.");
TryAddItem(player, itemName);
}
+
+ if (Ammo.Count > 0)
+ {
+ Log.Debug($"{Name}: Adding Ammo to {player.Nickname} inventory.");
+ foreach (AmmoType type in EnumUtils.Values)
+ {
+ if (type != AmmoType.None)
+ player.SetAmmo(type, Ammo.ContainsKey(type) ? Ammo[type] == ushort.MaxValue ? InventoryLimits.GetAmmoLimit(type.GetItemType(), player.ReferenceHub) : Ammo[type] : (ushort)0);
+ }
+ }
});
Log.Debug($"{Name}: Setting health values.");
@@ -910,25 +920,6 @@ private void OnInternalChangingRole(ChangingRoleEventArgs ev)
{
RemoveRole(ev.Player);
}
- else if (Check(ev.Player))
- {
- Log.Debug($"{Name}: Checking ammo stuff {Ammo.Count}");
- if (Ammo.Count > 0)
- {
- Log.Debug($"{Name}: Clearing ammo");
- ev.Ammo.Clear();
- Timing.CallDelayed(
- 0.5f,
- () =>
- {
- foreach (AmmoType type in Enum.GetValues(typeof(AmmoType)))
- {
- if (type != AmmoType.None)
- ev.Player.SetAmmo(type, Ammo.ContainsKey(type) ? Ammo[type] == ushort.MaxValue ? InventoryLimits.GetAmmoLimit(type.GetItemType(), ev.Player.ReferenceHub) : Ammo[type] : (ushort)0);
- }
- });
- }
- }
}
private void OnSpawningRagdoll(SpawningRagdollEventArgs ev)
From 06cf2d7f8f4d61d1cc081cbcd2749ae310051f49 Mon Sep 17 00:00:00 2001
From: Yamato <66829532+louis1706@users.noreply.github.com>
Date: Fri, 9 Aug 2024 10:05:15 +0200
Subject: [PATCH 06/39] `Item::Get()` and `Pickup::Get()` (#17)
* `Item::::Get()` and `Pickup::Get()`
* Revert doc change
* Add `Hazard::Get()`
* doc fix
* `Door::Get()`
* AdminToy.Get()
* More implementation
* WeirdFix
* simplify Scp244Spawning patch and AddedComment & NO IL error
* Remove Log.info
* DroppingItem light modifiication
* Moving Item.Get inside the eventargs instead of transpiller
---
EXILED/Exiled.API/Features/Doors/Door.cs | 57 ++++++++++++++-----
EXILED/Exiled.API/Features/Hazards/Hazard.cs | 9 +++
.../Features/Items/ExplosiveGrenade.cs | 2 +-
.../Exiled.API/Features/Items/FlashGrenade.cs | 2 +-
EXILED/Exiled.API/Features/Items/Item.cs | 45 ++++++++++++++-
EXILED/Exiled.API/Features/Items/Scp018.cs | 2 +-
EXILED/Exiled.API/Features/Items/Scp2176.cs | 2 +-
EXILED/Exiled.API/Features/Items/Scp330.cs | 4 +-
EXILED/Exiled.API/Features/Items/Throwable.cs | 2 +-
EXILED/Exiled.API/Features/Lift.cs | 4 +-
EXILED/Exiled.API/Features/Map.cs | 2 +-
EXILED/Exiled.API/Features/Pickups/Pickup.cs | 52 +++++++++++++++++
.../Pickups/Projectiles/Projectile.cs | 43 ++++++++++++--
EXILED/Exiled.API/Features/Player.cs | 4 +-
EXILED/Exiled.API/Features/TeslaGate.cs | 2 +-
EXILED/Exiled.API/Features/Toys/AdminToy.cs | 9 +++
.../Patches/PlayerInventorySee.cs | 3 +-
.../Item/ChangingAttachmentsEventArgs.cs | 14 ++---
.../Map/ExplodingGrenadeEventArgs.cs | 4 +-
.../EventArgs/Map/Scp244SpawningEventArgs.cs | 13 +++--
.../Player/CancellingItemUseEventArgs.cs | 2 +-
.../Player/ChangingMicroHIDStateEventArgs.cs | 2 +-
.../Player/ChangingRadioPresetEventArgs.cs | 2 +-
.../EventArgs/Player/DroppedItemEventArgs.cs | 5 +-
.../Player/ThrowingRequestEventArgs.cs | 2 +-
.../Player/ThrownProjectileEventArgs.cs | 4 +-
.../Player/TogglingFlashlightEventArgs.cs | 2 +-
.../Player/TogglingRadioEventArgs.cs | 2 +-
.../Player/UsingMicroHIDEnergyEventArgs.cs | 2 +-
.../Player/UsingRadioBatteryEventArgs.cs | 2 +-
.../Scp096/StartPryingGateEventArgs.cs | 2 +-
.../EventArgs/Scp244/UsingScp244EventArgs.cs | 2 +-
.../Scp330/DroppingScp330EventArgs.cs | 2 +-
.../Events/Item/ChangingAttachments.cs | 7 +--
.../Patches/Events/Map/Scp244Spawning.cs | 39 ++++++-------
.../Patches/Events/Player/DroppingItem.cs | 7 +--
.../Events/Player/FirearmRequestReceived.cs | 3 +-
.../Patches/Fixes/GrenadePropertiesFix.cs | 3 +-
.../Exiled.Events/Patches/Generic/DoorList.cs | 2 +-
.../Patches/Generic/PickupControlPatch.cs | 7 ++-
40 files changed, 271 insertions(+), 103 deletions(-)
diff --git a/EXILED/Exiled.API/Features/Doors/Door.cs b/EXILED/Exiled.API/Features/Doors/Door.cs
index 230e05b0b..c06a13b9b 100644
--- a/EXILED/Exiled.API/Features/Doors/Door.cs
+++ b/EXILED/Exiled.API/Features/Doors/Door.cs
@@ -14,7 +14,9 @@ namespace Exiled.API.Features.Doors
using Exiled.API.Enums;
using Exiled.API.Extensions;
using Exiled.API.Features.Core;
+ using Exiled.API.Features.Hazards;
using Exiled.API.Interfaces;
+ using global::Hazards;
using Interactables.Interobjects;
using Interactables.Interobjects.DoorUtils;
using MEC;
@@ -309,6 +311,31 @@ public static Door Get(DoorVariant doorVariant)
return DoorVariantToDoor[doorVariant];
}
+ ///
+ /// Gets the by .
+ ///
+ /// The to convert into an door.
+ /// The specified type.
+ /// The door wrapper for the given .
+ public static T Get(DoorVariant doorVariant)
+ where T : Door => Get(doorVariant) as T;
+
+ ///
+ /// Gets a given the specified .
+ ///
+ /// The to search for.
+ /// The with the given or if not found.
+ public static Door Get(DoorType doorType) => List.FirstOrDefault(x => x.Type == doorType);
+
+ ///
+ /// Gets the by .
+ ///
+ /// The to convert into an door.
+ /// The specified type.
+ /// The door wrapper for the given .
+ public static T Get(DoorType doorType)
+ where T : Door => Get(doorType) as T;
+
///
/// Gets a given the specified name.
///
@@ -320,6 +347,15 @@ public static Door Get(string name)
return nameExtension is null ? null : Get(nameExtension.TargetDoor);
}
+ ///
+ /// Gets the by .
+ ///
+ /// The name to search for.
+ /// The specified type.
+ /// The door wrapper for the given .
+ public static T Get(string name)
+ where T : Door => Get(name) as T;
+
///
/// Gets the door object associated with a specific , or creates a new one if there isn't one.
///
@@ -327,20 +363,6 @@ public static Door Get(string name)
/// The with the given name or if not found.
public static Door Get(GameObject gameObject) => gameObject is null ? null : Get(gameObject.GetComponentInChildren());
- ///
- /// Gets a of filtered based on a predicate.
- ///
- /// The condition to satify.
- /// A of which contains elements that satify the condition.
- public static IEnumerable Get(Func predicate) => List.Where(predicate);
-
- ///
- /// Gets a given the specified .
- ///
- /// The to search for.
- /// The with the given or if not found.
- public static Door Get(DoorType doorType) => List.FirstOrDefault(x => x.Type == doorType);
-
///
/// Returns the closest to the given .
///
@@ -367,6 +389,13 @@ public static Door Random(ZoneType type = ZoneType.Unspecified, bool onlyUnbroke
return doors[UnityEngine.Random.Range(0, doors.Count)];
}
+ ///
+ /// Gets a of filtered based on a predicate.
+ ///
+ /// The condition to satify.
+ /// A of which contains elements that satify the condition.
+ public static IEnumerable Get(Func predicate) => List.Where(predicate);
+
///
/// Locks all doors given the specified .
///
diff --git a/EXILED/Exiled.API/Features/Hazards/Hazard.cs b/EXILED/Exiled.API/Features/Hazards/Hazard.cs
index 36695f6de..253825530 100644
--- a/EXILED/Exiled.API/Features/Hazards/Hazard.cs
+++ b/EXILED/Exiled.API/Features/Hazards/Hazard.cs
@@ -123,6 +123,15 @@ public static Hazard Get(EnvironmentalHazard environmentalHazard) =>
_ => new Hazard(environmentalHazard)
};
+ ///
+ /// Gets the by .
+ ///
+ /// The to convert into an hazard.
+ /// The specified type.
+ /// The hazard wrapper for the given .
+ public static T Get(EnvironmentalHazard environmentalHazard)
+ where T : Hazard => Get(environmentalHazard) as T;
+
///
/// Gets the hazard by the room where it's located.
///
diff --git a/EXILED/Exiled.API/Features/Items/ExplosiveGrenade.cs b/EXILED/Exiled.API/Features/Items/ExplosiveGrenade.cs
index 8f9aa165c..0002d5d76 100644
--- a/EXILED/Exiled.API/Features/Items/ExplosiveGrenade.cs
+++ b/EXILED/Exiled.API/Features/Items/ExplosiveGrenade.cs
@@ -119,7 +119,7 @@ public ExplosionGrenadeProjectile SpawnActive(Vector3 position, Player owner = n
ipb.Info = new PickupSyncInfo(Type, Weight, ItemSerialGenerator.GenerateNext());
- ExplosionGrenadeProjectile grenade = (ExplosionGrenadeProjectile)Pickup.Get(ipb);
+ ExplosionGrenadeProjectile grenade = Pickup.Get(ipb);
grenade.Base.gameObject.SetActive(true);
diff --git a/EXILED/Exiled.API/Features/Items/FlashGrenade.cs b/EXILED/Exiled.API/Features/Items/FlashGrenade.cs
index e2d009ac7..979784f2c 100644
--- a/EXILED/Exiled.API/Features/Items/FlashGrenade.cs
+++ b/EXILED/Exiled.API/Features/Items/FlashGrenade.cs
@@ -100,7 +100,7 @@ public FlashbangProjectile SpawnActive(Vector3 position, Player owner = null)
ipb.Info = new PickupSyncInfo(Type, Weight, ItemSerialGenerator.GenerateNext());
- FlashbangProjectile grenade = (FlashbangProjectile)Pickup.Get(ipb);
+ FlashbangProjectile grenade = Pickup.Get(ipb);
grenade.Base.gameObject.SetActive(true);
diff --git a/EXILED/Exiled.API/Features/Items/Item.cs b/EXILED/Exiled.API/Features/Items/Item.cs
index b38f4aa18..96d02257f 100644
--- a/EXILED/Exiled.API/Features/Items/Item.cs
+++ b/EXILED/Exiled.API/Features/Items/Item.cs
@@ -25,7 +25,6 @@ namespace Exiled.API.Features.Items
using InventorySystem.Items.Radio;
using InventorySystem.Items.ThrowableProjectiles;
using InventorySystem.Items.ToggleableLights;
- using InventorySystem.Items.ToggleableLights.Flashlight;
using InventorySystem.Items.Usables;
using InventorySystem.Items.Usables.Scp1576;
using InventorySystem.Items.Usables.Scp244;
@@ -33,7 +32,6 @@ namespace Exiled.API.Features.Items
using UnityEngine;
using BaseConsumable = InventorySystem.Items.Usables.Consumable;
- using Object = UnityEngine.Object;
///
/// A wrapper class for .
@@ -219,6 +217,15 @@ public static Item Get(ItemBase itemBase)
};
}
+ ///
+ /// Gets an existing or creates a new instance of one.
+ ///
+ /// The to convert into an item.
+ /// The specified type.
+ /// The item wrapper for the given .
+ public static T Get(ItemBase itemBase)
+ where T : Item => Get(itemBase) as T;
+
///
/// Gets the Item belonging to the specified serial.
///
@@ -279,6 +286,40 @@ ItemType.KeycardGuard or ItemType.KeycardJanitor or ItemType.KeycardO5 or ItemTy
_ => new Item(type),
};
+ ///
+ /// Creates a new with the proper inherited subclass.
+ ///
+ /// Based on the , the returned can be casted into a subclass to gain more control over the object.
+ ///
- Usable items (Adrenaline, Medkit, Painkillers, SCP-207, SCP-268, and SCP-500) should be casted to the class.
+ ///
- All valid ammo should be casted to the class.
+ ///
- All valid firearms (not including the Micro HID) should be casted to the class.
+ ///
- All valid keycards should be casted to the class.
+ ///
- All valid armor should be casted to the class.
+ ///
- Explosive grenades and SCP-018 should be casted to the class.
+ ///
- Flash grenades should be casted to the class.
+ ///
+ ///
+ ///
The following have their own respective classes:
+ ///
- Flashlights can be casted to .
+ ///
- Radios can be casted to .
+ ///
- The Micro HID can be casted to .
+ ///
- SCP-244 A and B variants can be casted to .
+ ///
- SCP-330 can be casted to .
+ ///
- SCP-2176 can be casted to the class.
+ ///
- SCP-1576 can be casted to the class.
+ ///
- Jailbird can be casted to the class.
+ ///
+ ///
+ /// Items that are not listed above do not have a subclass, and can only use the base class.
+ ///
+ ///
+ /// The of the item to create.
+ /// The who owns the item by default.
+ /// The specified type.
+ /// The created. This can be cast as a subclass.
+ public static Item Create(ItemType type, Player owner = null)
+ where T : Item => Create(type, owner) as T;
+
///
/// Gives this item to a .
///
diff --git a/EXILED/Exiled.API/Features/Items/Scp018.cs b/EXILED/Exiled.API/Features/Items/Scp018.cs
index 57f8122c1..7c08c7c6d 100644
--- a/EXILED/Exiled.API/Features/Items/Scp018.cs
+++ b/EXILED/Exiled.API/Features/Items/Scp018.cs
@@ -86,7 +86,7 @@ public Scp018Projectile SpawnActive(Vector3 position, Player owner = null)
ipb.Info = new PickupSyncInfo(Type, Weight, ItemSerialGenerator.GenerateNext());
- Scp018Projectile grenade = (Scp018Projectile)Pickup.Get(ipb);
+ Scp018Projectile grenade = Pickup.Get(ipb);
grenade.Base.gameObject.SetActive(true);
diff --git a/EXILED/Exiled.API/Features/Items/Scp2176.cs b/EXILED/Exiled.API/Features/Items/Scp2176.cs
index 412c371d6..0ca9fc54d 100644
--- a/EXILED/Exiled.API/Features/Items/Scp2176.cs
+++ b/EXILED/Exiled.API/Features/Items/Scp2176.cs
@@ -71,7 +71,7 @@ public Scp2176Projectile SpawnActive(Vector3 position, Player owner = null)
ipb.Info = new PickupSyncInfo(Type, Weight, ItemSerialGenerator.GenerateNext());
- Scp2176Projectile grenade = (Scp2176Projectile)Pickup.Get(ipb);
+ Scp2176Projectile grenade = Pickup.Get(ipb);
grenade.Base.gameObject.SetActive(true);
diff --git a/EXILED/Exiled.API/Features/Items/Scp330.cs b/EXILED/Exiled.API/Features/Items/Scp330.cs
index d0a7e3b9c..689929f12 100644
--- a/EXILED/Exiled.API/Features/Items/Scp330.cs
+++ b/EXILED/Exiled.API/Features/Items/Scp330.cs
@@ -197,7 +197,7 @@ public IEnumerable DropCandy(CandyKindID type, bool dropAll = fals
ipb.Info = new(Type, Weight, ItemSerialGenerator.GenerateNext());
- Scp330Pickup pickup = (Scp330Pickup)Pickup.Get(ipb);
+ Scp330Pickup pickup = Pickup.Get(ipb);
if (exposedType is not CandyKindID.None)
pickup.ExposedCandy = exposedType;
@@ -218,7 +218,7 @@ public IEnumerable DropCandy(CandyKindID type, bool dropAll = fals
ipb.Info = new(Type, Weight, ItemSerialGenerator.GenerateNext());
- Scp330Pickup pickup = (Scp330Pickup)Pickup.Get(ipb);
+ Scp330Pickup pickup = Pickup.Get(ipb);
if (exposedType is not CandyKindID.None)
pickup.ExposedCandy = exposedType;
diff --git a/EXILED/Exiled.API/Features/Items/Throwable.cs b/EXILED/Exiled.API/Features/Items/Throwable.cs
index 4946d1722..4de522295 100644
--- a/EXILED/Exiled.API/Features/Items/Throwable.cs
+++ b/EXILED/Exiled.API/Features/Items/Throwable.cs
@@ -29,7 +29,7 @@ public Throwable(ThrowableItem itemBase)
{
Base = itemBase;
Base.Projectile.gameObject.SetActive(false);
- Projectile = (Projectile)Pickup.Get(Object.Instantiate(Base.Projectile));
+ Projectile = Pickup.Get(Object.Instantiate(Base.Projectile));
Base.Projectile.gameObject.SetActive(true);
Projectile.Serial = Serial;
}
diff --git a/EXILED/Exiled.API/Features/Lift.cs b/EXILED/Exiled.API/Features/Lift.cs
index 5222ca985..9a3b183c2 100644
--- a/EXILED/Exiled.API/Features/Lift.cs
+++ b/EXILED/Exiled.API/Features/Lift.cs
@@ -76,7 +76,7 @@ internal Lift(ElevatorChamber elevator)
///
/// Gets a value of the internal doors list.
///
- public IReadOnlyCollection Doors => internalDoorsList.Select(x => Door.Get(x).As()).ToList();
+ public IReadOnlyCollection Doors => internalDoorsList.Select(x => Door.Get(x)).ToList();
///
/// Gets a of in the .
@@ -201,7 +201,7 @@ public float AnimationTime
///
/// Gets the .
///
- public Doors.ElevatorDoor CurrentDestination => Door.Get(Base.CurrentDestination).As();
+ public Doors.ElevatorDoor CurrentDestination => Door.Get(Base.CurrentDestination);
///
/// Gets a of which contains all the instances from the specified .
diff --git a/EXILED/Exiled.API/Features/Map.cs b/EXILED/Exiled.API/Features/Map.cs
index 24edcc90c..4e8f2b094 100644
--- a/EXILED/Exiled.API/Features/Map.cs
+++ b/EXILED/Exiled.API/Features/Map.cs
@@ -294,7 +294,7 @@ public static TantrumHazard PlaceTantrum(Vector3 position, bool isActive = true)
NetworkServer.Spawn(tantrum.gameObject);
- return Hazard.Get(tantrum).Cast();
+ return Hazard.Get(tantrum);
}
///
diff --git a/EXILED/Exiled.API/Features/Pickups/Pickup.cs b/EXILED/Exiled.API/Features/Pickups/Pickup.cs
index 70db62a8a..3f7373dee 100644
--- a/EXILED/Exiled.API/Features/Pickups/Pickup.cs
+++ b/EXILED/Exiled.API/Features/Pickups/Pickup.cs
@@ -338,6 +338,15 @@ public static Pickup Get(ItemPickupBase pickupBase)
};
}
+ ///
+ /// Gets an existing or creates a new instance of one.
+ ///
+ /// The to convert into an pickup.
+ /// The specified type.
+ /// The pickup wrapper for the given .
+ public static T Get(ItemPickupBase pickupBase)
+ where T : Pickup => Get(pickupBase) as T;
+
///
/// Gets the given a .
///
@@ -487,6 +496,36 @@ public static IEnumerable Get(IEnumerable gameObjects)
_ => new Pickup(type),
};
+ ///
+ /// Creates and returns a new with the proper inherited subclass.
+ ///
+ /// Based on the , the returned can be cast into a subclass to gain more control over the object.
+ ///
- All valid ammo should be cast to the class.
+ ///
- All valid firearms (not including the Micro HID) should be cast to the class.
+ ///
- All valid keycards should be cast to the class.
+ ///
- All valid armor should be cast to the class.
+ ///
- All grenades and throwables (not including SCP-018 and SCP-2176) should be cast to the class.
+ ///
+ ///
+ ///
The following have their own respective classes:
+ ///
- Radios can be cast to .
+ ///
- The Micro HID can be cast to .
+ ///
- SCP-244 A and B variants can be cast to .
+ ///
- SCP-330 can be cast to .
+ ///
- SCP-018 can be cast to .
+ ///
- SCP-2176 can be cast to .
+ ///
+ ///
+ /// Items that are not listed above do not have a subclass, and can only use the base class.
+ ///
+ ///
+ /// The of the pickup.
+ /// The specified type.
+ /// The created .
+ ///
+ public static Pickup Create(ItemType type)
+ where T : Pickup => Create(type) as T;
+
///
/// Creates and spawns a .
///
@@ -498,6 +537,19 @@ public static IEnumerable Get(IEnumerable gameObjects)
///
public static Pickup CreateAndSpawn(ItemType type, Vector3 position, Quaternion rotation, Player previousOwner = null) => Create(type).Spawn(position, rotation, previousOwner);
+ ///
+ /// Creates and spawns a .
+ ///
+ /// The of the pickup.
+ /// The position to spawn the at.
+ /// The rotation to spawn the .
+ /// An optional previous owner of the item.
+ /// The specified type.
+ /// The . See documentation of for more information on casting.
+ ///
+ public static Pickup CreateAndSpawn(ItemType type, Vector3 position, Quaternion rotation, Player previousOwner = null)
+ where T : Pickup => CreateAndSpawn(type, position, rotation, previousOwner) as T;
+
///
/// Spawns a .
///
diff --git a/EXILED/Exiled.API/Features/Pickups/Projectiles/Projectile.cs b/EXILED/Exiled.API/Features/Pickups/Projectiles/Projectile.cs
index cd18059bc..5406d6414 100644
--- a/EXILED/Exiled.API/Features/Pickups/Projectiles/Projectile.cs
+++ b/EXILED/Exiled.API/Features/Pickups/Projectiles/Projectile.cs
@@ -83,17 +83,37 @@ internal Projectile(ItemType type)
/// Projectile that are not listed will cause an Exception.
///
///
- /// The of the pickup.
- /// The created .
+ /// The of the projectile.
+ /// The created .
public static Projectile Create(ProjectileType projectiletype) => projectiletype switch
{
ProjectileType.Scp018 => new Scp018Projectile(),
ProjectileType.Flashbang => new FlashbangProjectile(),
ProjectileType.Scp2176 => new Scp2176Projectile(),
ProjectileType.FragGrenade => new ExplosionGrenadeProjectile(ItemType.GrenadeHE),
- _ => throw new System.Exception($"ProjectileType does not contain a valid value : {projectiletype}"),
+ _ => throw new Exception($"ProjectileType does not contain a valid value : {projectiletype}"),
};
+ ///
+ /// Creates and returns a new with the proper inherited subclass.
+ ///
+ /// Based on the , the returned can be casted into a subclass to gain more control over the object.
+ ///
The following have their own respective classes:
+ ///
- FragGrenade can be casted to .
+ ///
- Flashbang can be casted to .
+ ///
- Scp018 A and B variants can be casted to .
+ ///
- Scp2176 can be casted to .
+ ///
+ ///
+ /// Projectile that are not listed will cause an Exception.
+ ///
+ ///
+ /// The of the projectile.
+ /// The specified type.
+ /// The created .
+ public static Projectile Create(ProjectileType projectiletype)
+ where T : Projectile => Create(projectiletype) as T;
+
///
/// Spawns a .
///
@@ -110,14 +130,27 @@ public static Projectile Spawn(Projectile pickup, Vector3 position, Quaternion r
///
/// Creates and spawns a .
///
- /// The of the pickup.
+ /// The of the projectile.
/// The position to spawn the at.
/// The rotation to spawn the .
/// Whether the should be in active state after spawn.
/// An optional previous owner of the item.
- /// The . See documentation of for more information on casting.
+ /// The . See documentation of for more information on casting.
public static Projectile CreateAndSpawn(ProjectileType type, Vector3 position, Quaternion rotation, bool shouldBeActive = true, Player previousOwner = null) => Create(type).Spawn(position, rotation, shouldBeActive, previousOwner);
+ ///
+ /// Creates and spawns a .
+ ///
+ /// The of the projectile.
+ /// The position to spawn the at.
+ /// The rotation to spawn the .
+ /// Whether the should be in active state after spawn.
+ /// An optional previous owner of the item.
+ /// The specified type.
+ /// The . See documentation of for more information on casting.
+ public static Projectile CreateAndSpawn(ProjectileType type, Vector3 position, Quaternion rotation, bool shouldBeActive = true, Player previousOwner = null)
+ where T : Projectile => CreateAndSpawn(type, position, rotation, shouldBeActive, previousOwner) as T;
+
///
/// Activates the current .
///
diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs
index f7403cdb6..cb9e84fa3 100644
--- a/EXILED/Exiled.API/Features/Player.cs
+++ b/EXILED/Exiled.API/Features/Player.cs
@@ -969,7 +969,7 @@ public Item CurrentItem
///
/// Gets the armor that the player is currently wearing. Value will be if the player is not wearing any armor.
///
- public Armor CurrentArmor => Inventory.TryGetBodyArmor(out BodyArmor armor) ? (Armor)Item.Get(armor) : null;
+ public Armor CurrentArmor => Inventory.TryGetBodyArmor(out BodyArmor armor) ? Item.Get(armor) : null;
///
/// Gets the class.
@@ -2767,7 +2767,7 @@ public void AddItem(Firearm item, IEnumerable identifiers)
/// The that was added.
public Item AddItem(FirearmPickup pickup, IEnumerable identifiers)
{
- Firearm firearm = (Firearm)Item.Get(Inventory.ServerAddItem(pickup.Type, pickup.Serial, pickup.Base));
+ Firearm firearm = Item.Get(Inventory.ServerAddItem(pickup.Type, pickup.Serial, pickup.Base));
if (identifiers is not null)
firearm.AddAttachment(identifiers);
diff --git a/EXILED/Exiled.API/Features/TeslaGate.cs b/EXILED/Exiled.API/Features/TeslaGate.cs
index d161e6b08..4d7909592 100644
--- a/EXILED/Exiled.API/Features/TeslaGate.cs
+++ b/EXILED/Exiled.API/Features/TeslaGate.cs
@@ -178,7 +178,7 @@ public bool UseInstantBurst
///
/// Gets a of which contains all the tantrums to destroy.
///
- public IEnumerable TantrumsToDestroy => Base.TantrumsToBeDestroyed.Select(x => Hazard.Get(x) as TantrumHazard);
+ public IEnumerable TantrumsToDestroy => Base.TantrumsToBeDestroyed.Select(x => Hazard.Get(x));
///
/// Gets a of which contains all the players inside the hurt range.
diff --git a/EXILED/Exiled.API/Features/Toys/AdminToy.cs b/EXILED/Exiled.API/Features/Toys/AdminToy.cs
index 2139059e9..5287096a1 100644
--- a/EXILED/Exiled.API/Features/Toys/AdminToy.cs
+++ b/EXILED/Exiled.API/Features/Toys/AdminToy.cs
@@ -159,6 +159,15 @@ public static AdminToy Get(AdminToyBase adminToyBase)
};
}
+ ///
+ /// Gets the by .
+ ///
+ /// The to convert into an admintoy.
+ /// The specified type.
+ /// The admintoy wrapper for the given .
+ public static T Get(AdminToyBase adminToyBase)
+ where T : AdminToy => Get(adminToyBase) as T;
+
///
/// Spawns the toy into the game. Use to remove it.
///
diff --git a/EXILED/Exiled.CustomItems/Patches/PlayerInventorySee.cs b/EXILED/Exiled.CustomItems/Patches/PlayerInventorySee.cs
index 8f35712fb..1620477ba 100644
--- a/EXILED/Exiled.CustomItems/Patches/PlayerInventorySee.cs
+++ b/EXILED/Exiled.CustomItems/Patches/PlayerInventorySee.cs
@@ -8,6 +8,7 @@
namespace Exiled.CustomItems.Patches
{
using System.Collections.Generic;
+ using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
@@ -52,7 +53,7 @@ private static IEnumerable Transpiler(IEnumerable !x.IsGenericMethod && x.Name is nameof(Item.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemBase))),
new(OpCodes.Dup),
new(OpCodes.Stloc_S, item.LocalIndex),
new(OpCodes.Brfalse_S, continueLabel),
diff --git a/EXILED/Exiled.Events/EventArgs/Item/ChangingAttachmentsEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Item/ChangingAttachmentsEventArgs.cs
index 0e1c8fbed..fad3ec0b5 100644
--- a/EXILED/Exiled.Events/EventArgs/Item/ChangingAttachmentsEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Item/ChangingAttachmentsEventArgs.cs
@@ -38,17 +38,13 @@ public class ChangingAttachmentsEventArgs : IPlayerEvent, IDeniableEvent, IFirea
///
///
///
- public ChangingAttachmentsEventArgs(
- Player player,
- Firearm firearm,
- uint code,
- bool isAllowed = true)
+ public ChangingAttachmentsEventArgs(Player player, InventorySystem.Items.Firearms.Firearm firearm, uint code, bool isAllowed = true)
{
Player = player;
- Firearm = firearm;
- CurrentAttachmentIdentifiers = firearm.AttachmentIdentifiers;
- NewAttachmentIdentifiers = firearm.FirearmType.GetAttachmentIdentifiers(code).ToList();
- CurrentCode = firearm.Base.GetCurrentAttachmentsCode();
+ Firearm = Item.Get(firearm);
+ CurrentAttachmentIdentifiers = Firearm.AttachmentIdentifiers;
+ NewAttachmentIdentifiers = Firearm.FirearmType.GetAttachmentIdentifiers(code).ToList();
+ CurrentCode = firearm.GetCurrentAttachmentsCode();
NewCode = code;
IsAllowed = isAllowed;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs
index 28b7b30f0..a0dbd7920 100644
--- a/EXILED/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs
@@ -37,7 +37,7 @@ public class ExplodingGrenadeEventArgs : IPlayerEvent, IDeniableEvent
public ExplodingGrenadeEventArgs(Footprint thrower, Vector3 position, ExplosionGrenade grenade, Collider[] targets)
{
Player = Player.Get(thrower.Hub);
- Projectile = (EffectGrenadeProjectile)Pickup.Get(grenade);
+ Projectile = Pickup.Get(grenade);
Position = position;
TargetsToAffect = ListPool.Pool.Get();
@@ -97,7 +97,7 @@ public ExplodingGrenadeEventArgs(Footprint thrower, Vector3 position, ExplosionG
public ExplodingGrenadeEventArgs(Player thrower, EffectGrenade grenade, List targetsToAffect, bool isAllowed = true)
{
Player = thrower ?? Server.Host;
- Projectile = (EffectGrenadeProjectile)Pickup.Get(grenade);
+ Projectile = Pickup.Get(grenade);
Position = Projectile.Position;
TargetsToAffect = ListPool.Pool.Get(targetsToAffect ?? new());
IsAllowed = isAllowed;
diff --git a/EXILED/Exiled.Events/EventArgs/Map/Scp244SpawningEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Map/Scp244SpawningEventArgs.cs
index 1ed101269..fd6a6fa76 100644
--- a/EXILED/Exiled.Events/EventArgs/Map/Scp244SpawningEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Map/Scp244SpawningEventArgs.cs
@@ -1,4 +1,4 @@
-// -----------------------------------------------------------------------
+// -----------------------------------------------------------------------
//
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
@@ -10,6 +10,8 @@ namespace Exiled.Events.EventArgs.Map
using API.Features;
using Exiled.API.Features.Pickups;
using Interfaces;
+ using InventorySystem.Items.Usables.Scp244;
+ using MapGeneration;
///
/// Contains all information up to spawning Scp244.
@@ -25,11 +27,10 @@ public class Scp244SpawningEventArgs : IRoomEvent, IPickupEvent, IDeniableEvent
///
///
///
- public Scp244SpawningEventArgs(Room room, Pickup scp244Pickup)
+ public Scp244SpawningEventArgs(RoomIdentifier room, Scp244DeployablePickup scp244Pickup)
{
- Room = room;
- Pickup = scp244Pickup;
- Scp244Pickup = scp244Pickup.As();
+ Room = Room.Get(room);
+ Scp244Pickup = Pickup.Get(scp244Pickup);
}
///
@@ -38,7 +39,7 @@ public Scp244SpawningEventArgs(Room room, Pickup scp244Pickup)
public Room Room { get; }
///
- public Pickup Pickup { get; }
+ public Pickup Pickup => Scp244Pickup;
///
/// Gets a value indicating the pickup being spawning.
diff --git a/EXILED/Exiled.Events/EventArgs/Player/CancellingItemUseEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/CancellingItemUseEventArgs.cs
index ac07393e7..f3f623f40 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/CancellingItemUseEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/CancellingItemUseEventArgs.cs
@@ -27,7 +27,7 @@ public class CancellingItemUseEventArgs : IPlayerEvent, IDeniableEvent, IUsableE
public CancellingItemUseEventArgs(Player player, UsableItem item)
{
Player = player;
- Usable = Item.Get(item) is Usable usable ? usable : null;
+ Usable = Item.Get(item);
}
///
diff --git a/EXILED/Exiled.Events/EventArgs/Player/ChangingMicroHIDStateEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ChangingMicroHIDStateEventArgs.cs
index 4cb941b0a..2b9bacdbf 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/ChangingMicroHIDStateEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/ChangingMicroHIDStateEventArgs.cs
@@ -40,7 +40,7 @@ public class ChangingMicroHIDStateEventArgs : IPlayerEvent, IDeniableEvent
public ChangingMicroHIDStateEventArgs(Player player, MicroHIDItem microHID, HidState oldState, HidState newState, bool isAllowed = true)
{
Player = player;
- MicroHID = (MicroHid)Item.Get(microHID);
+ MicroHID = Item.Get(microHID);
OldState = oldState;
NewState = newState;
IsAllowed = isAllowed;
diff --git a/EXILED/Exiled.Events/EventArgs/Player/ChangingRadioPresetEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ChangingRadioPresetEventArgs.cs
index 7b59f2fb9..70af9b581 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/ChangingRadioPresetEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/ChangingRadioPresetEventArgs.cs
@@ -44,7 +44,7 @@ public class ChangingRadioPresetEventArgs : IPlayerEvent, IItemEvent, IDeniableE
public ChangingRadioPresetEventArgs(Player player, RadioItem item, RadioRangeLevel oldValue, RadioRangeLevel newValue, bool isAllowed = true)
{
Player = player;
- Radio = (Radio)Item.Get(item);
+ Radio = Item.Get(item);
OldValue = (RadioRange)oldValue;
NewValue = (RadioRange)newValue;
IsAllowed = isAllowed;
diff --git a/EXILED/Exiled.Events/EventArgs/Player/DroppedItemEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/DroppedItemEventArgs.cs
index 14d298c84..0109a4bba 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/DroppedItemEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/DroppedItemEventArgs.cs
@@ -10,6 +10,7 @@ namespace Exiled.Events.EventArgs.Player
using API.Features;
using Exiled.API.Features.Pickups;
using Interfaces;
+ using InventorySystem.Items.Pickups;
///
/// Contains all information after a player drops an item.
@@ -28,10 +29,10 @@ public class DroppedItemEventArgs : IPlayerEvent, IPickupEvent
///
///
///
- public DroppedItemEventArgs(Player player, Pickup pickup, bool wasThrown)
+ public DroppedItemEventArgs(Player player, ItemPickupBase pickup, bool wasThrown)
{
Player = player;
- Pickup = pickup;
+ Pickup = Pickup.Get(pickup);
WasThrown = wasThrown;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/ThrowingRequestEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ThrowingRequestEventArgs.cs
index 273763fda..089695333 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/ThrowingRequestEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/ThrowingRequestEventArgs.cs
@@ -28,7 +28,7 @@ public class ThrowingRequestEventArgs : IPlayerEvent, IItemEvent
public ThrowingRequestEventArgs(Player player, ThrowableItem item, ThrowableNetworkHandler.RequestType request)
{
Player = player;
- Throwable = (Throwable)Item.Get(item);
+ Throwable = Item.Get(item);
RequestType = (ThrowRequest)request;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/ThrownProjectileEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ThrownProjectileEventArgs.cs
index aaf654d42..26984b839 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/ThrownProjectileEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/ThrownProjectileEventArgs.cs
@@ -29,8 +29,8 @@ public class ThrownProjectileEventArgs : IPlayerEvent, IItemEvent, IPickupEvent
public ThrownProjectileEventArgs(ThrownProjectile projectile, Player player, ThrowableItem item)
{
Player = player;
- Throwable = (Throwable)Item.Get(item);
- Projectile = (Projectile)Pickup.Get(projectile);
+ Throwable = Item.Get(item);
+ Projectile = Pickup.Get(projectile);
}
///
diff --git a/EXILED/Exiled.Events/EventArgs/Player/TogglingFlashlightEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/TogglingFlashlightEventArgs.cs
index b8990f85d..3a87a66a1 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/TogglingFlashlightEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/TogglingFlashlightEventArgs.cs
@@ -35,7 +35,7 @@ public class TogglingFlashlightEventArgs : IPlayerEvent, IDeniableEvent, IItemEv
public TogglingFlashlightEventArgs(ReferenceHub hub, ToggleableLightItemBase flashlight, bool newState)
{
Player = Player.Get(hub);
- Flashlight = (Flashlight)Item.Get(flashlight);
+ Flashlight = Item.Get(flashlight);
initialState = newState;
NewState = newState;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/TogglingRadioEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/TogglingRadioEventArgs.cs
index 67db54505..38d0fe09b 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/TogglingRadioEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/TogglingRadioEventArgs.cs
@@ -36,7 +36,7 @@ public class TogglingRadioEventArgs : IPlayerEvent, IDeniableEvent, IItemEvent
public TogglingRadioEventArgs(Player player, RadioItem radio, bool newState, bool isAllowed = true)
{
Player = player;
- Radio = (Radio)Item.Get(radio);
+ Radio = Item.Get(radio);
NewState = newState;
IsAllowed = isAllowed;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/UsingMicroHIDEnergyEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/UsingMicroHIDEnergyEventArgs.cs
index d98f014ed..402d20e1b 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/UsingMicroHIDEnergyEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/UsingMicroHIDEnergyEventArgs.cs
@@ -40,7 +40,7 @@ public class UsingMicroHIDEnergyEventArgs : IPlayerEvent, IDeniableEvent, IItemE
public UsingMicroHIDEnergyEventArgs(Player player, MicroHIDItem microHIDitem, HidState currentState, float drain, bool isAllowed = true)
{
Player = player;
- MicroHID = (MicroHid)Item.Get(microHIDitem);
+ MicroHID = Item.Get(microHIDitem);
CurrentState = currentState;
Drain = drain;
IsAllowed = isAllowed;
diff --git a/EXILED/Exiled.Events/EventArgs/Player/UsingRadioBatteryEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/UsingRadioBatteryEventArgs.cs
index 90369883f..1f07dadde 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/UsingRadioBatteryEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/UsingRadioBatteryEventArgs.cs
@@ -36,7 +36,7 @@ public class UsingRadioBatteryEventArgs : IPlayerEvent, IDeniableEvent, IItemEve
///
public UsingRadioBatteryEventArgs(RadioItem radio, Player player, float drain, bool isAllowed = true)
{
- Radio = (Radio)Item.Get(radio);
+ Radio = Item.Get(radio);
Player = player;
Drain = drain;
IsAllowed = isAllowed;
diff --git a/EXILED/Exiled.Events/EventArgs/Scp096/StartPryingGateEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp096/StartPryingGateEventArgs.cs
index f40e1e874..be8cef0ce 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp096/StartPryingGateEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp096/StartPryingGateEventArgs.cs
@@ -36,7 +36,7 @@ public StartPryingGateEventArgs(Player player, PryableDoor gate, bool isAllowed
{
Player = player;
Scp096 = player.Role.As();
- Gate = Door.Get(gate).As();
+ Gate = Door.Get(gate);
IsAllowed = isAllowed;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Scp244/UsingScp244EventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp244/UsingScp244EventArgs.cs
index c3077777c..5dca87a99 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp244/UsingScp244EventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp244/UsingScp244EventArgs.cs
@@ -33,7 +33,7 @@ public class UsingScp244EventArgs : IPlayerEvent, IDeniableEvent
///
public UsingScp244EventArgs(Scp244Item scp244, Player player, bool isAllowed = true)
{
- Scp244 = (Scp244)Item.Get(scp244);
+ Scp244 = Item.Get(scp244);
Player = player;
IsAllowed = isAllowed;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Scp330/DroppingScp330EventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp330/DroppingScp330EventArgs.cs
index 0105cc1a1..985e90337 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp330/DroppingScp330EventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp330/DroppingScp330EventArgs.cs
@@ -34,7 +34,7 @@ public class DroppingScp330EventArgs : IPlayerEvent, IScp330Event, IDeniableEven
public DroppingScp330EventArgs(Player player, Scp330Bag scp330, CandyKindID candy)
{
Player = player;
- Scp330 = (Scp330)Item.Get(scp330);
+ Scp330 = Item.Get(scp330);
Candy = candy;
}
diff --git a/EXILED/Exiled.Events/Patches/Events/Item/ChangingAttachments.cs b/EXILED/Exiled.Events/Patches/Events/Item/ChangingAttachments.cs
index 3e49274a0..17394e444 100644
--- a/EXILED/Exiled.Events/Patches/Events/Item/ChangingAttachments.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Item/ChangingAttachments.cs
@@ -8,6 +8,7 @@
namespace Exiled.Events.Patches.Events.Item
{
using System.Collections.Generic;
+ using System.Linq;
using System.Reflection.Emit;
using API.Features;
@@ -67,10 +68,8 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions);
- Label continueLabel = generator.DefineLabel();
-
LocalBuilder pickup = generator.DeclareLocal(typeof(ItemPickupBase));
- int index = newInstructions.FindLastIndex(i => i.opcode == OpCodes.Dup);
-
- newInstructions.RemoveRange(index, 1);
-
- int offset = 1;
- index = newInstructions.FindIndex(i => i.opcode == OpCodes.Dup) + offset;
- newInstructions.InsertRange(index, new CodeInstruction[]
- {
- new(OpCodes.Dup),
- new(OpCodes.Stloc_S, pickup.LocalIndex),
- });
+ Label continueLabel = generator.DefineLabel();
- offset = -2;
- index = newInstructions.FindIndex(i => i.Calls(Method(typeof(NetworkServer), nameof(NetworkServer.Spawn), new[] { typeof(GameObject), typeof(NetworkConnection) }))) + offset;
+ int offset = -2;
+ int index = newInstructions.FindIndex(i => i.Calls(Method(typeof(NetworkServer), nameof(NetworkServer.Spawn), new[] { typeof(GameObject), typeof(NetworkConnection) }))) + offset;
newInstructions.InsertRange(index, new[]
{
- new CodeInstruction(OpCodes.Ldsfld, Field(typeof(Scp244Spawner), nameof(Scp244Spawner.CompatibleRooms))).MoveLabelsFrom(newInstructions[index]),
+ // save Pickup from the stack
+ new CodeInstruction(OpCodes.Stloc_S, pickup.LocalIndex).MoveLabelsFrom(newInstructions[index]),
+
+ // Scp244Spawner.CompatibleRooms[num]
+ new(OpCodes.Ldsfld, Field(typeof(Scp244Spawner), nameof(Scp244Spawner.CompatibleRooms))),
new(OpCodes.Ldloc_0),
new(OpCodes.Callvirt, PropertyGetter(typeof(List), "Item")),
- new(OpCodes.Ldloc_S, pickup.LocalIndex),
- new(OpCodes.Call, Method(typeof(Pickup), nameof(Pickup.Get), new[] { typeof(ItemPickupBase) })),
+ // scp244DeployablePickup
+ new(OpCodes.Ldloc_2),
+ // Scp244SpawningEventArgs ev = new(Room, Scp244DeployablePickup)
new(OpCodes.Newobj, GetDeclaredConstructors(typeof(Scp244SpawningEventArgs))[0]),
new(OpCodes.Dup),
new(OpCodes.Call, Method(typeof(Handlers.Map), nameof(Handlers.Map.OnScp244Spawning))),
+ // if (ev.IsAllowed) goto continueLabel;
new(OpCodes.Call, PropertyGetter(typeof(Scp244SpawningEventArgs), nameof(Scp244SpawningEventArgs.IsAllowed))),
new(OpCodes.Brtrue_S, continueLabel),
- new(OpCodes.Ldloc_S, pickup.LocalIndex),
+ // scp244DeployablePickup.gameObject.Destroy()
+ // return;
+ new(OpCodes.Ldloc_2),
new(OpCodes.Callvirt, PropertyGetter(typeof(ItemPickupBase), nameof(ItemPickupBase.gameObject))),
new(OpCodes.Call, Method(typeof(NetworkServer), nameof(NetworkServer.Destroy))),
new(OpCodes.Ret),
- new CodeInstruction(OpCodes.Nop).WithLabels(continueLabel),
- new(OpCodes.Ldloc_S, pickup.LocalIndex),
+ // load pickup back into the stack
+ new CodeInstruction(OpCodes.Ldloc_S, pickup.LocalIndex).WithLabels(continueLabel),
});
for (int z = 0; z < newInstructions.Count; z++)
diff --git a/EXILED/Exiled.Events/Patches/Events/Player/DroppingItem.cs b/EXILED/Exiled.Events/Patches/Events/Player/DroppingItem.cs
index 0e0311532..26d8cb2cc 100644
--- a/EXILED/Exiled.Events/Patches/Events/Player/DroppingItem.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Player/DroppingItem.cs
@@ -11,14 +11,12 @@ namespace Exiled.Events.Patches.Events.Player
using System.Reflection.Emit;
using API.Features.Pools;
- using Exiled.API.Features.Pickups;
using Exiled.Events.Attributes;
using Exiled.Events.EventArgs.Player;
using HarmonyLib;
using InventorySystem;
- using InventorySystem.Items.Pickups;
using static HarmonyLib.AccessTools;
@@ -117,15 +115,14 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable !x.IsGenericMethod && x.Name is nameof(API.Features.Items.Item.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemBase))),
new(OpCodes.Isinst, typeof(Firearm)),
new(OpCodes.Dup),
new(OpCodes.Stloc_S, firearm.LocalIndex),
diff --git a/EXILED/Exiled.Events/Patches/Fixes/GrenadePropertiesFix.cs b/EXILED/Exiled.Events/Patches/Fixes/GrenadePropertiesFix.cs
index 8c37c4f9a..e37fb8993 100644
--- a/EXILED/Exiled.Events/Patches/Fixes/GrenadePropertiesFix.cs
+++ b/EXILED/Exiled.Events/Patches/Fixes/GrenadePropertiesFix.cs
@@ -8,6 +8,7 @@
namespace Exiled.Events.Patches.Fixes
{
using System.Collections.Generic;
+ using System.Linq;
using System.Reflection.Emit;
using API.Features.Items;
@@ -58,7 +59,7 @@ private static IEnumerable Transpiler(IEnumerable !x.IsGenericMethod && x.Name is nameof(Item.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemBase))),
new(OpCodes.Isinst, typeof(Throwable)),
new(OpCodes.Dup),
new(OpCodes.Stloc_S, throwable.LocalIndex),
diff --git a/EXILED/Exiled.Events/Patches/Generic/DoorList.cs b/EXILED/Exiled.Events/Patches/Generic/DoorList.cs
index 1b63ec329..4f1ce6e33 100644
--- a/EXILED/Exiled.Events/Patches/Generic/DoorList.cs
+++ b/EXILED/Exiled.Events/Patches/Generic/DoorList.cs
@@ -50,7 +50,7 @@ private static void Postfix(DoorVariant __instance)
foreach (DoorVariant subDoor in checkpoint.Base.SubDoors)
{
subDoor.RegisterRooms();
- BreakableDoor targetDoor = Door.Get(subDoor).Cast();
+ BreakableDoor targetDoor = Door.Get(subDoor);
targetDoor.ParentCheckpointDoor = checkpoint;
checkpoint.SubDoorsValue.Add(targetDoor);
diff --git a/EXILED/Exiled.Events/Patches/Generic/PickupControlPatch.cs b/EXILED/Exiled.Events/Patches/Generic/PickupControlPatch.cs
index c76a8f247..fc319c474 100644
--- a/EXILED/Exiled.Events/Patches/Generic/PickupControlPatch.cs
+++ b/EXILED/Exiled.Events/Patches/Generic/PickupControlPatch.cs
@@ -9,6 +9,7 @@ namespace Exiled.Events.Patches.Generic
{
using System;
using System.Collections.Generic;
+ using System.Linq;
using System.Reflection.Emit;
using API.Features.Pickups;
@@ -48,11 +49,11 @@ private static IEnumerable Transpiler(
{
// pickup = Pickup.Get(pickupBase);
new(OpCodes.Ldloc_0),
- new(OpCodes.Call, Method(typeof(Pickup), nameof(Pickup.Get), new[] { typeof(ItemPickupBase) })),
+ new(OpCodes.Call, GetDeclaredMethods(typeof(Pickup)).First(x => !x.IsGenericMethod && x.Name is nameof(Pickup.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemPickupBase))),
// Item.Get(itemBase);
new(OpCodes.Ldarg_0),
- new(OpCodes.Call, Method(typeof(Item), nameof(Item.Get), new[] { typeof(ItemBase) })),
+ new(OpCodes.Call, GetDeclaredMethods(typeof(Item)).First(x => !x.IsGenericMethod && x.Name is nameof(Item.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemBase))),
// pickup.ReadItemInfo(item);
new(OpCodes.Callvirt, Method(typeof(Pickup), nameof(Pickup.ReadItemInfo))),
@@ -79,7 +80,7 @@ private static IEnumerable Transpiler(IEnumerable !x.IsGenericMethod && x.Name is nameof(Pickup.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemPickupBase))),
new(OpCodes.Pop),
});
From 09de18badea5b048caab8e24198eab8341739f09 Mon Sep 17 00:00:00 2001
From: Yamato <66829532+louis1706@users.noreply.github.com>
Date: Fri, 9 Aug 2024 19:03:12 +0200
Subject: [PATCH 07/39] FixNpcNoclip (#34)
* FixNpcNoclip
* oups
* .
* virtual / override
---
EXILED/Exiled.API/Features/Npc.cs | 15 ++++++++++++++-
EXILED/Exiled.API/Features/Player.cs | 2 +-
2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/EXILED/Exiled.API/Features/Npc.cs b/EXILED/Exiled.API/Features/Npc.cs
index 473bb4c14..6824cbab9 100644
--- a/EXILED/Exiled.API/Features/Npc.cs
+++ b/EXILED/Exiled.API/Features/Npc.cs
@@ -17,7 +17,6 @@ namespace Exiled.API.Features
using Exiled.API.Enums;
using Exiled.API.Extensions;
using Exiled.API.Features.Components;
-
using Footprinting;
using MEC;
@@ -52,6 +51,20 @@ public Npc(GameObject gameObject)
///
public static new List List => Player.List.OfType().ToList();
+ ///
+ /// Gets or sets the player's position.
+ ///
+ public override Vector3 Position
+ {
+ get => base.Position;
+ set
+ {
+ base.Position = value;
+ if (Role is Roles.FpcRole fpcRole)
+ fpcRole.RelativePosition = new(value);
+ }
+ }
+
///
/// Retrieves the NPC associated with the specified ReferenceHub.
///
diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs
index cb9e84fa3..0a0b6c83e 100644
--- a/EXILED/Exiled.API/Features/Player.cs
+++ b/EXILED/Exiled.API/Features/Player.cs
@@ -514,7 +514,7 @@ public Player Cuffer
///
///
///
- public Vector3 Position
+ public virtual Vector3 Position
{
get => Transform.position;
set => ReferenceHub.TryOverridePosition(value, Vector3.zero);
From a1ae55455dfca4bd39fa2c5a8dfd0b1c4f51cd99 Mon Sep 17 00:00:00 2001
From: Misaka-ZeroTwo <45165615+Misaka-ZeroTwo@users.noreply.github.com>
Date: Sat, 10 Aug 2024 02:40:30 +0800
Subject: [PATCH 08/39] Implements more patches for RemovingHandcuffs event and
adding RemovedHandcuffs event (#3)
* Update labeler.yml
* RemovingHandcuffs event
* Update UncuffReason.cs
* New event
* docs
---
EXILED/Exiled.API/Enums/UncuffReason.cs | 30 +++
.../Player/RemovedHandcuffsEventArgs.cs | 47 ++++
.../Player/RemovingHandcuffsEventArgs.cs | 13 +-
EXILED/Exiled.Events/Handlers/Player.cs | 11 +
.../Events/Player/ProcessDisarmMessage.cs | 224 +++++++++++++++++-
5 files changed, 321 insertions(+), 4 deletions(-)
create mode 100644 EXILED/Exiled.API/Enums/UncuffReason.cs
create mode 100644 EXILED/Exiled.Events/EventArgs/Player/RemovedHandcuffsEventArgs.cs
diff --git a/EXILED/Exiled.API/Enums/UncuffReason.cs b/EXILED/Exiled.API/Enums/UncuffReason.cs
new file mode 100644
index 000000000..6b3018fda
--- /dev/null
+++ b/EXILED/Exiled.API/Enums/UncuffReason.cs
@@ -0,0 +1,30 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.API.Enums
+{
+ ///
+ /// Reasons that player gets uncuffed.
+ ///
+ public enum UncuffReason
+ {
+ ///
+ /// Uncuffed by a player.
+ ///
+ Player,
+
+ ///
+ /// Uncuffed due to the distance between cuffer and target.
+ ///
+ OutOfRange,
+
+ ///
+ /// Uncuffed due to the cuffer no longer alive.
+ ///
+ CufferDied,
+ }
+}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/RemovedHandcuffsEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/RemovedHandcuffsEventArgs.cs
new file mode 100644
index 000000000..ee152fb34
--- /dev/null
+++ b/EXILED/Exiled.Events/EventArgs/Player/RemovedHandcuffsEventArgs.cs
@@ -0,0 +1,47 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.EventArgs.Player
+{
+ using API.Enums;
+ using API.Features;
+ using Exiled.Events.EventArgs.Interfaces;
+
+ ///
+ /// Contains all information after freeing a handcuffed player.
+ ///
+ public class RemovedHandcuffsEventArgs : IPlayerEvent
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The cuffer player.
+ /// The target player was uncuffed.
+ /// The reason for removing the handcuffs.
+ public RemovedHandcuffsEventArgs(Player cuffer, Player target, UncuffReason uncuffReason)
+ {
+ Player = cuffer;
+ Target = target;
+ UncuffReason = uncuffReason;
+ }
+
+ ///
+ /// Gets the target player to be cuffed.
+ ///
+ public Player Target { get; }
+
+ ///
+ /// Gets the cuffer player.
+ ///
+ public Player Player { get; }
+
+ ///
+ /// Gets the reason for removing handcuffs.
+ ///
+ public UncuffReason UncuffReason { get; }
+ }
+}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/RemovingHandcuffsEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/RemovingHandcuffsEventArgs.cs
index c6a90a835..c375bb4b7 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/RemovingHandcuffsEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/RemovingHandcuffsEventArgs.cs
@@ -7,6 +7,7 @@
namespace Exiled.Events.EventArgs.Player
{
+ using API.Enums;
using API.Features;
using Exiled.Events.EventArgs.Interfaces;
@@ -20,11 +21,13 @@ public class RemovingHandcuffsEventArgs : IPlayerEvent, IDeniableEvent
///
/// The cuffer player.
/// The target player to be uncuffed.
+ /// The reason of removing handcuffs.
/// Indicates whether the event can be executed or not.
- public RemovingHandcuffsEventArgs(Player cuffer, Player target, bool isAllowed = true)
+ public RemovingHandcuffsEventArgs(Player cuffer, Player target, UncuffReason uncuffReason, bool isAllowed = true)
{
Player = cuffer;
Target = target;
+ UncuffReason = uncuffReason;
IsAllowed = isAllowed;
}
@@ -34,13 +37,19 @@ public RemovingHandcuffsEventArgs(Player cuffer, Player target, bool isAllowed =
public Player Target { get; }
///
- /// Gets or sets a value indicating whether or not the player can be handcuffed.
+ /// Gets or sets a value indicating whether or not the player can be handcuffed. Denying the event will only have an effect when is until next major update.
///
+ /// TODO: Update docs and patches
public bool IsAllowed { get; set; }
///
/// Gets the cuffer player.
///
public Player Player { get; }
+
+ ///
+ /// Gets the reason of removing handcuffs.
+ ///
+ public UncuffReason UncuffReason { get; }
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Handlers/Player.cs b/EXILED/Exiled.Events/Handlers/Player.cs
index aca07ce6e..016bdf644 100644
--- a/EXILED/Exiled.Events/Handlers/Player.cs
+++ b/EXILED/Exiled.Events/Handlers/Player.cs
@@ -223,6 +223,11 @@ public class Player
///
public static Event RemovingHandcuffs { get; set; } = new();
+ ///
+ /// Invoked after freeing a handcuffed .
+ ///
+ public static Event RemovedHandcuffs { get; set; } = new();
+
///
/// Invoked before a escapes.
///
@@ -704,6 +709,12 @@ public class Player
/// The instance.
public static void OnRemovingHandcuffs(RemovingHandcuffsEventArgs ev) => RemovingHandcuffs.InvokeSafely(ev);
+ ///
+ /// Called after freeing a handcuffed .
+ ///
+ /// The instance.
+ public static void OnRemovedHandcuffs(RemovedHandcuffsEventArgs ev) => RemovedHandcuffs.InvokeSafely(ev);
+
///
/// Called before a escapes.
///
diff --git a/EXILED/Exiled.Events/Patches/Events/Player/ProcessDisarmMessage.cs b/EXILED/Exiled.Events/Patches/Events/Player/ProcessDisarmMessage.cs
index bd83c19e9..a3805d33d 100644
--- a/EXILED/Exiled.Events/Patches/Events/Player/ProcessDisarmMessage.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Player/ProcessDisarmMessage.cs
@@ -7,6 +7,7 @@
namespace Exiled.Events.Patches.Events.Player
{
+ #pragma warning disable SA1402 // File may only contain a single type
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
@@ -26,10 +27,11 @@ namespace Exiled.Events.Patches.Events.Player
///
/// Patches .
- /// Adds the and events.
+ /// Adds the , , and events.
///
[EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.Handcuffing))]
[EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.RemovingHandcuffs))]
+ [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.RemovedHandcuffs))]
[HarmonyPatch(typeof(DisarmingHandlers), nameof(DisarmingHandlers.ServerProcessDisarmMessage))]
internal static class ProcessDisarmMessage
{
@@ -46,6 +48,7 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions);
}
}
+
+ ///
+ /// Patches .
+ /// Invokes and event.
+ ///
+ [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.RemovingHandcuffs))]
+ [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.RemovedHandcuffs))]
+ [HarmonyPatch(typeof(DisarmedPlayers), nameof(DisarmedPlayers.ValidateEntry))]
+ internal static class Uncuff
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+ Label returnLabel = generator.DefineLabel();
+
+ int offset = 2;
+ int index = newInstructions.FindLastIndex(
+ instruction => instruction.Calls(Method(typeof(ReferenceHub), nameof(ReferenceHub.TryGetHubNetID)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // Invoking RemovingHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.CufferDied
+ new(OpCodes.Ldc_I4_2),
+
+ // true
+ new(OpCodes.Ldc_I4_1),
+
+ // RemovingHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.CufferDied, true)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovingHandcuffsEventArgs))[0]),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // new(OpCodes.Dup),
+
+ // Handlers.Player.OnRemovingHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovingHandcuffs))),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // if (!ev.IsAllowed)
+ // return true;
+ // new(OpCodes.Callvirt, PropertyGetter(typeof(RemovingHandcuffsEventArgs), nameof(RemovingHandcuffsEventArgs.IsAllowed))),
+ // new(OpCodes.Brfalse_S, returnLabel),
+
+ // Invoking RemovedHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.CufferDied
+ new(OpCodes.Ldc_I4_2),
+
+ // RemovedHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.CufferDied)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovedHandcuffsEventArgs))[0]),
+
+ // Handlers.Player.OnRemovedHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovedHandcuffs))),
+ });
+
+ offset = 5;
+ index = newInstructions.FindLastIndex(
+ instruction => instruction.Calls(PropertyGetter(typeof(PlayerRoles.PlayerRoleManager), nameof(PlayerRoles.PlayerRoleManager.CurrentRole)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // Invoking RemovingHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.CufferDied
+ new(OpCodes.Ldc_I4_2),
+
+ // true
+ new(OpCodes.Ldc_I4_1),
+
+ // RemovingHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.CufferDied, true)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovingHandcuffsEventArgs))[0]),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // new(OpCodes.Dup),
+
+ // Handlers.Player.OnRemovingHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovingHandcuffs))),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // if (!ev.IsAllowed)
+ // return true;
+ // new(OpCodes.Callvirt, PropertyGetter(typeof(RemovingHandcuffsEventArgs), nameof(RemovingHandcuffsEventArgs.IsAllowed))),
+ // new(OpCodes.Brfalse_S, returnLabel),
+
+ // Invoking RemovedHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.CufferDied
+ new(OpCodes.Ldc_I4_2),
+
+ // RemovedHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.CufferDied)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovedHandcuffsEventArgs))[0]),
+
+ // Handlers.Player.OnRemovedHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovedHandcuffs))),
+ });
+
+ offset = 3;
+ index = newInstructions.FindLastIndex(
+ instruction => instruction.Calls(PropertyGetter(typeof(UnityEngine.Vector3), nameof(UnityEngine.Vector3.sqrMagnitude)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // Invoking RemovingHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.OutOfRange
+ new(OpCodes.Ldc_I4_1),
+
+ // true
+ new(OpCodes.Ldc_I4_1),
+
+ // RemovingHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.OutOfRange, true)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovingHandcuffsEventArgs))[0]),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // new(OpCodes.Dup),
+
+ // Handlers.Player.OnRemovingHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovingHandcuffs))),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // if (!ev.IsAllowed)
+ // return true;
+ // new(OpCodes.Callvirt, PropertyGetter(typeof(RemovingHandcuffsEventArgs), nameof(RemovingHandcuffsEventArgs.IsAllowed))),
+ // new(OpCodes.Brfalse_S, returnLabel),
+
+ // Invoking RemovedHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.CufferDied
+ new(OpCodes.Ldc_I4_2),
+
+ // RemovedHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.OutOfRange)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovedHandcuffsEventArgs))[0]),
+
+ // Handlers.Player.OnRemovedHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovedHandcuffs))),
+ });
+
+ newInstructions[newInstructions.Count - 2].labels.Add(returnLabel);
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
}
\ No newline at end of file
From 2c41d7d817c73864b63033120a35949a5c2e859f Mon Sep 17 00:00:00 2001
From: X <24619207+Undid-Iridium@users.noreply.github.com>
Date: Sat, 10 Aug 2024 02:47:30 -0400
Subject: [PATCH 09/39] Interacting scp330 compile fix (#43)
* My scp built with no issues.. no idea why.
* Fixes issue
* Harmony suppresses NON harmony errors.
---------
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
---
.../Patches/Events/Scp330/InteractingScp330.cs | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/EXILED/Exiled.Events/Patches/Events/Scp330/InteractingScp330.cs b/EXILED/Exiled.Events/Patches/Events/Scp330/InteractingScp330.cs
index aea840168..640b224d7 100644
--- a/EXILED/Exiled.Events/Patches/Events/Scp330/InteractingScp330.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Scp330/InteractingScp330.cs
@@ -36,6 +36,7 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable instruction.Calls(Method(typeof(Scp330Interobject), nameof(Scp330Interobject.RpcMakeSound)))) + addShouldSeverOffset;
int serverEffectLocationStart = -1;
int enableEffect = newInstructions.FindLastIndex(
- instruction => instruction.LoadsField(Field(typeof(PlayerEffectsController), nameof(ReferenceHub.playerEffectsController)))) + serverEffectLocationStart;
-
+ instruction => instruction.LoadsField(Field(typeof(ReferenceHub), nameof(ReferenceHub.playerEffectsController)))) + serverEffectLocationStart;
+ newInstructions[enableEffect].WithLabels(enableEffectLabel);
newInstructions.InsertRange(
addShouldSeverIndex,
new[]
@@ -91,7 +92,7 @@ private static IEnumerable Transpiler(IEnumerable
Date: Sat, 10 Aug 2024 22:15:40 +0200
Subject: [PATCH 10/39] Trello is no more & more NW Fix & Fix IL Code on dev
(#28)
* Trello Is Replaced by gitlab
* also this one
* Fix106RegenerationWithScp244
* Report To NW
* .
* Scp3114FriendlyFireFix
* Fix
* yamatotototo
* Fix
* Fix for building dev + TODO than i just seen
* Fix Undid patch
* fIX
* Fix Client Crash Issue
---------
Co-authored-by: IRacle
---
.../Player/LocalReportingEventArgs.cs | 2 +-
.../Scp049/FinishingRecallEventArgs.cs | 2 +-
.../Handlers/Internal/MapGenerated.cs | 2 +-
.../Exiled.Events/Handlers/Internal/Round.cs | 2 +-
.../Patches/Events/Map/SpawningItem.cs | 7 +-
.../Patches/Events/Player/Escaping.cs | 4 +-
.../Patches/Events/Scp049/FinishingRecall.cs | 5 +-
.../Events/Scp330/InteractingScp330.cs | 1 +
.../Patches/Events/Server/Reporting.cs | 2 +-
.../Fixes/Fix106RegenerationWithScp244.cs | 72 ++++++++++
.../Patches/Fixes/NWFixScp096BreakingDoor.cs | 2 +-
.../Patches/Fixes/Scp3114AttackAhpFix.cs | 2 +-
.../Patches/Fixes/Scp3114FriendlyFireFix.cs | 124 ++++++++++++++++++
.../Patches/Fixes/SlownessFix.cs | 2 +
.../Patches/Fixes/WarheadConfigLockGateFix.cs | 45 +++++++
15 files changed, 259 insertions(+), 15 deletions(-)
create mode 100644 EXILED/Exiled.Events/Patches/Fixes/Fix106RegenerationWithScp244.cs
create mode 100644 EXILED/Exiled.Events/Patches/Fixes/Scp3114FriendlyFireFix.cs
create mode 100644 EXILED/Exiled.Events/Patches/Fixes/WarheadConfigLockGateFix.cs
diff --git a/EXILED/Exiled.Events/EventArgs/Player/LocalReportingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/LocalReportingEventArgs.cs
index 316be5039..97a443ea7 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/LocalReportingEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/LocalReportingEventArgs.cs
@@ -5,7 +5,7 @@
//
// -----------------------------------------------------------------------
-namespace Exiled.Events.EventArgs.Player
+namespace Exiled.Events.EventArgs.Player // TODO: Wrong namespace should be Server
{
using API.Features;
diff --git a/EXILED/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs
index e2c2c0728..e897dbb04 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs
@@ -38,7 +38,7 @@ public FinishingRecallEventArgs(Player target, Player scp049, BasicRagdoll ragdo
Scp049 = Player.Role.As();
Target = target;
Ragdoll = Ragdoll.Get(ragdoll);
- IsAllowed = isAllowed && Target.Role is SpectatorRole spectatorRole && spectatorRole.IsReadyToRespawn;
+ IsAllowed = isAllowed;
}
///
diff --git a/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs b/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs
index 1b1dadf46..f75f2e91f 100644
--- a/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs
+++ b/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs
@@ -48,7 +48,7 @@ public static void OnMapGenerated()
Map.ClearCache();
PrefabHelper.LoadPrefabs();
- // TODO: Fix For (https://trello.com/c/cUwpZDLs/5003-config-teamrespawnqueue-in-configgameplay-is-not-working-as-expected)
+ // TODO: Fix For (https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/377)
PlayerRoles.RoleAssign.HumanSpawner.Handlers[PlayerRoles.Team.ChaosInsurgency] = new PlayerRoles.RoleAssign.OneRoleHumanSpawner(PlayerRoles.RoleTypeId.ChaosConscript);
PlayerRoles.RoleAssign.HumanSpawner.Handlers[PlayerRoles.Team.OtherAlive] = new PlayerRoles.RoleAssign.OneRoleHumanSpawner(PlayerRoles.RoleTypeId.Tutorial);
PlayerRoles.RoleAssign.HumanSpawner.Handlers[PlayerRoles.Team.Dead] = new PlayerRoles.RoleAssign.OneRoleHumanSpawner(PlayerRoles.RoleTypeId.Spectator);
diff --git a/EXILED/Exiled.Events/Handlers/Internal/Round.cs b/EXILED/Exiled.Events/Handlers/Internal/Round.cs
index 400eb044c..8f9bd58b2 100644
--- a/EXILED/Exiled.Events/Handlers/Internal/Round.cs
+++ b/EXILED/Exiled.Events/Handlers/Internal/Round.cs
@@ -86,7 +86,7 @@ public static void OnVerified(VerifiedEventArgs ev)
{
RoleAssigner.CheckLateJoin(ev.Player.ReferenceHub, ClientInstanceMode.ReadyClient);
- // TODO: Remove if this has been fixed for https://trello.com/c/CzPD304L/5983-networking-blackout-is-not-synchronized-for-the-new-players
+ // TODO: Remove if this has been fixed for https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/52
foreach (Room room in Room.List.Where(current => current.AreLightsOff))
{
ev.Player.SendFakeSyncVar(room.RoomLightControllerNetIdentity, typeof(RoomLightController), nameof(RoomLightController.NetworkLightsEnabled), true);
diff --git a/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs b/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs
index 28fd021f0..e65ced6fb 100644
--- a/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs
@@ -40,6 +40,7 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable instruction.IsLdarg(0));
diff --git a/EXILED/Exiled.Events/Patches/Events/Player/Escaping.cs b/EXILED/Exiled.Events/Patches/Events/Player/Escaping.cs
index b19ffdaee..048de1136 100644
--- a/EXILED/Exiled.Events/Patches/Events/Player/Escaping.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Player/Escaping.cs
@@ -138,9 +138,9 @@ private static IEnumerable Transpiler(IEnumerable customExit)
newInstructions[i].opcode = OpCodes.Ldc_I4_5;
}
diff --git a/EXILED/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs b/EXILED/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs
index d1458e0fd..2eab40acc 100644
--- a/EXILED/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs
@@ -25,11 +25,8 @@ namespace Exiled.Events.Patches.Events.Scp049
///
/// Patches .
/// Adds the event.
- /// Fix bug than Overwatch can get force respawn by Scp049
- /// Bug reported to NW https://trello.com/c/V0uHP2eV/5745-overwatch-overwatch-can-get-respawned-by-scp-049.
- /// The fix is directly inside the .
///
- // [EventPatch(typeof(Handlers.Scp049), nameof(Handlers.Scp049.FinishingRecall))]
+ [EventPatch(typeof(Handlers.Scp049), nameof(Handlers.Scp049.FinishingRecall))]
[HarmonyPatch(typeof(Scp049ResurrectAbility), nameof(Scp049ResurrectAbility.ServerComplete))]
internal static class FinishingRecall
{
diff --git a/EXILED/Exiled.Events/Patches/Events/Scp330/InteractingScp330.cs b/EXILED/Exiled.Events/Patches/Events/Scp330/InteractingScp330.cs
index 640b224d7..c8ff97d32 100644
--- a/EXILED/Exiled.Events/Patches/Events/Scp330/InteractingScp330.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Scp330/InteractingScp330.cs
@@ -82,6 +82,7 @@ private static IEnumerable Transpiler(IEnumerable instruction.LoadsField(Field(typeof(ReferenceHub), nameof(ReferenceHub.playerEffectsController)))) + serverEffectLocationStart;
+
newInstructions[enableEffect].WithLabels(enableEffectLabel);
newInstructions.InsertRange(
addShouldSeverIndex,
diff --git a/EXILED/Exiled.Events/Patches/Events/Server/Reporting.cs b/EXILED/Exiled.Events/Patches/Events/Server/Reporting.cs
index 5d22e96c5..d672d1831 100644
--- a/EXILED/Exiled.Events/Patches/Events/Server/Reporting.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Server/Reporting.cs
@@ -41,7 +41,7 @@ private static IEnumerable Transpiler(IEnumerable instruction.opcode == OpCodes.Ldarg_S && instruction.operand == (object)4) + offset;
+ int index = newInstructions.FindLastIndex(instruction => instruction.opcode == OpCodes.Ldarg_S && instruction.operand is byte and 4) + offset;
Label ret = generator.DefineLabel();
diff --git a/EXILED/Exiled.Events/Patches/Fixes/Fix106RegenerationWithScp244.cs b/EXILED/Exiled.Events/Patches/Fixes/Fix106RegenerationWithScp244.cs
new file mode 100644
index 000000000..33c54d5ad
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Fixes/Fix106RegenerationWithScp244.cs
@@ -0,0 +1,72 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Fixes
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using API.Features.Pools;
+ using CustomPlayerEffects;
+ using HarmonyLib;
+ using InventorySystem.Items.Usables.Scp244.Hypothermia;
+ using PlayerRoles;
+ using PlayerRoles.PlayableScps.Scp106;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches the delegate.
+ /// Fix than SCP-106 regenerates slower in SCP-244 even if they are in stalk.
+ /// Bug reported to NW (https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/367).
+ ///
+ [HarmonyPatch(typeof(Hypothermia), nameof(Hypothermia.Update))]
+ internal class Fix106RegenerationWithScp244
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ LocalBuilder scp106Role = generator.DeclareLocal(typeof(Scp106Role));
+ Label continueLabel = generator.DefineLabel();
+
+ int offset = 1;
+ int index = newInstructions.FindLastIndex(x => x.operand == (object)Method(typeof(SpawnProtected), nameof(SpawnProtected.CheckPlayer))) + offset;
+
+ Label skip = (Label)newInstructions[index].operand;
+
+ index += offset;
+
+ newInstructions[index].labels.Add(continueLabel);
+
+ newInstructions.InsertRange(index, new[]
+ {
+ // Scp106Role scp106Role = base.Hub.roleManager.CurrentRole as Scp106Role;
+ new CodeInstruction(OpCodes.Ldarg_0),
+ new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(StatusEffectBase), nameof(StatusEffectBase.Hub))),
+ new CodeInstruction(OpCodes.Ldfld, Field(typeof(ReferenceHub), nameof(ReferenceHub.roleManager))),
+ new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(PlayerRoleManager), nameof(PlayerRoleManager.CurrentRole))),
+ new CodeInstruction(OpCodes.Isinst, typeof(Scp106Role)),
+ new CodeInstruction(OpCodes.Stloc_S, scp106Role.LocalIndex),
+
+ // if (scp106Role is null) goto continueLabel
+ new CodeInstruction(OpCodes.Ldloc_S, scp106Role.LocalIndex),
+ new CodeInstruction(OpCodes.Brfalse_S, continueLabel),
+
+ // if (!scp106Role.IsSubmerged) goto skip
+ new CodeInstruction(OpCodes.Ldloc_S, scp106Role.LocalIndex),
+ new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(Scp106Role), nameof(Scp106Role.IsSubmerged))),
+ new CodeInstruction(OpCodes.Brtrue_S, skip),
+ });
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
diff --git a/EXILED/Exiled.Events/Patches/Fixes/NWFixScp096BreakingDoor.cs b/EXILED/Exiled.Events/Patches/Fixes/NWFixScp096BreakingDoor.cs
index 4b294f953..5401f24b8 100644
--- a/EXILED/Exiled.Events/Patches/Fixes/NWFixScp096BreakingDoor.cs
+++ b/EXILED/Exiled.Events/Patches/Fixes/NWFixScp096BreakingDoor.cs
@@ -22,7 +22,7 @@ namespace Exiled.Events.Patches.Fixes
///
/// Patches the delegate.
/// Fixes open doors getting easily broke.
- /// Bug reported to NW (https://trello.com/c/6Nz7Isjm/4637-scp096-easily-breaking-opened-doors).
+ /// Bug reported to NW (https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/198).
///
[HarmonyPatch(typeof(Scp096HitHandler), nameof(Scp096HitHandler.CheckDoorHit))]
internal class NWFixScp096BreakingDoor
diff --git a/EXILED/Exiled.Events/Patches/Fixes/Scp3114AttackAhpFix.cs b/EXILED/Exiled.Events/Patches/Fixes/Scp3114AttackAhpFix.cs
index 2722c59e1..45f3774c4 100644
--- a/EXILED/Exiled.Events/Patches/Fixes/Scp3114AttackAhpFix.cs
+++ b/EXILED/Exiled.Events/Patches/Fixes/Scp3114AttackAhpFix.cs
@@ -20,7 +20,7 @@ namespace Exiled.Events.Patches.Fixes
///
/// Patches the delegate.
/// Fix than Scp3114Slap was giving humeshield even if player was not hit by Scp3114.
- /// Bug reported to NW (https://trello.com/c/1AwpM8XE/5814-scp3114-is-able-to-get-humeshield-with-godmod-player).
+ /// Bug reported to NW (https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/119).
///
[HarmonyPatch(typeof(Scp3114Slap), nameof(Scp3114Slap.DamagePlayers))]
internal class Scp3114AttackAhpFix
diff --git a/EXILED/Exiled.Events/Patches/Fixes/Scp3114FriendlyFireFix.cs b/EXILED/Exiled.Events/Patches/Fixes/Scp3114FriendlyFireFix.cs
new file mode 100644
index 000000000..70eca7fba
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Fixes/Scp3114FriendlyFireFix.cs
@@ -0,0 +1,124 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Fixes
+{
+#pragma warning disable SA1402 // File may only contain a single type
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using API.Features.Pools;
+
+ using Exiled.API.Features;
+
+ using Footprinting;
+ using HarmonyLib;
+ using InventorySystem.Items.Pickups;
+ using InventorySystem.Items.ThrowableProjectiles;
+ using PlayerRoles;
+ using PlayerStatsSystem;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches the delegate.
+ /// Fix Throwing a ghostlight with Scp in the room stun 079.
+ /// Bug reported to NW (https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/55).
+ ///
+ [HarmonyPatch(typeof(Scp2176Projectile), nameof(Scp2176Projectile.ServerShatter))]
+ internal class Scp3114FriendlyFireFix
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ Label cnt = generator.DefineLabel();
+
+ int offset = 0;
+ int index = newInstructions.FindIndex(x => x.LoadsField(Field(typeof(RoomLightController), nameof(RoomLightController.Instances)))) + offset;
+
+ Label skip = newInstructions[index].labels[0];
+
+ offset = -3;
+ index += offset;
+
+ newInstructions.InsertRange(index, new[]
+ {
+ // if (this.PreviousOwner.Role.GetTeam() is Team.SCPs)
+ new CodeInstruction(OpCodes.Ldarg_0).MoveLabelsFrom(newInstructions[index]),
+ new(OpCodes.Ldfld, Field(typeof(Scp2176Projectile), nameof(Scp2176Projectile.PreviousOwner))),
+ new(OpCodes.Ldfld, Field(typeof(Footprint), nameof(Footprint.Role))),
+ new(OpCodes.Call, Method(typeof(PlayerRolesUtils), nameof(PlayerRolesUtils.GetTeam), new[] { typeof(RoleTypeId) })),
+ new(OpCodes.Ldc_I4_0),
+ new(OpCodes.Ceq),
+
+ new(OpCodes.Brfalse_S, cnt),
+
+ new(OpCodes.Pop),
+ new(OpCodes.Br_S, skip),
+
+ new CodeInstruction(OpCodes.Nop).WithLabels(cnt),
+ });
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+
+ ///
+ /// Patches the delegate.
+ /// Fix Throwing a ghostlight with Scp in the room stun 079.
+ /// Bug reported to NW (https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/55).
+ ///
+ [HarmonyPatch(typeof(CollisionDetectionPickup), nameof(CollisionDetectionPickup.ProcessCollision))]
+ internal class Scp3114FriendlyFireFix2 : AttackerDamageHandler
+ {
+#pragma warning disable SA1600 // Elements should be documented
+ public Scp3114FriendlyFireFix2(Footprint attacker, float damage)
+ {
+ Attacker = attacker;
+ Damage = damage;
+ AllowSelfDamage = false;
+ ServerLogsText = "Scp3114 Fix";
+ }
+
+ public override Footprint Attacker { get; set; }
+
+ public override bool AllowSelfDamage { get; }
+
+ public override float Damage { get; set; }
+
+ public override string ServerLogsText { get; }
+#pragma warning restore SA1600 // Elements should be documented
+
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ int offset = 0;
+ int index = newInstructions.FindLastIndex(x => x.opcode == OpCodes.Ldnull) + offset;
+
+ // replace null with new Scp3114FriendlyFireFix2(this.PreviousOwner, num2)
+ newInstructions.RemoveAt(index);
+ newInstructions.InsertRange(index, new CodeInstruction[]
+ {
+ // new Scp3114FriendlyFireFix2(this.PreviousOwner, num2)
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Ldfld, Field(typeof(CollisionDetectionPickup), nameof(CollisionDetectionPickup.PreviousOwner))),
+ new(OpCodes.Ldloc_3),
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(Scp3114FriendlyFireFix2))[0]),
+ });
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
diff --git a/EXILED/Exiled.Events/Patches/Fixes/SlownessFix.cs b/EXILED/Exiled.Events/Patches/Fixes/SlownessFix.cs
index 5a05c31a0..1d8774a46 100644
--- a/EXILED/Exiled.Events/Patches/Fixes/SlownessFix.cs
+++ b/EXILED/Exiled.Events/Patches/Fixes/SlownessFix.cs
@@ -19,6 +19,7 @@ namespace Exiled.Events.Patches.Fixes
///
/// Patches getter to fix Slowness effect.
+ /// reported to NW (https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/378).
///
[HarmonyPatch(typeof(FpcMotor), nameof(FpcMotor.DesiredMove), MethodType.Getter)]
internal class SlownessFix
@@ -41,6 +42,7 @@ private static IEnumerable Transpiler(IEnumerable
/// Patches method to fix Slowness effect.
+ /// reported to NW (https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/378).
///
[HarmonyPatch(typeof(FpcMotor), nameof(FpcMotor.UpdatePosition))]
#pragma warning disable SA1402 // File may only contain a single type
diff --git a/EXILED/Exiled.Events/Patches/Fixes/WarheadConfigLockGateFix.cs b/EXILED/Exiled.Events/Patches/Fixes/WarheadConfigLockGateFix.cs
new file mode 100644
index 000000000..2c65c58a8
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Fixes/WarheadConfigLockGateFix.cs
@@ -0,0 +1,45 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Fixes
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using API.Features.Pools;
+ using Footprinting;
+ using HarmonyLib;
+ using Interactables.Interobjects.DoorUtils;
+ using InventorySystem;
+ using InventorySystem.Items.Firearms.Ammo;
+ using InventorySystem.Items.Pickups;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches delegate.
+ /// Fix than NW config "lock_gates_on_countdown"
+ /// reported https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/316.
+ ///
+ [HarmonyPatch(typeof(DoorEventOpenerExtension), nameof(DoorEventOpenerExtension.Trigger))]
+ internal class WarheadConfigLockGateFix
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ // replace Contains with StartWith
+ int index = newInstructions.FindIndex(x => x.operand == (object)Method(typeof(string), nameof(string.Contains), new[] { typeof(string) }));
+ newInstructions[index].operand = Method(typeof(string), nameof(string.StartsWith), new System.Type[] { typeof(string) });
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
From a3595e10250fe3233bf22670f0582a438667d540 Mon Sep 17 00:00:00 2001
From: Nameless <85962933+Misfiy@users.noreply.github.com>
Date: Sun, 11 Aug 2024 10:59:58 +0200
Subject: [PATCH 11/39] Additions (#29)
* Add a bunch
* Fix
* Security
* Make changes
* remove unused usings
* Getting inventory
* oops
* Dev commit
* use exiled
---------
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
---
EXILED/Exiled.API/Enums/HazardType.cs | 37 +++++++++++
.../Extensions/BitwiseExtensions.cs | 63 +++++++++++++++++++
.../Exiled.API/Extensions/RoleExtensions.cs | 23 ++++---
.../Features/Hazards/AmnesticCloudHazard.cs | 22 ++++++-
EXILED/Exiled.API/Features/Hazards/Hazard.cs | 13 ++++
.../Features/Hazards/SinkholeHazard.cs | 4 ++
.../Features/Hazards/TantrumHazard.cs | 44 +++++++++++++
EXILED/Exiled.API/Features/Map.cs | 56 +----------------
EXILED/Exiled.API/Features/Player.cs | 2 +-
EXILED/Exiled.API/Features/PrefabHelper.cs | 19 +++++-
EXILED/Exiled.API/Features/Roles/FpcRole.cs | 11 +++-
EXILED/Exiled.API/Features/Server.cs | 9 +++
EXILED/Exiled.Events/Commands/TpsCommand.cs | 46 ++++++++++++++
.../EventArgs/Player/ChangingRoleEventArgs.cs | 22 +++----
14 files changed, 290 insertions(+), 81 deletions(-)
create mode 100644 EXILED/Exiled.API/Enums/HazardType.cs
create mode 100644 EXILED/Exiled.API/Extensions/BitwiseExtensions.cs
create mode 100644 EXILED/Exiled.Events/Commands/TpsCommand.cs
diff --git a/EXILED/Exiled.API/Enums/HazardType.cs b/EXILED/Exiled.API/Enums/HazardType.cs
new file mode 100644
index 000000000..f05f8852f
--- /dev/null
+++ b/EXILED/Exiled.API/Enums/HazardType.cs
@@ -0,0 +1,37 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.API.Enums
+{
+ using Exiled.API.Features.Hazards;
+
+ ///
+ /// Unique identifier for a .
+ ///
+ public enum HazardType
+ {
+ ///
+ /// SCP-939 amnestic cloud.
+ ///
+ AmnesticCloud,
+
+ ///
+ /// Sinkhole spawned at start of round.
+ ///
+ Sinkhole,
+
+ ///
+ /// SCP-173 tantrum.
+ ///
+ Tantrum,
+
+ ///
+ /// Should never happen
+ ///
+ Unknown,
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Extensions/BitwiseExtensions.cs b/EXILED/Exiled.API/Extensions/BitwiseExtensions.cs
new file mode 100644
index 000000000..2f8473784
--- /dev/null
+++ b/EXILED/Exiled.API/Extensions/BitwiseExtensions.cs
@@ -0,0 +1,63 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.API.Extensions
+{
+ using System;
+
+ ///
+ /// Extensions for bitwise operations.
+ ///
+ public static class BitwiseExtensions
+ {
+ ///
+ /// Adds the specified flags to the given enum value.
+ ///
+ /// The type of the enum.
+ /// The enum value to add flags to.
+ /// The flags to add.
+ /// The enum value with the specified flags added.
+ public static T AddFlags(this T flags, params T[] newFlags)
+ where T : Enum => flags.ModifyFlags(true, newFlags);
+
+ ///
+ /// Removes the specified flags from the given enum value.
+ ///
+ /// The type of the enum.
+ /// The enum value to remove flags from.
+ /// The flags to remove.
+ /// The enum value with the specified flags removed.
+ public static T RemoveFlags(this T flags, params T[] oldFlags)
+ where T : Enum => flags.ModifyFlags(false, oldFlags);
+
+ ///
+ /// Sets the specified flag to the given value, default is true.
+ ///
+ /// The flags enum to modify.
+ /// The value to set the flag to.
+ /// The flags to modify.
+ /// The type of the enum.
+ /// The flags enum with the flag set to the given value.
+ public static T ModifyFlags(this T flags, bool value, params T[] changeFlags)
+ where T : Enum
+ {
+ long currentValue = Convert.ToInt64(flags);
+
+ foreach (T flag in changeFlags)
+ {
+ long flagValue = Convert.ToInt64(flag);
+
+ if (value)
+ currentValue |= flagValue;
+ else
+ currentValue &= ~flagValue;
+ }
+
+ return (T)Enum.ToObject(typeof(T), currentValue);
+ }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Extensions/RoleExtensions.cs b/EXILED/Exiled.API/Extensions/RoleExtensions.cs
index e9186ba71..c674af1bf 100644
--- a/EXILED/Exiled.API/Extensions/RoleExtensions.cs
+++ b/EXILED/Exiled.API/Extensions/RoleExtensions.cs
@@ -140,18 +140,22 @@ public static SpawnLocation GetRandomSpawnLocation(this RoleTypeId roleType)
return null;
}
+ ///
+ /// Gets the starting of a .
+ ///
+ /// The .
+ /// The that the role receives on spawn.
+ public static InventoryRoleInfo GetInventory(this RoleTypeId role)
+ => StartingInventories.DefinedInventories.TryGetValue(role, out InventoryRoleInfo info)
+ ? info
+ : new(Array.Empty(), new());
+
///
/// Gets the starting items of a .
///
/// The .
/// An of that the role receives on spawn. Will be empty for classes that do not spawn with items.
- public static ItemType[] GetStartingInventory(this RoleTypeId roleType)
- {
- if (StartingInventories.DefinedInventories.TryGetValue(roleType, out InventoryRoleInfo info))
- return info.Items;
-
- return Array.Empty();
- }
+ public static ItemType[] GetStartingInventory(this RoleTypeId roleType) => GetInventory(roleType).Items;
///
/// Gets the starting ammo of a .
@@ -160,10 +164,9 @@ public static ItemType[] GetStartingInventory(this RoleTypeId roleType)
/// An of that the role receives on spawn. Will be empty for classes that do not spawn with ammo.
public static Dictionary GetStartingAmmo(this RoleTypeId roleType)
{
- if (StartingInventories.DefinedInventories.TryGetValue(roleType, out InventoryRoleInfo info))
- return info.Ammo.ToDictionary(kvp => kvp.Key.GetAmmoType(), kvp => kvp.Value);
+ InventoryRoleInfo info = roleType.GetInventory();
- return new();
+ return info.Ammo.ToDictionary(kvp => kvp.Key.GetAmmoType(), kvp => kvp.Value);
}
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Features/Hazards/AmnesticCloudHazard.cs b/EXILED/Exiled.API/Features/Hazards/AmnesticCloudHazard.cs
index c385c143f..31c9ad244 100644
--- a/EXILED/Exiled.API/Features/Hazards/AmnesticCloudHazard.cs
+++ b/EXILED/Exiled.API/Features/Hazards/AmnesticCloudHazard.cs
@@ -3,10 +3,11 @@
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
//
-// -----------------------------------------------------------------------
+// ------------------------------------------------------------------------
namespace Exiled.API.Features.Hazards
{
+ using Exiled.API.Enums;
using PlayerRoles.PlayableScps.Scp939;
///
@@ -14,6 +15,8 @@ namespace Exiled.API.Features.Hazards
///
public class AmnesticCloudHazard : TemporaryHazard
{
+ private static Scp939AmnesticCloudInstance amnesticCloudPrefab;
+
///
/// Initializes a new instance of the class.
///
@@ -26,9 +29,26 @@ public AmnesticCloudHazard(Scp939AmnesticCloudInstance hazard)
Owner = Player.Get(Ability.Owner);
}
+ ///
+ /// Gets the amnestic cloud prefab.
+ ///
+ public static Scp939AmnesticCloudInstance AmnesticCloudPrefab
+ {
+ get
+ {
+ if (amnesticCloudPrefab == null)
+ amnesticCloudPrefab = PrefabHelper.GetPrefab(PrefabType.AmnesticCloudHazard);
+
+ return amnesticCloudPrefab;
+ }
+ }
+
///
public new Scp939AmnesticCloudInstance Base { get; }
+ ///
+ public override HazardType Type => HazardType.AmnesticCloud;
+
///
/// Gets the for this instance.
///
diff --git a/EXILED/Exiled.API/Features/Hazards/Hazard.cs b/EXILED/Exiled.API/Features/Hazards/Hazard.cs
index 253825530..09bd880d3 100644
--- a/EXILED/Exiled.API/Features/Hazards/Hazard.cs
+++ b/EXILED/Exiled.API/Features/Hazards/Hazard.cs
@@ -11,6 +11,7 @@ namespace Exiled.API.Features.Hazards
using System.Collections.Generic;
using System.Linq;
+ using Exiled.API.Enums;
using Exiled.API.Features.Core;
using Exiled.API.Interfaces;
using global::Hazards;
@@ -48,6 +49,11 @@ public Hazard(EnvironmentalHazard hazard)
///
public EnvironmentalHazard Base { get; }
+ ///
+ /// Gets the associated with the current Hazard.
+ ///
+ public virtual HazardType Type { get; } = HazardType.Unknown;
+
///
/// Gets or sets the list with all affected by this hazard players.
///
@@ -153,6 +159,13 @@ public static T Get(EnvironmentalHazard environmentalHazard)
/// of based on predicate.
public static IEnumerable Get(Func predicate) => List.Where(predicate);
+ ///
+ /// Gets an of .
+ ///
+ /// The to get.
+ /// of based on type.
+ public static IEnumerable Get(HazardType type) => Get(h => h.Type == type);
+
///
/// Checks if player is in hazard zone.
///
diff --git a/EXILED/Exiled.API/Features/Hazards/SinkholeHazard.cs b/EXILED/Exiled.API/Features/Hazards/SinkholeHazard.cs
index e8e0c4f3a..34cbaacd5 100644
--- a/EXILED/Exiled.API/Features/Hazards/SinkholeHazard.cs
+++ b/EXILED/Exiled.API/Features/Hazards/SinkholeHazard.cs
@@ -7,6 +7,7 @@
namespace Exiled.API.Features.Hazards
{
+ using Exiled.API.Enums;
using global::Hazards;
///
@@ -28,5 +29,8 @@ public SinkholeHazard(SinkholeEnvironmentalHazard hazard)
/// Gets the .
///
public new SinkholeEnvironmentalHazard Base { get; }
+
+ ///
+ public override HazardType Type => HazardType.Sinkhole;
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Features/Hazards/TantrumHazard.cs b/EXILED/Exiled.API/Features/Hazards/TantrumHazard.cs
index 15f54b5ac..906bd4faf 100644
--- a/EXILED/Exiled.API/Features/Hazards/TantrumHazard.cs
+++ b/EXILED/Exiled.API/Features/Hazards/TantrumHazard.cs
@@ -7,7 +7,9 @@
namespace Exiled.API.Features.Hazards
{
+ using Exiled.API.Enums;
using global::Hazards;
+ using Mirror;
using RelativePositioning;
using UnityEngine;
@@ -16,6 +18,8 @@ namespace Exiled.API.Features.Hazards
///
public class TantrumHazard : TemporaryHazard
{
+ private static TantrumEnvironmentalHazard tantrumPrefab;
+
///
/// Initializes a new instance of the class.
///
@@ -26,11 +30,28 @@ public TantrumHazard(TantrumEnvironmentalHazard hazard)
Base = hazard;
}
+ ///
+ /// Gets the tantrum prefab.
+ ///
+ public static TantrumEnvironmentalHazard TantrumPrefab
+ {
+ get
+ {
+ if (tantrumPrefab == null)
+ tantrumPrefab = PrefabHelper.GetPrefab(PrefabType.TantrumObj);
+
+ return tantrumPrefab;
+ }
+ }
+
///
/// Gets the .
///
public new TantrumEnvironmentalHazard Base { get; }
+ ///
+ public override HazardType Type => HazardType.Tantrum;
+
///
/// Gets or sets a value indicating whether or not sizzle should be played.
///
@@ -57,5 +78,28 @@ public Transform CorrectPosition
get => Base._correctPosition;
set => Base._correctPosition = value;
}
+
+ ///
+ /// Places a Tantrum (SCP-173's ability) in the indicated position.
+ ///
+ /// The position where you want to spawn the Tantrum.
+ /// Whether or not the tantrum will apply the effect.
+ /// If is , the tantrum is moved slightly up from its original position. Otherwise, the collision will not be detected and the slowness will not work.
+ /// The instance.
+ public static TantrumHazard PlaceTantrum(Vector3 position, bool isActive = true)
+ {
+ TantrumEnvironmentalHazard tantrum = Object.Instantiate(TantrumPrefab);
+
+ if (!isActive)
+ tantrum.SynchronizedPosition = new(position);
+ else
+ tantrum.SynchronizedPosition = new(position + (Vector3.up * 0.25f));
+
+ tantrum._destroyed = !isActive;
+
+ NetworkServer.Spawn(tantrum.gameObject);
+
+ return Get(tantrum);
+ }
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Features/Map.cs b/EXILED/Exiled.API/Features/Map.cs
index 4e8f2b094..3f1f6fc1a 100644
--- a/EXILED/Exiled.API/Features/Map.cs
+++ b/EXILED/Exiled.API/Features/Map.cs
@@ -28,9 +28,6 @@ namespace Exiled.API.Features
using LightContainmentZoneDecontamination;
using MapGeneration;
using MapGeneration.Distributors;
- using Mirror;
- using PlayerRoles;
- using PlayerRoles.PlayableScps.Scp173;
using PlayerRoles.PlayableScps.Scp939;
using PlayerRoles.Ragdolls;
using RelativePositioning;
@@ -39,8 +36,6 @@ namespace Exiled.API.Features
using Utils.Networking;
using Object = UnityEngine.Object;
- using Scp173GameRole = PlayerRoles.PlayableScps.Scp173.Scp173Role;
- using Scp939GameRole = PlayerRoles.PlayableScps.Scp939.Scp939Role;
///
/// A set of tools to easily handle the in-game map.
@@ -57,48 +52,17 @@ public static class Map
///
internal static readonly List TeleportsValue = new(8);
- private static TantrumEnvironmentalHazard tantrumPrefab;
- private static Scp939AmnesticCloudInstance amnesticCloudPrefab;
-
private static AmbientSoundPlayer ambientSoundPlayer;
///
/// Gets the tantrum prefab.
///
- public static TantrumEnvironmentalHazard TantrumPrefab
- {
- get
- {
- if (tantrumPrefab == null)
- {
- Scp173GameRole scp173Role = (Scp173GameRole)RoleTypeId.Scp173.GetRoleBase();
-
- if (scp173Role.SubroutineModule.TryGetSubroutine(out Scp173TantrumAbility scp173TantrumAbility))
- tantrumPrefab = scp173TantrumAbility._tantrumPrefab;
- }
-
- return tantrumPrefab;
- }
- }
+ public static TantrumEnvironmentalHazard TantrumPrefab => TantrumHazard.TantrumPrefab; // TODO: Remove this.
///
/// Gets the amnestic cloud prefab.
///
- public static Scp939AmnesticCloudInstance AmnesticCloudPrefab
- {
- get
- {
- if (amnesticCloudPrefab == null)
- {
- Scp939GameRole scp939Role = (Scp939GameRole)RoleTypeId.Scp939.GetRoleBase();
-
- if (scp939Role.SubroutineModule.TryGetSubroutine(out Scp939AmnesticCloudAbility ability))
- amnesticCloudPrefab = ability._instancePrefab;
- }
-
- return amnesticCloudPrefab;
- }
- }
+ public static Scp939AmnesticCloudInstance AmnesticCloudPrefab => AmnesticCloudHazard.AmnesticCloudPrefab; // TODO: Remove this.
///
/// Gets a value indicating whether decontamination has begun in the light containment zone.
@@ -281,21 +245,7 @@ public static void PlayAmbientSound(int id)
/// Whether or not the tantrum will apply the effect.
/// If is , the tantrum is moved slightly up from its original position. Otherwise, the collision will not be detected and the slowness will not work.
/// The instance.
- public static TantrumHazard PlaceTantrum(Vector3 position, bool isActive = true)
- {
- TantrumEnvironmentalHazard tantrum = Object.Instantiate(TantrumPrefab);
-
- if (!isActive)
- tantrum.SynchronizedPosition = new RelativePosition(position);
- else
- tantrum.SynchronizedPosition = new RelativePosition(position + (Vector3.up * 0.25f));
-
- tantrum._destroyed = !isActive;
-
- NetworkServer.Spawn(tantrum.gameObject);
-
- return Hazard.Get(tantrum);
- }
+ public static TantrumHazard PlaceTantrum(Vector3 position, bool isActive = true) => TantrumHazard.PlaceTantrum(position, isActive); // TODO: Remove this.
///
/// Destroy all objects.
diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs
index 0a0b6c83e..0885000f7 100644
--- a/EXILED/Exiled.API/Features/Player.cs
+++ b/EXILED/Exiled.API/Features/Player.cs
@@ -3406,7 +3406,7 @@ public void ChangeEffectIntensity(string effectName, byte intensity, float durat
/// Whether or not the tantrum will apply the effect.
/// If is , the tantrum is moved slightly up from its original position. Otherwise, the collision will not be detected and the slowness will not work.
/// The instance..
- public TantrumHazard PlaceTantrum(bool isActive = true) => Map.PlaceTantrum(Position, isActive);
+ public TantrumHazard PlaceTantrum(bool isActive = true) => TantrumHazard.PlaceTantrum(Position, isActive);
///
/// Gives a new to the player.
diff --git a/EXILED/Exiled.API/Features/PrefabHelper.cs b/EXILED/Exiled.API/Features/PrefabHelper.cs
index de97e15d5..fd18e9fe9 100644
--- a/EXILED/Exiled.API/Features/PrefabHelper.cs
+++ b/EXILED/Exiled.API/Features/PrefabHelper.cs
@@ -41,6 +41,21 @@ public static PrefabAttribute GetPrefabAttribute(this PrefabType prefabType)
return type.GetField(Enum.GetName(type, prefabType)).GetCustomAttribute();
}
+ ///
+ /// Gets the prefab of the specified .
+ ///
+ /// The to get prefab of.
+ /// The to get.
+ /// Returns the prefab component as {T}.
+ public static T GetPrefab(PrefabType type)
+ where T : Component
+ {
+ if (!Stored.TryGetValue(type, out GameObject gameObject) || !gameObject.TryGetComponent(out T component))
+ return null;
+
+ return component;
+ }
+
///
/// Spawns a prefab on server.
///
@@ -68,9 +83,7 @@ public static GameObject Spawn(PrefabType prefabType, Vector3 position = default
public static T Spawn(PrefabType prefabType, Vector3 position = default, Quaternion rotation = default)
where T : Component
{
- if (!Stored.TryGetValue(prefabType, out GameObject gameObject) || !gameObject.TryGetComponent(out T component))
- return null;
- T obj = UnityEngine.Object.Instantiate(component, position, rotation);
+ T obj = UnityEngine.Object.Instantiate(GetPrefab(prefabType), position, rotation);
NetworkServer.Spawn(obj.gameObject);
return obj;
}
diff --git a/EXILED/Exiled.API/Features/Roles/FpcRole.cs b/EXILED/Exiled.API/Features/Roles/FpcRole.cs
index dcf4e27cb..4ca71d8c8 100644
--- a/EXILED/Exiled.API/Features/Roles/FpcRole.cs
+++ b/EXILED/Exiled.API/Features/Roles/FpcRole.cs
@@ -50,9 +50,18 @@ protected FpcRole(FpcStandardRoleBase baseRole)
public FpcStandardRoleBase FirstPersonController { get; }
///
- /// Gets or sets the player's relative position.
+ /// Gets or sets the player's relative position as perceived by the server.
///
public RelativePosition RelativePosition
+ {
+ get => new(Owner.Position);
+ set => Owner.Position = value.Position;
+ }
+
+ ///
+ /// Gets or sets the player's relative position as perceived by the client.
+ ///
+ public RelativePosition ClientRelativePosition
{
get => FirstPersonController.FpcModule.Motor.ReceivedPosition;
set => FirstPersonController.FpcModule.Motor.ReceivedPosition = value;
diff --git a/EXILED/Exiled.API/Features/Server.cs b/EXILED/Exiled.API/Features/Server.cs
index 908096434..b1b05e8c1 100644
--- a/EXILED/Exiled.API/Features/Server.cs
+++ b/EXILED/Exiled.API/Features/Server.cs
@@ -111,6 +111,15 @@ public static string Name
///
public static double Tps => Math.Round(1f / Time.smoothDeltaTime);
+ ///
+ /// Gets or sets the max ticks per second of the server.
+ ///
+ public static short MaxTps
+ {
+ get => ServerStatic.ServerTickrate;
+ set => ServerStatic.ServerTickrate = value;
+ }
+
///
/// Gets the actual frametime of the server.
///
diff --git a/EXILED/Exiled.Events/Commands/TpsCommand.cs b/EXILED/Exiled.Events/Commands/TpsCommand.cs
new file mode 100644
index 000000000..2d7412919
--- /dev/null
+++ b/EXILED/Exiled.Events/Commands/TpsCommand.cs
@@ -0,0 +1,46 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Commands
+{
+ using System;
+
+ using CommandSystem;
+ using Exiled.API.Features;
+
+ ///
+ /// Command for showing current server TPS.
+ ///
+ [CommandHandler(typeof(RemoteAdminCommandHandler))]
+ [CommandHandler(typeof(GameConsoleCommandHandler))]
+ public class TpsCommand : ICommand
+ {
+ ///
+ public string Command { get; } = "tps";
+
+ ///
+ public string[] Aliases { get; } = Array.Empty();
+
+ ///
+ public string Description { get; } = "Shows the current TPS of the server";
+
+ ///
+ public bool Execute(ArraySegment arguments, ICommandSender sender, out string response)
+ {
+ double diff = Server.Tps / Server.MaxTps;
+ string color = diff switch
+ {
+ > 0.9 => "green",
+ > 0.5 => "yellow",
+ _ => "red"
+ };
+
+ response = $"{Server.Tps}/{Server.MaxTps}";
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/EventArgs/Player/ChangingRoleEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ChangingRoleEventArgs.cs
index 552d70a73..c8ab4d9ba 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/ChangingRoleEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/ChangingRoleEventArgs.cs
@@ -11,11 +11,10 @@ namespace Exiled.Events.EventArgs.Player
using API.Enums;
using API.Features;
+ using Exiled.API.Extensions;
using Exiled.API.Features.Pools;
using Interfaces;
-
- using InventorySystem.Configs;
-
+ using InventorySystem;
using PlayerRoles;
///
@@ -70,17 +69,16 @@ public RoleTypeId NewRole
get => newRole;
set
{
- if (StartingInventories.DefinedInventories.ContainsKey(value))
- {
- Items.Clear();
- Ammo.Clear();
+ InventoryRoleInfo inventory = value.GetInventory();
+
+ Items.Clear();
+ Ammo.Clear();
- foreach (ItemType itemType in StartingInventories.DefinedInventories[value].Items)
- Items.Add(itemType);
+ foreach (ItemType itemType in inventory.Items)
+ Items.Add(itemType);
- foreach (KeyValuePair ammoPair in StartingInventories.DefinedInventories[value].Ammo)
- Ammo.Add(ammoPair.Key, ammoPair.Value);
- }
+ foreach (KeyValuePair ammoPair in inventory.Ammo)
+ Ammo.Add(ammoPair.Key, ammoPair.Value);
newRole = value;
}
From fada3676d72f58936cbf37680d0bc50bcb110ab6 Mon Sep 17 00:00:00 2001
From: ZeroTwo <63092138+NotZer0Two@users.noreply.github.com>
Date: Sun, 11 Aug 2024 13:31:52 +0200
Subject: [PATCH 12/39] `[EXILED::API]` Pickup::Category (#46)
* Item Category on Pickup
* Fixed Build Error
---
EXILED/Exiled.API/Features/Pickups/Pickup.cs | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/EXILED/Exiled.API/Features/Pickups/Pickup.cs b/EXILED/Exiled.API/Features/Pickups/Pickup.cs
index 3f7373dee..6c600dd4c 100644
--- a/EXILED/Exiled.API/Features/Pickups/Pickup.cs
+++ b/EXILED/Exiled.API/Features/Pickups/Pickup.cs
@@ -11,6 +11,7 @@ namespace Exiled.API.Features.Pickups
using System.Collections.Generic;
using System.Linq;
+ using Exiled.API.Extensions;
using Exiled.API.Features.Core;
using Exiled.API.Features.Pickups.Projectiles;
using Exiled.API.Interfaces;
@@ -208,6 +209,11 @@ public float PickupTime
///
public ItemType Type => Base.NetworkInfo.ItemId;
+ ///
+ /// Gets the of the item.
+ ///
+ public ItemCategory Category => Type.GetCategory();
+
///
/// Gets or sets a value indicating whether the pickup is locked (can't be picked up).
///
From 2ee9f467af6b4eceaa83372cd273249a77760bff Mon Sep 17 00:00:00 2001
From: ZeroTwo <63092138+NotZer0Two@users.noreply.github.com>
Date: Sun, 11 Aug 2024 19:29:08 +0200
Subject: [PATCH 13/39] Fix (#50)
---
EXILED/Exiled.CreditTags/Features/DatabaseHandler.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/EXILED/Exiled.CreditTags/Features/DatabaseHandler.cs b/EXILED/Exiled.CreditTags/Features/DatabaseHandler.cs
index d052b914c..e31fe2615 100644
--- a/EXILED/Exiled.CreditTags/Features/DatabaseHandler.cs
+++ b/EXILED/Exiled.CreditTags/Features/DatabaseHandler.cs
@@ -1,4 +1,4 @@
-// -----------------------------------------------------------------------
+// -----------------------------------------------------------------------
//
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
@@ -17,7 +17,7 @@ namespace Exiled.CreditTags.Features
public static class DatabaseHandler
{
- private const string Url = "https://raw.githubusercontent.com/Exiled-Official/CreditTags/main/data.yml";
+ private const string Url = "https://raw.githubusercontent.com/ExMod-Team/CreditTags/main/data.yml";
private const string ETagCacheFileName = "etag_cache.txt";
private const string DatabaseCacheFileName = "data.yml";
private const int CacheTimeInMinutes = 5;
From 8b8eb963abbb09b29eb1437a786ec4d068a3b921 Mon Sep 17 00:00:00 2001
From: Misaka-ZeroTwo <45165615+Misaka-ZeroTwo@users.noreply.github.com>
Date: Mon, 12 Aug 2024 02:38:44 +0800
Subject: [PATCH 14/39] Update push_nuget.yml
---
.github/workflows/push_nuget.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/push_nuget.yml b/.github/workflows/push_nuget.yml
index 72e45189e..3e5cf7c46 100644
--- a/.github/workflows/push_nuget.yml
+++ b/.github/workflows/push_nuget.yml
@@ -10,7 +10,7 @@ defaults:
working-directory: ./EXILED
env:
- EXILED_REFERENCES_URL: https://Exiled-Official.github.io/SL-References/Master.zip
+ EXILED_REFERENCES_URL: https://ExMod-Team.github.io/SL-References/Master.zip
EXILED_REFERENCES_PATH: ${{ github.workspace }}/EXILED/References
jobs:
From 7de6428fe8d3cb0233aa2404cee58b4d362beb54 Mon Sep 17 00:00:00 2001
From: Misaka-ZeroTwo <45165615+Misaka-ZeroTwo@users.noreply.github.com>
Date: Mon, 12 Aug 2024 02:53:55 +0800
Subject: [PATCH 15/39] v8.12.0-rc.1 (#52)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* uwu (#5)
* AdminToy.List (#18)
* AdminToy.List
* Better AdminToy::Get()
* Update EXILED/Exiled.API/Features/Toys/AdminToy.cs
Co-authored-by: IRacle <79921583+IRacle1@users.noreply.github.com>
* TODO
* Fix Error
* Fix2
---------
Co-authored-by: IRacle <79921583+IRacle1@users.noreply.github.com>
* Fix `Jailbird::WearState` (#12)
* Jailbird
* Fix
* Exception
* Fix NW moment
* Porting EXILED9 RespawnedTeam event. by.VALERA771 (#27)
https://github.com/Exiled-Team/EXILED/pull/2386
* Fix not returning null (#22)
* Fix not returning null
* Apply suggestions from code review
Co-authored-by: Jesus QC <69375249+Jesus-QC@users.noreply.github.com>
* Little modification
---------
Co-authored-by: Jesus QC <69375249+Jesus-QC@users.noreply.github.com>
* RecontainedEventArgs more feature (#20)
* RecontainedEventArgs more feature
* Fix Naming
* grammar
* Update RecontainedEventArgs.cs
---------
Co-authored-by: Misaka-ZeroTwo <45165615+Misaka-ZeroTwo@users.noreply.github.com>
* InteractingScp330.cs: Reduction of bloat code from original design. (#30)
* Should reduce bloat code that was required years ago.
* Should reduce bloat code that was required years ago.
* Added back per Yamato's request
---------
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* IScp330Event (#11)
Co-authored-by: Misaka-ZeroTwo <45165615+Misaka-ZeroTwo@users.noreply.github.com>
* PR than i made (#9)
* PR made by me
Thanks Ika for the Help on CustomAmmo/CategoryLimit
Co-Authored-By: Ika <36999341+IkaOverride@users.noreply.github.com>
* Build error
* Added support to SCPs for escaping-related events.
* spacing
* LocalReporting Exiled should be call before NWAPI
* Optimising / More documentation on SpawningItem
* ISpawnableScp
* Use of ComponentsEqualityComparer for Dictionary
---------
Co-authored-by: Ika <36999341+IkaOverride@users.noreply.github.com>
* [Events] Fix null reference (#15)
* fix situation when `ply == null`
* lol why
---------
Co-authored-by: IRacle
* Offline mode support (#19)
* Fix Offline-mode breaking everything
* Add `offline` authentication type and append `@offline` to UserIds during offline mode
* Add offline id support to Player.Get
* Comment transpilers
---------
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Fix NW bugs (#32)
* Fix Armor Drop
from https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/230
* add comments
* fix scp173 and adding bug report link to summary class
fix https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/143
* Moved patches and fixed Scp173FirstKillPatch
* Add Slowness Fix
Avoid values more than 100 for effect slowness to fix https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/378
* skill issue
* skill issue (again=
---------
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Useless Event (939 Placed Amnestic Cloud) (#40)
* Fix custom role classes giving custom ammo when they are not suppose to (#24)
* Fix custom role classes giving custom ammmo when they are not suppose to
* change to using EnumUtils
* Moved Ammo additions into Inventory call delayed
* `Item::Get()` and `Pickup::Get()` (#17)
* `Item::::Get()` and `Pickup::Get()`
* Revert doc change
* Add `Hazard::Get()`
* doc fix
* `Door::Get()`
* AdminToy.Get()
* More implementation
* WeirdFix
* simplify Scp244Spawning patch and AddedComment & NO IL error
* Remove Log.info
* DroppingItem light modifiication
* Moving Item.Get inside the eventargs instead of transpiller
* FixNpcNoclip (#34)
* FixNpcNoclip
* oups
* .
* virtual / override
* Implements more patches for RemovingHandcuffs event and adding RemovedHandcuffs event (#3)
* Update labeler.yml
* RemovingHandcuffs event
* Update UncuffReason.cs
* New event
* docs
* Interacting scp330 compile fix (#43)
* My scp built with no issues.. no idea why.
* Fixes issue
* Harmony suppresses NON harmony errors.
---------
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Trello is no more & more NW Fix & Fix IL Code on dev (#28)
* Trello Is Replaced by gitlab
* also this one
* Fix106RegenerationWithScp244
* Report To NW
* .
* Scp3114FriendlyFireFix
* Fix
* yamatotototo
* Fix
* Fix for building dev + TODO than i just seen
* Fix Undid patch
* fIX
* Fix Client Crash Issue
---------
Co-authored-by: IRacle
* Additions (#29)
* Add a bunch
* Fix
* Security
* Make changes
* remove unused usings
* Getting inventory
* oops
* Dev commit
* use exiled
---------
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* `[EXILED::API]` Pickup::Category (#46)
* Item Category on Pickup
* Fixed Build Error
* Fix (#50)
---------
Co-authored-by: Jesus QC <69375249+Jesus-QC@users.noreply.github.com>
Co-authored-by: VALERA771 <72030575+VALERA771@users.noreply.github.com>
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
Co-authored-by: IRacle <79921583+IRacle1@users.noreply.github.com>
Co-authored-by: 永安404 <101850798+YongAn404@users.noreply.github.com>
Co-authored-by: X <24619207+Undid-Iridium@users.noreply.github.com>
Co-authored-by: Ika <36999341+IkaOverride@users.noreply.github.com>
Co-authored-by: IRacle
Co-authored-by: x3rt
Co-authored-by: sky <99112969+skyyt15@users.noreply.github.com>
Co-authored-by: TtroubleTT <121741230+TtroubleTT@users.noreply.github.com>
Co-authored-by: Nameless <85962933+Misfiy@users.noreply.github.com>
Co-authored-by: ZeroTwo <63092138+NotZer0Two@users.noreply.github.com>
---
EXILED/Exiled.API/Enums/AuthenticationType.cs | 5 +
EXILED/Exiled.API/Enums/HazardType.cs | 37 +++
EXILED/Exiled.API/Enums/UncuffReason.cs | 30 +++
.../Extensions/BitwiseExtensions.cs | 63 +++++
.../Exiled.API/Extensions/MirrorExtensions.cs | 9 +-
.../Exiled.API/Extensions/RoleExtensions.cs | 23 +-
.../Exiled.API/Extensions/StringExtensions.cs | 6 +-
EXILED/Exiled.API/Features/Camera.cs | 2 +-
.../Features/Doors/AirlockController.cs | 2 +-
EXILED/Exiled.API/Features/Doors/Door.cs | 59 +++--
EXILED/Exiled.API/Features/Generator.cs | 2 +-
.../Features/Hazards/AmnesticCloudHazard.cs | 22 +-
EXILED/Exiled.API/Features/Hazards/Hazard.cs | 24 +-
.../Features/Hazards/SinkholeHazard.cs | 4 +
.../Features/Hazards/TantrumHazard.cs | 44 ++++
EXILED/Exiled.API/Features/Items/Ammo.cs | 2 +-
.../Features/Items/ExplosiveGrenade.cs | 2 +-
.../Exiled.API/Features/Items/FlashGrenade.cs | 2 +-
EXILED/Exiled.API/Features/Items/Item.cs | 47 +++-
EXILED/Exiled.API/Features/Items/Jailbird.cs | 30 ++-
EXILED/Exiled.API/Features/Items/Scp018.cs | 2 +-
EXILED/Exiled.API/Features/Items/Scp2176.cs | 2 +-
EXILED/Exiled.API/Features/Items/Scp330.cs | 4 +-
EXILED/Exiled.API/Features/Items/Throwable.cs | 2 +-
EXILED/Exiled.API/Features/Lift.cs | 6 +-
EXILED/Exiled.API/Features/Map.cs | 63 +----
EXILED/Exiled.API/Features/Npc.cs | 15 +-
EXILED/Exiled.API/Features/Pickups/Pickup.cs | 58 +++++
.../Pickups/Projectiles/Projectile.cs | 43 +++-
EXILED/Exiled.API/Features/Player.cs | 213 +++++++++++++++--
EXILED/Exiled.API/Features/PrefabHelper.cs | 19 +-
EXILED/Exiled.API/Features/Ragdoll.cs | 2 +-
EXILED/Exiled.API/Features/Recontainer.cs | 5 +
EXILED/Exiled.API/Features/Roles/FpcRole.cs | 27 ++-
.../Exiled.API/Features/Roles/Scp049Role.cs | 2 +-
.../Exiled.API/Features/Roles/Scp079Role.cs | 3 +-
.../Exiled.API/Features/Roles/Scp096Role.cs | 3 +-
.../Exiled.API/Features/Roles/Scp106Role.cs | 3 +-
.../Exiled.API/Features/Roles/Scp173Role.cs | 3 +-
.../Exiled.API/Features/Roles/Scp3114Role.cs | 2 +-
.../Exiled.API/Features/Roles/Scp939Role.cs | 3 +-
EXILED/Exiled.API/Features/Room.cs | 2 +-
EXILED/Exiled.API/Features/Server.cs | 9 +
EXILED/Exiled.API/Features/TeslaGate.cs | 4 +-
EXILED/Exiled.API/Features/Toys/AdminToy.cs | 42 +++-
EXILED/Exiled.API/Features/Window.cs | 2 +-
.../Features/DatabaseHandler.cs | 4 +-
.../Patches/PlayerInventorySee.cs | 3 +-
.../API/Features/CustomRole.cs | 29 +--
EXILED/Exiled.Events/Commands/TpsCommand.cs | 46 ++++
.../EventArgs/Interfaces/IHazardEvent.cs | 2 +-
.../EventArgs/Interfaces/IScp330Event.cs | 22 ++
.../EventArgs/Interfaces/IUsableEvent.cs | 4 +-
.../Item/ChangingAttachmentsEventArgs.cs | 14 +-
.../Item/ChargingJailbirdEventArgs.cs | 11 +-
.../EventArgs/Item/SwingingEventArgs.cs | 9 +-
.../Map/ExplodingGrenadeEventArgs.cs | 4 +-
.../EventArgs/Map/Scp244SpawningEventArgs.cs | 13 +-
.../Player/CancellingItemUseEventArgs.cs | 2 +-
.../EventArgs/Player/ChangingItemEventArgs.cs | 2 +-
.../Player/ChangingMicroHIDStateEventArgs.cs | 2 +-
.../Player/ChangingRadioPresetEventArgs.cs | 2 +-
.../EventArgs/Player/ChangingRoleEventArgs.cs | 22 +-
.../EventArgs/Player/DroppedItemEventArgs.cs | 5 +-
.../Player/LocalReportingEventArgs.cs | 2 +-
.../Player/RemovedHandcuffsEventArgs.cs | 47 ++++
.../Player/RemovingHandcuffsEventArgs.cs | 13 +-
.../Player/ThrowingRequestEventArgs.cs | 2 +-
.../Player/ThrownProjectileEventArgs.cs | 4 +-
.../Player/TogglingFlashlightEventArgs.cs | 2 +-
.../Player/TogglingRadioEventArgs.cs | 2 +-
.../Player/UsingMicroHIDEnergyEventArgs.cs | 2 +-
.../Player/UsingRadioBatteryEventArgs.cs | 2 +-
.../Scp049/FinishingRecallEventArgs.cs | 2 +-
.../EventArgs/Scp079/RecontainedEventArgs.cs | 25 +-
.../Scp096/StartPryingGateEventArgs.cs | 2 +-
.../EventArgs/Scp244/UsingScp244EventArgs.cs | 2 +-
.../Scp330/DroppingScp330EventArgs.cs | 11 +-
.../EventArgs/Scp330/EatenScp330EventArgs.cs | 14 +-
.../EventArgs/Scp330/EatingScp330EventArgs.cs | 16 +-
.../Scp330/InteractingScp330EventArgs.cs | 14 +-
.../Scp939/PlacedAmnesticCloudEventArgs.cs | 51 ++++
.../Server/RespawnedTeamEventArgs.cs | 43 ++++
EXILED/Exiled.Events/Events.cs | 5 +-
.../Handlers/Internal/MapGenerated.cs | 2 +-
.../Exiled.Events/Handlers/Internal/Round.cs | 2 +-
.../Handlers/Internal/SceneUnloaded.cs | 4 +-
EXILED/Exiled.Events/Handlers/Player.cs | 11 +
EXILED/Exiled.Events/Handlers/Scp939.cs | 11 +
EXILED/Exiled.Events/Handlers/Server.cs | 15 ++
.../Events/Item/ChangingAttachments.cs | 7 +-
.../Patches/Events/Map/Scp244Spawning.cs | 39 ++-
.../Patches/Events/Map/SpawningItem.cs | 17 +-
.../Patches/Events/Player/DroppingItem.cs | 7 +-
.../Patches/Events/Player/Escaping.cs | 27 ++-
.../Events/Player/FirearmRequestReceived.cs | 3 +-
.../Patches/Events/Player/Healing.cs | 17 +-
.../Events/Player/ProcessDisarmMessage.cs | 224 +++++++++++++++++-
.../Patches/Events/Player/Verified.cs | 56 ++++-
.../Patches/Events/Scp049/FinishingRecall.cs | 5 +-
.../Patches/Events/Scp079/Recontain.cs | 3 +-
.../Patches/Events/Scp330/EatingScp330.cs | 10 +-
.../Events/Scp330/InteractingScp330.cs | 91 +------
.../Events/Scp939/PlacedAmnesticCloud.cs | 73 ++++++
.../Patches/Events/Server/Reporting.cs | 4 +-
.../Patches/Fixes/ArmorDropPatch.cs | 55 +++++
.../Fixes/Fix106RegenerationWithScp244.cs | 72 ++++++
.../Patches/Fixes/GrenadePropertiesFix.cs | 3 +-
.../Patches/Fixes/Jailbird914CoarseFix.cs | 61 +++++
.../Patches/Fixes/NWFixScp096BreakingDoor.cs | 2 +-
.../Patches/Fixes/Scp173FirstKillPatch.cs | 55 +++++
.../Patches/Fixes/Scp173SecondKillPatch.cs | 54 +++++
.../Patches/Fixes/Scp3114AttackAhpFix.cs | 2 +-
.../Patches/Fixes/Scp3114FriendlyFireFix.cs | 124 ++++++++++
.../Patches/Fixes/SlownessFix.cs | 4 +-
.../Patches/Fixes/WarheadConfigLockGateFix.cs | 45 ++++
.../Exiled.Events/Patches/Generic/DoorList.cs | 2 +-
.../Patches/Generic/GetCustomAmmoLimit.cs | 35 +++
.../Patches/Generic/GetCustomCategoryLimit.cs | 34 +++
.../Patches/Generic/OfflineModeIds.cs | 164 +++++++++++++
.../Patches/Generic/PickupControlPatch.cs | 7 +-
121 files changed, 2351 insertions(+), 407 deletions(-)
create mode 100644 EXILED/Exiled.API/Enums/HazardType.cs
create mode 100644 EXILED/Exiled.API/Enums/UncuffReason.cs
create mode 100644 EXILED/Exiled.API/Extensions/BitwiseExtensions.cs
create mode 100644 EXILED/Exiled.Events/Commands/TpsCommand.cs
create mode 100644 EXILED/Exiled.Events/EventArgs/Interfaces/IScp330Event.cs
create mode 100644 EXILED/Exiled.Events/EventArgs/Player/RemovedHandcuffsEventArgs.cs
create mode 100644 EXILED/Exiled.Events/EventArgs/Scp939/PlacedAmnesticCloudEventArgs.cs
create mode 100644 EXILED/Exiled.Events/EventArgs/Server/RespawnedTeamEventArgs.cs
create mode 100644 EXILED/Exiled.Events/Patches/Events/Scp939/PlacedAmnesticCloud.cs
create mode 100644 EXILED/Exiled.Events/Patches/Fixes/ArmorDropPatch.cs
create mode 100644 EXILED/Exiled.Events/Patches/Fixes/Fix106RegenerationWithScp244.cs
create mode 100644 EXILED/Exiled.Events/Patches/Fixes/Jailbird914CoarseFix.cs
create mode 100644 EXILED/Exiled.Events/Patches/Fixes/Scp173FirstKillPatch.cs
create mode 100644 EXILED/Exiled.Events/Patches/Fixes/Scp173SecondKillPatch.cs
create mode 100644 EXILED/Exiled.Events/Patches/Fixes/Scp3114FriendlyFireFix.cs
create mode 100644 EXILED/Exiled.Events/Patches/Fixes/WarheadConfigLockGateFix.cs
create mode 100644 EXILED/Exiled.Events/Patches/Generic/GetCustomAmmoLimit.cs
create mode 100644 EXILED/Exiled.Events/Patches/Generic/GetCustomCategoryLimit.cs
create mode 100644 EXILED/Exiled.Events/Patches/Generic/OfflineModeIds.cs
diff --git a/EXILED/Exiled.API/Enums/AuthenticationType.cs b/EXILED/Exiled.API/Enums/AuthenticationType.cs
index 9de49f902..0590ea097 100644
--- a/EXILED/Exiled.API/Enums/AuthenticationType.cs
+++ b/EXILED/Exiled.API/Enums/AuthenticationType.cs
@@ -42,5 +42,10 @@ public enum AuthenticationType
/// Indicates that the player has been authenticated as DedicatedServer.
///
DedicatedServer,
+
+ ///
+ /// Indicates that the player has been authenticated during Offline mode.
+ ///
+ Offline,
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Enums/HazardType.cs b/EXILED/Exiled.API/Enums/HazardType.cs
new file mode 100644
index 000000000..f05f8852f
--- /dev/null
+++ b/EXILED/Exiled.API/Enums/HazardType.cs
@@ -0,0 +1,37 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.API.Enums
+{
+ using Exiled.API.Features.Hazards;
+
+ ///
+ /// Unique identifier for a .
+ ///
+ public enum HazardType
+ {
+ ///
+ /// SCP-939 amnestic cloud.
+ ///
+ AmnesticCloud,
+
+ ///
+ /// Sinkhole spawned at start of round.
+ ///
+ Sinkhole,
+
+ ///
+ /// SCP-173 tantrum.
+ ///
+ Tantrum,
+
+ ///
+ /// Should never happen
+ ///
+ Unknown,
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Enums/UncuffReason.cs b/EXILED/Exiled.API/Enums/UncuffReason.cs
new file mode 100644
index 000000000..6b3018fda
--- /dev/null
+++ b/EXILED/Exiled.API/Enums/UncuffReason.cs
@@ -0,0 +1,30 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.API.Enums
+{
+ ///
+ /// Reasons that player gets uncuffed.
+ ///
+ public enum UncuffReason
+ {
+ ///
+ /// Uncuffed by a player.
+ ///
+ Player,
+
+ ///
+ /// Uncuffed due to the distance between cuffer and target.
+ ///
+ OutOfRange,
+
+ ///
+ /// Uncuffed due to the cuffer no longer alive.
+ ///
+ CufferDied,
+ }
+}
diff --git a/EXILED/Exiled.API/Extensions/BitwiseExtensions.cs b/EXILED/Exiled.API/Extensions/BitwiseExtensions.cs
new file mode 100644
index 000000000..2f8473784
--- /dev/null
+++ b/EXILED/Exiled.API/Extensions/BitwiseExtensions.cs
@@ -0,0 +1,63 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.API.Extensions
+{
+ using System;
+
+ ///
+ /// Extensions for bitwise operations.
+ ///
+ public static class BitwiseExtensions
+ {
+ ///
+ /// Adds the specified flags to the given enum value.
+ ///
+ /// The type of the enum.
+ /// The enum value to add flags to.
+ /// The flags to add.
+ /// The enum value with the specified flags added.
+ public static T AddFlags(this T flags, params T[] newFlags)
+ where T : Enum => flags.ModifyFlags(true, newFlags);
+
+ ///
+ /// Removes the specified flags from the given enum value.
+ ///
+ /// The type of the enum.
+ /// The enum value to remove flags from.
+ /// The flags to remove.
+ /// The enum value with the specified flags removed.
+ public static T RemoveFlags(this T flags, params T[] oldFlags)
+ where T : Enum => flags.ModifyFlags(false, oldFlags);
+
+ ///
+ /// Sets the specified flag to the given value, default is true.
+ ///
+ /// The flags enum to modify.
+ /// The value to set the flag to.
+ /// The flags to modify.
+ /// The type of the enum.
+ /// The flags enum with the flag set to the given value.
+ public static T ModifyFlags(this T flags, bool value, params T[] changeFlags)
+ where T : Enum
+ {
+ long currentValue = Convert.ToInt64(flags);
+
+ foreach (T flag in changeFlags)
+ {
+ long flagValue = Convert.ToInt64(flag);
+
+ if (value)
+ currentValue |= flagValue;
+ else
+ currentValue &= ~flagValue;
+ }
+
+ return (T)Enum.ToObject(typeof(T), currentValue);
+ }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
index 2dd66766c..e85b54ed6 100644
--- a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
+++ b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
@@ -435,12 +435,11 @@ public static void SendFakeTargetRpc(Player target, NetworkIdentity behaviorOwne
///
/// EffectOnlySCP207.
///
- /// MirrorExtensions.SendCustomSync(player, player.ReferenceHub.networkIdentity, typeof(PlayerEffectsController), (writer) => {
- /// writer.WriteUInt64(1ul); // DirtyObjectsBit
- /// writer.WriteUInt32(1); // DirtyIndexCount
+ /// MirrorExtensions.SendFakeSyncObject(player, player.NetworkIdentity, typeof(PlayerEffectsController), (writer) => {
+ /// writer.WriteULong(1ul); // DirtyObjectsBit
+ /// writer.WriteUInt(1); // DirtyIndexCount
/// writer.WriteByte((byte)SyncList<byte>.Operation.OP_SET); // Operations
- /// writer.WriteUInt32(17); // EditIndex
- /// writer.WriteByte(1); // Value
+ /// writer.WriteUInt(17); // EditIndex
/// });
///
///
diff --git a/EXILED/Exiled.API/Extensions/RoleExtensions.cs b/EXILED/Exiled.API/Extensions/RoleExtensions.cs
index e9186ba71..c674af1bf 100644
--- a/EXILED/Exiled.API/Extensions/RoleExtensions.cs
+++ b/EXILED/Exiled.API/Extensions/RoleExtensions.cs
@@ -140,18 +140,22 @@ public static SpawnLocation GetRandomSpawnLocation(this RoleTypeId roleType)
return null;
}
+ ///
+ /// Gets the starting of a .
+ ///
+ /// The .
+ /// The that the role receives on spawn.
+ public static InventoryRoleInfo GetInventory(this RoleTypeId role)
+ => StartingInventories.DefinedInventories.TryGetValue(role, out InventoryRoleInfo info)
+ ? info
+ : new(Array.Empty(), new());
+
///
/// Gets the starting items of a .
///
/// The .
/// An of that the role receives on spawn. Will be empty for classes that do not spawn with items.
- public static ItemType[] GetStartingInventory(this RoleTypeId roleType)
- {
- if (StartingInventories.DefinedInventories.TryGetValue(roleType, out InventoryRoleInfo info))
- return info.Items;
-
- return Array.Empty();
- }
+ public static ItemType[] GetStartingInventory(this RoleTypeId roleType) => GetInventory(roleType).Items;
///
/// Gets the starting ammo of a .
@@ -160,10 +164,9 @@ public static ItemType[] GetStartingInventory(this RoleTypeId roleType)
/// An of that the role receives on spawn. Will be empty for classes that do not spawn with ammo.
public static Dictionary GetStartingAmmo(this RoleTypeId roleType)
{
- if (StartingInventories.DefinedInventories.TryGetValue(roleType, out InventoryRoleInfo info))
- return info.Ammo.ToDictionary(kvp => kvp.Key.GetAmmoType(), kvp => kvp.Value);
+ InventoryRoleInfo info = roleType.GetInventory();
- return new();
+ return info.Ammo.ToDictionary(kvp => kvp.Key.GetAmmoType(), kvp => kvp.Value);
}
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Extensions/StringExtensions.cs b/EXILED/Exiled.API/Extensions/StringExtensions.cs
index 69b184bc6..37d1a6777 100644
--- a/EXILED/Exiled.API/Extensions/StringExtensions.cs
+++ b/EXILED/Exiled.API/Extensions/StringExtensions.cs
@@ -161,7 +161,11 @@ public static string GetBefore(this string input, char symbol)
///
/// The user id.
/// Returns the raw user id.
- public static string GetRawUserId(this string userId) => userId.Substring(0, userId.LastIndexOf('@'));
+ public static string GetRawUserId(this string userId)
+ {
+ int index = userId.IndexOf('@');
+ return index == -1 ? userId : userId.Substring(0, index);
+ }
///
/// Gets a SHA256 hash of a player's user id without the authentication.
diff --git a/EXILED/Exiled.API/Features/Camera.cs b/EXILED/Exiled.API/Features/Camera.cs
index 67f5d7418..56e7e7f24 100644
--- a/EXILED/Exiled.API/Features/Camera.cs
+++ b/EXILED/Exiled.API/Features/Camera.cs
@@ -28,7 +28,7 @@ public class Camera : IWrapper, IWorldSpace
///
/// A containing all known s and their corresponding .
///
- internal static readonly Dictionary Camera079ToCamera = new(250);
+ internal static readonly Dictionary Camera079ToCamera = new(250, new ComponentsEqualityComparer());
private static readonly Dictionary NameToCameraType = new()
{
diff --git a/EXILED/Exiled.API/Features/Doors/AirlockController.cs b/EXILED/Exiled.API/Features/Doors/AirlockController.cs
index 62645a392..eceda5f31 100644
--- a/EXILED/Exiled.API/Features/Doors/AirlockController.cs
+++ b/EXILED/Exiled.API/Features/Doors/AirlockController.cs
@@ -20,7 +20,7 @@ public class AirlockController
///
/// A containing all known 's and their corresponding .
///
- internal static readonly Dictionary BaseToExiledControllers = new();
+ internal static readonly Dictionary BaseToExiledControllers = new(new ComponentsEqualityComparer());
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Doors/Door.cs b/EXILED/Exiled.API/Features/Doors/Door.cs
index 584dc3fb0..c06a13b9b 100644
--- a/EXILED/Exiled.API/Features/Doors/Door.cs
+++ b/EXILED/Exiled.API/Features/Doors/Door.cs
@@ -14,7 +14,9 @@ namespace Exiled.API.Features.Doors
using Exiled.API.Enums;
using Exiled.API.Extensions;
using Exiled.API.Features.Core;
+ using Exiled.API.Features.Hazards;
using Exiled.API.Interfaces;
+ using global::Hazards;
using Interactables.Interobjects;
using Interactables.Interobjects.DoorUtils;
using MEC;
@@ -38,7 +40,7 @@ public class Door : TypeCastObject, IWrapper, IWorldSpace
///
/// A containing all known 's and their corresponding .
///
- internal static readonly Dictionary DoorVariantToDoor = new();
+ internal static readonly Dictionary DoorVariantToDoor = new(new ComponentsEqualityComparer());
///
/// Initializes a new instance of the class.
@@ -309,6 +311,31 @@ public static Door Get(DoorVariant doorVariant)
return DoorVariantToDoor[doorVariant];
}
+ ///
+ /// Gets the by .
+ ///
+ /// The to convert into an door.
+ /// The specified type.
+ /// The door wrapper for the given .
+ public static T Get(DoorVariant doorVariant)
+ where T : Door => Get(doorVariant) as T;
+
+ ///
+ /// Gets a given the specified .
+ ///
+ /// The to search for.
+ /// The with the given or if not found.
+ public static Door Get(DoorType doorType) => List.FirstOrDefault(x => x.Type == doorType);
+
+ ///
+ /// Gets the by .
+ ///
+ /// The to convert into an door.
+ /// The specified type.
+ /// The door wrapper for the given .
+ public static T Get(DoorType doorType)
+ where T : Door => Get(doorType) as T;
+
///
/// Gets a given the specified name.
///
@@ -320,6 +347,15 @@ public static Door Get(string name)
return nameExtension is null ? null : Get(nameExtension.TargetDoor);
}
+ ///
+ /// Gets the by .
+ ///
+ /// The name to search for.
+ /// The specified type.
+ /// The door wrapper for the given .
+ public static T Get(string name)
+ where T : Door => Get(name) as T;
+
///
/// Gets the door object associated with a specific , or creates a new one if there isn't one.
///
@@ -327,20 +363,6 @@ public static Door Get(string name)
/// The with the given name or if not found.
public static Door Get(GameObject gameObject) => gameObject is null ? null : Get(gameObject.GetComponentInChildren());
- ///
- /// Gets a of filtered based on a predicate.
- ///
- /// The condition to satify.
- /// A of which contains elements that satify the condition.
- public static IEnumerable Get(Func predicate) => List.Where(predicate);
-
- ///
- /// Gets a given the specified .
- ///
- /// The to search for.
- /// The with the given or if not found.
- public static Door Get(DoorType doorType) => List.FirstOrDefault(x => x.Type == doorType);
-
///
/// Returns the closest to the given .
///
@@ -367,6 +389,13 @@ public static Door Random(ZoneType type = ZoneType.Unspecified, bool onlyUnbroke
return doors[UnityEngine.Random.Range(0, doors.Count)];
}
+ ///
+ /// Gets a of filtered based on a predicate.
+ ///
+ /// The condition to satify.
+ /// A of which contains elements that satify the condition.
+ public static IEnumerable Get(Func predicate) => List.Where(predicate);
+
///
/// Locks all doors given the specified .
///
diff --git a/EXILED/Exiled.API/Features/Generator.cs b/EXILED/Exiled.API/Features/Generator.cs
index b3f5bfb69..315f9af0f 100644
--- a/EXILED/Exiled.API/Features/Generator.cs
+++ b/EXILED/Exiled.API/Features/Generator.cs
@@ -26,7 +26,7 @@ public class Generator : IWrapper, IWorldSpace
///
/// A of on the map.
///
- internal static readonly Dictionary Scp079GeneratorToGenerator = new();
+ internal static readonly Dictionary Scp079GeneratorToGenerator = new(new ComponentsEqualityComparer());
private Room room;
///
diff --git a/EXILED/Exiled.API/Features/Hazards/AmnesticCloudHazard.cs b/EXILED/Exiled.API/Features/Hazards/AmnesticCloudHazard.cs
index c385c143f..31c9ad244 100644
--- a/EXILED/Exiled.API/Features/Hazards/AmnesticCloudHazard.cs
+++ b/EXILED/Exiled.API/Features/Hazards/AmnesticCloudHazard.cs
@@ -3,10 +3,11 @@
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
//
-// -----------------------------------------------------------------------
+// ------------------------------------------------------------------------
namespace Exiled.API.Features.Hazards
{
+ using Exiled.API.Enums;
using PlayerRoles.PlayableScps.Scp939;
///
@@ -14,6 +15,8 @@ namespace Exiled.API.Features.Hazards
///
public class AmnesticCloudHazard : TemporaryHazard
{
+ private static Scp939AmnesticCloudInstance amnesticCloudPrefab;
+
///
/// Initializes a new instance of the class.
///
@@ -26,9 +29,26 @@ public AmnesticCloudHazard(Scp939AmnesticCloudInstance hazard)
Owner = Player.Get(Ability.Owner);
}
+ ///
+ /// Gets the amnestic cloud prefab.
+ ///
+ public static Scp939AmnesticCloudInstance AmnesticCloudPrefab
+ {
+ get
+ {
+ if (amnesticCloudPrefab == null)
+ amnesticCloudPrefab = PrefabHelper.GetPrefab(PrefabType.AmnesticCloudHazard);
+
+ return amnesticCloudPrefab;
+ }
+ }
+
///
public new Scp939AmnesticCloudInstance Base { get; }
+ ///
+ public override HazardType Type => HazardType.AmnesticCloud;
+
///
/// Gets the for this instance.
///
diff --git a/EXILED/Exiled.API/Features/Hazards/Hazard.cs b/EXILED/Exiled.API/Features/Hazards/Hazard.cs
index 6861d1a68..09bd880d3 100644
--- a/EXILED/Exiled.API/Features/Hazards/Hazard.cs
+++ b/EXILED/Exiled.API/Features/Hazards/Hazard.cs
@@ -11,6 +11,7 @@ namespace Exiled.API.Features.Hazards
using System.Collections.Generic;
using System.Linq;
+ using Exiled.API.Enums;
using Exiled.API.Features.Core;
using Exiled.API.Interfaces;
using global::Hazards;
@@ -25,7 +26,7 @@ public class Hazard : TypeCastObject, IWrapper
///
/// with to it's .
///
- internal static readonly Dictionary EnvironmentalHazardToHazard = new();
+ internal static readonly Dictionary EnvironmentalHazardToHazard = new(new ComponentsEqualityComparer());
///
/// Initializes a new instance of the class.
@@ -48,6 +49,11 @@ public Hazard(EnvironmentalHazard hazard)
///
public EnvironmentalHazard Base { get; }
+ ///
+ /// Gets the associated with the current Hazard.
+ ///
+ public virtual HazardType Type { get; } = HazardType.Unknown;
+
///
/// Gets or sets the list with all affected by this hazard players.
///
@@ -123,6 +129,15 @@ public static Hazard Get(EnvironmentalHazard environmentalHazard) =>
_ => new Hazard(environmentalHazard)
};
+ ///
+ /// Gets the by .
+ ///
+ /// The to convert into an hazard.
+ /// The specified type.
+ /// The hazard wrapper for the given .
+ public static T Get(EnvironmentalHazard environmentalHazard)
+ where T : Hazard => Get(environmentalHazard) as T;
+
///
/// Gets the hazard by the room where it's located.
///
@@ -144,6 +159,13 @@ public static Hazard Get(EnvironmentalHazard environmentalHazard) =>
/// of based on predicate.
public static IEnumerable Get(Func predicate) => List.Where(predicate);
+ ///
+ /// Gets an of .
+ ///
+ /// The to get.
+ /// of based on type.
+ public static IEnumerable Get(HazardType type) => Get(h => h.Type == type);
+
///
/// Checks if player is in hazard zone.
///
diff --git a/EXILED/Exiled.API/Features/Hazards/SinkholeHazard.cs b/EXILED/Exiled.API/Features/Hazards/SinkholeHazard.cs
index e8e0c4f3a..34cbaacd5 100644
--- a/EXILED/Exiled.API/Features/Hazards/SinkholeHazard.cs
+++ b/EXILED/Exiled.API/Features/Hazards/SinkholeHazard.cs
@@ -7,6 +7,7 @@
namespace Exiled.API.Features.Hazards
{
+ using Exiled.API.Enums;
using global::Hazards;
///
@@ -28,5 +29,8 @@ public SinkholeHazard(SinkholeEnvironmentalHazard hazard)
/// Gets the .
///
public new SinkholeEnvironmentalHazard Base { get; }
+
+ ///
+ public override HazardType Type => HazardType.Sinkhole;
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Features/Hazards/TantrumHazard.cs b/EXILED/Exiled.API/Features/Hazards/TantrumHazard.cs
index 15f54b5ac..906bd4faf 100644
--- a/EXILED/Exiled.API/Features/Hazards/TantrumHazard.cs
+++ b/EXILED/Exiled.API/Features/Hazards/TantrumHazard.cs
@@ -7,7 +7,9 @@
namespace Exiled.API.Features.Hazards
{
+ using Exiled.API.Enums;
using global::Hazards;
+ using Mirror;
using RelativePositioning;
using UnityEngine;
@@ -16,6 +18,8 @@ namespace Exiled.API.Features.Hazards
///
public class TantrumHazard : TemporaryHazard
{
+ private static TantrumEnvironmentalHazard tantrumPrefab;
+
///
/// Initializes a new instance of the class.
///
@@ -26,11 +30,28 @@ public TantrumHazard(TantrumEnvironmentalHazard hazard)
Base = hazard;
}
+ ///
+ /// Gets the tantrum prefab.
+ ///
+ public static TantrumEnvironmentalHazard TantrumPrefab
+ {
+ get
+ {
+ if (tantrumPrefab == null)
+ tantrumPrefab = PrefabHelper.GetPrefab(PrefabType.TantrumObj);
+
+ return tantrumPrefab;
+ }
+ }
+
///
/// Gets the .
///
public new TantrumEnvironmentalHazard Base { get; }
+ ///
+ public override HazardType Type => HazardType.Tantrum;
+
///
/// Gets or sets a value indicating whether or not sizzle should be played.
///
@@ -57,5 +78,28 @@ public Transform CorrectPosition
get => Base._correctPosition;
set => Base._correctPosition = value;
}
+
+ ///
+ /// Places a Tantrum (SCP-173's ability) in the indicated position.
+ ///
+ /// The position where you want to spawn the Tantrum.
+ /// Whether or not the tantrum will apply the effect.
+ /// If is , the tantrum is moved slightly up from its original position. Otherwise, the collision will not be detected and the slowness will not work.
+ /// The instance.
+ public static TantrumHazard PlaceTantrum(Vector3 position, bool isActive = true)
+ {
+ TantrumEnvironmentalHazard tantrum = Object.Instantiate(TantrumPrefab);
+
+ if (!isActive)
+ tantrum.SynchronizedPosition = new(position);
+ else
+ tantrum.SynchronizedPosition = new(position + (Vector3.up * 0.25f));
+
+ tantrum._destroyed = !isActive;
+
+ NetworkServer.Spawn(tantrum.gameObject);
+
+ return Get(tantrum);
+ }
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.API/Features/Items/Ammo.cs b/EXILED/Exiled.API/Features/Items/Ammo.cs
index 4c88a7f84..64c598ad7 100644
--- a/EXILED/Exiled.API/Features/Items/Ammo.cs
+++ b/EXILED/Exiled.API/Features/Items/Ammo.cs
@@ -20,7 +20,7 @@ public class Ammo : Item, IWrapper
///
/// Gets the absolute maximum amount of ammo that may be held at one time, if ammo is forcefully given to the player (regardless of worn armor or server configuration).
///
- /// For accessing the maximum amount of ammo that may be held based on worn armor and server settings, see .
+ /// For accessing the maximum amount of ammo that may be held based on worn armor and server settings, see .
///
///
public const ushort AmmoLimit = ushort.MaxValue;
diff --git a/EXILED/Exiled.API/Features/Items/ExplosiveGrenade.cs b/EXILED/Exiled.API/Features/Items/ExplosiveGrenade.cs
index 8f9aa165c..0002d5d76 100644
--- a/EXILED/Exiled.API/Features/Items/ExplosiveGrenade.cs
+++ b/EXILED/Exiled.API/Features/Items/ExplosiveGrenade.cs
@@ -119,7 +119,7 @@ public ExplosionGrenadeProjectile SpawnActive(Vector3 position, Player owner = n
ipb.Info = new PickupSyncInfo(Type, Weight, ItemSerialGenerator.GenerateNext());
- ExplosionGrenadeProjectile grenade = (ExplosionGrenadeProjectile)Pickup.Get(ipb);
+ ExplosionGrenadeProjectile grenade = Pickup.Get(ipb);
grenade.Base.gameObject.SetActive(true);
diff --git a/EXILED/Exiled.API/Features/Items/FlashGrenade.cs b/EXILED/Exiled.API/Features/Items/FlashGrenade.cs
index e2d009ac7..979784f2c 100644
--- a/EXILED/Exiled.API/Features/Items/FlashGrenade.cs
+++ b/EXILED/Exiled.API/Features/Items/FlashGrenade.cs
@@ -100,7 +100,7 @@ public FlashbangProjectile SpawnActive(Vector3 position, Player owner = null)
ipb.Info = new PickupSyncInfo(Type, Weight, ItemSerialGenerator.GenerateNext());
- FlashbangProjectile grenade = (FlashbangProjectile)Pickup.Get(ipb);
+ FlashbangProjectile grenade = Pickup.Get(ipb);
grenade.Base.gameObject.SetActive(true);
diff --git a/EXILED/Exiled.API/Features/Items/Item.cs b/EXILED/Exiled.API/Features/Items/Item.cs
index fd0bba35a..96d02257f 100644
--- a/EXILED/Exiled.API/Features/Items/Item.cs
+++ b/EXILED/Exiled.API/Features/Items/Item.cs
@@ -25,7 +25,6 @@ namespace Exiled.API.Features.Items
using InventorySystem.Items.Radio;
using InventorySystem.Items.ThrowableProjectiles;
using InventorySystem.Items.ToggleableLights;
- using InventorySystem.Items.ToggleableLights.Flashlight;
using InventorySystem.Items.Usables;
using InventorySystem.Items.Usables.Scp1576;
using InventorySystem.Items.Usables.Scp244;
@@ -33,7 +32,6 @@ namespace Exiled.API.Features.Items
using UnityEngine;
using BaseConsumable = InventorySystem.Items.Usables.Consumable;
- using Object = UnityEngine.Object;
///
/// A wrapper class for .
@@ -43,7 +41,7 @@ public class Item : TypeCastObject- , IWrapper
///
/// A dictionary of all 's that have been converted into .
///
- internal static readonly Dictionary BaseToItem = new();
+ internal static readonly Dictionary BaseToItem = new(new ComponentsEqualityComparer());
///
/// Initializes a new instance of the class.
@@ -219,6 +217,15 @@ public static Item Get(ItemBase itemBase)
};
}
+ ///
+ /// Gets an existing or creates a new instance of one.
+ ///
+ /// The to convert into an item.
+ /// The specified type.
+ /// The item wrapper for the given .
+ public static T Get(ItemBase itemBase)
+ where T : Item => Get(itemBase) as T;
+
///
/// Gets the Item belonging to the specified serial.
///
@@ -279,6 +286,40 @@ ItemType.KeycardGuard or ItemType.KeycardJanitor or ItemType.KeycardO5 or ItemTy
_ => new Item(type),
};
+ ///
+ /// Creates a new with the proper inherited subclass.
+ ///
+ /// Based on the , the returned can be casted into a subclass to gain more control over the object.
+ ///
- Usable items (Adrenaline, Medkit, Painkillers, SCP-207, SCP-268, and SCP-500) should be casted to the class.
+ ///
- All valid ammo should be casted to the class.
+ ///
- All valid firearms (not including the Micro HID) should be casted to the class.
+ ///
- All valid keycards should be casted to the class.
+ ///
- All valid armor should be casted to the class.
+ ///
- Explosive grenades and SCP-018 should be casted to the class.
+ ///
- Flash grenades should be casted to the class.
+ ///
+ ///
+ ///
The following have their own respective classes:
+ ///
- Flashlights can be casted to .
+ ///
- Radios can be casted to .
+ ///
- The Micro HID can be casted to .
+ ///
- SCP-244 A and B variants can be casted to .
+ ///
- SCP-330 can be casted to .
+ ///
- SCP-2176 can be casted to the class.
+ ///
- SCP-1576 can be casted to the class.
+ ///
- Jailbird can be casted to the class.
+ ///
+ ///
+ /// Items that are not listed above do not have a subclass, and can only use the base class.
+ ///
+ ///
+ /// The of the item to create.
+ /// The who owns the item by default.
+ /// The specified type.
+ /// The created. This can be cast as a subclass.
+ public static Item Create(ItemType type, Player owner = null)
+ where T : Item => Create(type, owner) as T;
+
///
/// Gives this item to a .
///
diff --git a/EXILED/Exiled.API/Features/Items/Jailbird.cs b/EXILED/Exiled.API/Features/Items/Jailbird.cs
index 9e508da11..2f8bfb524 100644
--- a/EXILED/Exiled.API/Features/Items/Jailbird.cs
+++ b/EXILED/Exiled.API/Features/Items/Jailbird.cs
@@ -7,11 +7,14 @@
namespace Exiled.API.Features.Items
{
+ using System;
+
using Exiled.API.Features.Pickups;
using Exiled.API.Interfaces;
using InventorySystem.Items.Autosync;
using InventorySystem.Items.Jailbird;
using Mirror;
+ using UnityEngine;
using JailbirdPickup = Pickups.JailbirdPickup;
@@ -114,12 +117,35 @@ public JailbirdWearState WearState
get => Base._deterioration.WearState;
set
{
- if (JailbirdDeteriorationTracker.ReceivedStates.ContainsKey(Serial))
- JailbirdDeteriorationTracker.ReceivedStates[Serial] = value;
+ TotalDamageDealt = GetDamage(value);
+ TotalCharges = GetCharge(value);
Base._deterioration.RecheckUsage();
}
}
+ ///
+ /// Calculates the damage corresponding to a given .
+ ///
+ /// The wear state to calculate damage for.
+ /// The amount of damage associated with the specified wear state.
+ public float GetDamage(JailbirdWearState wearState)
+ {
+ foreach (Keyframe keyframe in Base._deterioration._damageToWearState.keys)
+ {
+ if (Base._deterioration.FloatToState(keyframe.value) == wearState)
+ return keyframe.time;
+ }
+
+ throw new Exception("Wear state not found in damage to wear state mapping.");
+ }
+
+ ///
+ /// Gets the charge needed to reach a specific .
+ ///
+ /// The desired wear state to calculate the charge for.
+ /// The charge value required to achieve the specified wear state.
+ public int GetCharge(JailbirdWearState wearState) => (int)wearState;
+
///
/// Breaks the Jailbird.
///
diff --git a/EXILED/Exiled.API/Features/Items/Scp018.cs b/EXILED/Exiled.API/Features/Items/Scp018.cs
index 57f8122c1..7c08c7c6d 100644
--- a/EXILED/Exiled.API/Features/Items/Scp018.cs
+++ b/EXILED/Exiled.API/Features/Items/Scp018.cs
@@ -86,7 +86,7 @@ public Scp018Projectile SpawnActive(Vector3 position, Player owner = null)
ipb.Info = new PickupSyncInfo(Type, Weight, ItemSerialGenerator.GenerateNext());
- Scp018Projectile grenade = (Scp018Projectile)Pickup.Get(ipb);
+ Scp018Projectile grenade = Pickup.Get(ipb);
grenade.Base.gameObject.SetActive(true);
diff --git a/EXILED/Exiled.API/Features/Items/Scp2176.cs b/EXILED/Exiled.API/Features/Items/Scp2176.cs
index 412c371d6..0ca9fc54d 100644
--- a/EXILED/Exiled.API/Features/Items/Scp2176.cs
+++ b/EXILED/Exiled.API/Features/Items/Scp2176.cs
@@ -71,7 +71,7 @@ public Scp2176Projectile SpawnActive(Vector3 position, Player owner = null)
ipb.Info = new PickupSyncInfo(Type, Weight, ItemSerialGenerator.GenerateNext());
- Scp2176Projectile grenade = (Scp2176Projectile)Pickup.Get(ipb);
+ Scp2176Projectile grenade = Pickup.Get(ipb);
grenade.Base.gameObject.SetActive(true);
diff --git a/EXILED/Exiled.API/Features/Items/Scp330.cs b/EXILED/Exiled.API/Features/Items/Scp330.cs
index d0a7e3b9c..689929f12 100644
--- a/EXILED/Exiled.API/Features/Items/Scp330.cs
+++ b/EXILED/Exiled.API/Features/Items/Scp330.cs
@@ -197,7 +197,7 @@ public IEnumerable DropCandy(CandyKindID type, bool dropAll = fals
ipb.Info = new(Type, Weight, ItemSerialGenerator.GenerateNext());
- Scp330Pickup pickup = (Scp330Pickup)Pickup.Get(ipb);
+ Scp330Pickup pickup = Pickup.Get(ipb);
if (exposedType is not CandyKindID.None)
pickup.ExposedCandy = exposedType;
@@ -218,7 +218,7 @@ public IEnumerable DropCandy(CandyKindID type, bool dropAll = fals
ipb.Info = new(Type, Weight, ItemSerialGenerator.GenerateNext());
- Scp330Pickup pickup = (Scp330Pickup)Pickup.Get(ipb);
+ Scp330Pickup pickup = Pickup.Get(ipb);
if (exposedType is not CandyKindID.None)
pickup.ExposedCandy = exposedType;
diff --git a/EXILED/Exiled.API/Features/Items/Throwable.cs b/EXILED/Exiled.API/Features/Items/Throwable.cs
index 4946d1722..4de522295 100644
--- a/EXILED/Exiled.API/Features/Items/Throwable.cs
+++ b/EXILED/Exiled.API/Features/Items/Throwable.cs
@@ -29,7 +29,7 @@ public Throwable(ThrowableItem itemBase)
{
Base = itemBase;
Base.Projectile.gameObject.SetActive(false);
- Projectile = (Projectile)Pickup.Get(Object.Instantiate(Base.Projectile));
+ Projectile = Pickup.Get(Object.Instantiate(Base.Projectile));
Base.Projectile.gameObject.SetActive(true);
Projectile.Serial = Serial;
}
diff --git a/EXILED/Exiled.API/Features/Lift.cs b/EXILED/Exiled.API/Features/Lift.cs
index 944e5bd77..9a3b183c2 100644
--- a/EXILED/Exiled.API/Features/Lift.cs
+++ b/EXILED/Exiled.API/Features/Lift.cs
@@ -33,7 +33,7 @@ public class Lift : IWrapper, IWorldSpace
///
/// A containing all known s and their corresponding .
///
- internal static readonly Dictionary ElevatorChamberToLift = new(8);
+ internal static readonly Dictionary ElevatorChamberToLift = new(8, new ComponentsEqualityComparer());
///
/// Internal list that contains all ElevatorDoor for current group.
@@ -76,7 +76,7 @@ internal Lift(ElevatorChamber elevator)
///
/// Gets a value of the internal doors list.
///
- public IReadOnlyCollection Doors => internalDoorsList.Select(x => Door.Get(x).As()).ToList();
+ public IReadOnlyCollection Doors => internalDoorsList.Select(x => Door.Get(x)).ToList();
///
/// Gets a of in the .
@@ -201,7 +201,7 @@ public float AnimationTime
///
/// Gets the .
///
- public Doors.ElevatorDoor CurrentDestination => Door.Get(Base.CurrentDestination).As();
+ public Doors.ElevatorDoor CurrentDestination => Door.Get(Base.CurrentDestination);
///
/// Gets a of which contains all the instances from the specified .
diff --git a/EXILED/Exiled.API/Features/Map.cs b/EXILED/Exiled.API/Features/Map.cs
index 14f6d8214..3f1f6fc1a 100644
--- a/EXILED/Exiled.API/Features/Map.cs
+++ b/EXILED/Exiled.API/Features/Map.cs
@@ -28,9 +28,6 @@ namespace Exiled.API.Features
using LightContainmentZoneDecontamination;
using MapGeneration;
using MapGeneration.Distributors;
- using Mirror;
- using PlayerRoles;
- using PlayerRoles.PlayableScps.Scp173;
using PlayerRoles.PlayableScps.Scp939;
using PlayerRoles.Ragdolls;
using RelativePositioning;
@@ -39,8 +36,6 @@ namespace Exiled.API.Features
using Utils.Networking;
using Object = UnityEngine.Object;
- using Scp173GameRole = PlayerRoles.PlayableScps.Scp173.Scp173Role;
- using Scp939GameRole = PlayerRoles.PlayableScps.Scp939.Scp939Role;
///
/// A set of tools to easily handle the in-game map.
@@ -57,53 +52,17 @@ public static class Map
///
internal static readonly List TeleportsValue = new(8);
- ///
- /// A list of s on the map.
- ///
- internal static readonly List ToysValue = new();
-
- private static TantrumEnvironmentalHazard tantrumPrefab;
- private static Scp939AmnesticCloudInstance amnesticCloudPrefab;
-
private static AmbientSoundPlayer ambientSoundPlayer;
///
/// Gets the tantrum prefab.
///
- public static TantrumEnvironmentalHazard TantrumPrefab
- {
- get
- {
- if (tantrumPrefab == null)
- {
- Scp173GameRole scp173Role = (Scp173GameRole)RoleTypeId.Scp173.GetRoleBase();
-
- if (scp173Role.SubroutineModule.TryGetSubroutine(out Scp173TantrumAbility scp173TantrumAbility))
- tantrumPrefab = scp173TantrumAbility._tantrumPrefab;
- }
-
- return tantrumPrefab;
- }
- }
+ public static TantrumEnvironmentalHazard TantrumPrefab => TantrumHazard.TantrumPrefab; // TODO: Remove this.
///
/// Gets the amnestic cloud prefab.
///
- public static Scp939AmnesticCloudInstance AmnesticCloudPrefab
- {
- get
- {
- if (amnesticCloudPrefab == null)
- {
- Scp939GameRole scp939Role = (Scp939GameRole)RoleTypeId.Scp939.GetRoleBase();
-
- if (scp939Role.SubroutineModule.TryGetSubroutine(out Scp939AmnesticCloudAbility ability))
- amnesticCloudPrefab = ability._instancePrefab;
- }
-
- return amnesticCloudPrefab;
- }
- }
+ public static Scp939AmnesticCloudInstance AmnesticCloudPrefab => AmnesticCloudHazard.AmnesticCloudPrefab; // TODO: Remove this.
///
/// Gets a value indicating whether decontamination has begun in the light containment zone.
@@ -130,7 +89,7 @@ DecontaminationController.Singleton.NetworkDecontaminationOverride is Decontamin
///
/// Gets all objects.
///
- public static ReadOnlyCollection Toys { get; } = ToysValue.AsReadOnly();
+ public static ReadOnlyCollection Toys => AdminToy.BaseToAdminToy.Values.ToList().AsReadOnly(); // TODO: Obsolete it and make people use AdminToy.List
///
/// Gets or sets the current seed of the map.
@@ -286,21 +245,7 @@ public static void PlayAmbientSound(int id)
/// Whether or not the tantrum will apply the effect.
/// If is , the tantrum is moved slightly up from its original position. Otherwise, the collision will not be detected and the slowness will not work.
/// The instance.
- public static TantrumHazard PlaceTantrum(Vector3 position, bool isActive = true)
- {
- TantrumEnvironmentalHazard tantrum = Object.Instantiate(TantrumPrefab);
-
- if (!isActive)
- tantrum.SynchronizedPosition = new RelativePosition(position);
- else
- tantrum.SynchronizedPosition = new RelativePosition(position + (Vector3.up * 0.25f));
-
- tantrum._destroyed = !isActive;
-
- NetworkServer.Spawn(tantrum.gameObject);
-
- return Hazard.Get(tantrum).Cast();
- }
+ public static TantrumHazard PlaceTantrum(Vector3 position, bool isActive = true) => TantrumHazard.PlaceTantrum(position, isActive); // TODO: Remove this.
///
/// Destroy all objects.
diff --git a/EXILED/Exiled.API/Features/Npc.cs b/EXILED/Exiled.API/Features/Npc.cs
index 473bb4c14..6824cbab9 100644
--- a/EXILED/Exiled.API/Features/Npc.cs
+++ b/EXILED/Exiled.API/Features/Npc.cs
@@ -17,7 +17,6 @@ namespace Exiled.API.Features
using Exiled.API.Enums;
using Exiled.API.Extensions;
using Exiled.API.Features.Components;
-
using Footprinting;
using MEC;
@@ -52,6 +51,20 @@ public Npc(GameObject gameObject)
///
public static new List List => Player.List.OfType().ToList();
+ ///
+ /// Gets or sets the player's position.
+ ///
+ public override Vector3 Position
+ {
+ get => base.Position;
+ set
+ {
+ base.Position = value;
+ if (Role is Roles.FpcRole fpcRole)
+ fpcRole.RelativePosition = new(value);
+ }
+ }
+
///
/// Retrieves the NPC associated with the specified ReferenceHub.
///
diff --git a/EXILED/Exiled.API/Features/Pickups/Pickup.cs b/EXILED/Exiled.API/Features/Pickups/Pickup.cs
index 70db62a8a..6c600dd4c 100644
--- a/EXILED/Exiled.API/Features/Pickups/Pickup.cs
+++ b/EXILED/Exiled.API/Features/Pickups/Pickup.cs
@@ -11,6 +11,7 @@ namespace Exiled.API.Features.Pickups
using System.Collections.Generic;
using System.Linq;
+ using Exiled.API.Extensions;
using Exiled.API.Features.Core;
using Exiled.API.Features.Pickups.Projectiles;
using Exiled.API.Interfaces;
@@ -208,6 +209,11 @@ public float PickupTime
///
public ItemType Type => Base.NetworkInfo.ItemId;
+ ///
+ /// Gets the of the item.
+ ///
+ public ItemCategory Category => Type.GetCategory();
+
///
/// Gets or sets a value indicating whether the pickup is locked (can't be picked up).
///
@@ -338,6 +344,15 @@ public static Pickup Get(ItemPickupBase pickupBase)
};
}
+ ///
+ /// Gets an existing or creates a new instance of one.
+ ///
+ /// The to convert into an pickup.
+ /// The specified type.
+ /// The pickup wrapper for the given .
+ public static T Get(ItemPickupBase pickupBase)
+ where T : Pickup => Get(pickupBase) as T;
+
///
/// Gets the given a .
///
@@ -487,6 +502,36 @@ public static IEnumerable Get(IEnumerable gameObjects)
_ => new Pickup(type),
};
+ ///
+ /// Creates and returns a new with the proper inherited subclass.
+ ///
+ /// Based on the , the returned can be cast into a subclass to gain more control over the object.
+ ///
- All valid ammo should be cast to the class.
+ ///
- All valid firearms (not including the Micro HID) should be cast to the class.
+ ///
- All valid keycards should be cast to the class.
+ ///
- All valid armor should be cast to the class.
+ ///
- All grenades and throwables (not including SCP-018 and SCP-2176) should be cast to the class.
+ ///
+ ///
+ ///
The following have their own respective classes:
+ ///
- Radios can be cast to .
+ ///
- The Micro HID can be cast to .
+ ///
- SCP-244 A and B variants can be cast to .
+ ///
- SCP-330 can be cast to .
+ ///
- SCP-018 can be cast to .
+ ///
- SCP-2176 can be cast to .
+ ///
+ ///
+ /// Items that are not listed above do not have a subclass, and can only use the base class.
+ ///
+ ///
+ /// The of the pickup.
+ /// The specified type.
+ /// The created .
+ ///
+ public static Pickup Create(ItemType type)
+ where T : Pickup => Create(type) as T;
+
///
/// Creates and spawns a .
///
@@ -498,6 +543,19 @@ public static IEnumerable Get(IEnumerable gameObjects)
///
public static Pickup CreateAndSpawn(ItemType type, Vector3 position, Quaternion rotation, Player previousOwner = null) => Create(type).Spawn(position, rotation, previousOwner);
+ ///
+ /// Creates and spawns a .
+ ///
+ /// The of the pickup.
+ /// The position to spawn the at.
+ /// The rotation to spawn the .
+ /// An optional previous owner of the item.
+ /// The specified type.
+ /// The . See documentation of for more information on casting.
+ ///
+ public static Pickup CreateAndSpawn(ItemType type, Vector3 position, Quaternion rotation, Player previousOwner = null)
+ where T : Pickup => CreateAndSpawn(type, position, rotation, previousOwner) as T;
+
///
/// Spawns a .
///
diff --git a/EXILED/Exiled.API/Features/Pickups/Projectiles/Projectile.cs b/EXILED/Exiled.API/Features/Pickups/Projectiles/Projectile.cs
index cd18059bc..5406d6414 100644
--- a/EXILED/Exiled.API/Features/Pickups/Projectiles/Projectile.cs
+++ b/EXILED/Exiled.API/Features/Pickups/Projectiles/Projectile.cs
@@ -83,17 +83,37 @@ internal Projectile(ItemType type)
/// Projectile that are not listed will cause an Exception.
///
///
- /// The of the pickup.
- /// The created .
+ /// The of the projectile.
+ /// The created .
public static Projectile Create(ProjectileType projectiletype) => projectiletype switch
{
ProjectileType.Scp018 => new Scp018Projectile(),
ProjectileType.Flashbang => new FlashbangProjectile(),
ProjectileType.Scp2176 => new Scp2176Projectile(),
ProjectileType.FragGrenade => new ExplosionGrenadeProjectile(ItemType.GrenadeHE),
- _ => throw new System.Exception($"ProjectileType does not contain a valid value : {projectiletype}"),
+ _ => throw new Exception($"ProjectileType does not contain a valid value : {projectiletype}"),
};
+ ///
+ /// Creates and returns a new with the proper inherited subclass.
+ ///
+ /// Based on the , the returned can be casted into a subclass to gain more control over the object.
+ ///
The following have their own respective classes:
+ ///
- FragGrenade can be casted to .
+ ///
- Flashbang can be casted to .
+ ///
- Scp018 A and B variants can be casted to .
+ ///
- Scp2176 can be casted to .
+ ///
+ ///
+ /// Projectile that are not listed will cause an Exception.
+ ///
+ ///
+ /// The of the projectile.
+ /// The specified type.
+ /// The created .
+ public static Projectile Create(ProjectileType projectiletype)
+ where T : Projectile => Create(projectiletype) as T;
+
///
/// Spawns a .
///
@@ -110,14 +130,27 @@ public static Projectile Spawn(Projectile pickup, Vector3 position, Quaternion r
///
/// Creates and spawns a .
///
- /// The of the pickup.
+ /// The of the projectile.
/// The position to spawn the at.
/// The rotation to spawn the .
/// Whether the should be in active state after spawn.
/// An optional previous owner of the item.
- /// The . See documentation of for more information on casting.
+ /// The . See documentation of for more information on casting.
public static Projectile CreateAndSpawn(ProjectileType type, Vector3 position, Quaternion rotation, bool shouldBeActive = true, Player previousOwner = null) => Create(type).Spawn(position, rotation, shouldBeActive, previousOwner);
+ ///
+ /// Creates and spawns a .
+ ///
+ /// The of the projectile.
+ /// The position to spawn the at.
+ /// The rotation to spawn the .
+ /// Whether the should be in active state after spawn.
+ /// An optional previous owner of the item.
+ /// The specified type.
+ /// The . See documentation of for more information on casting.
+ public static Projectile CreateAndSpawn(ProjectileType type, Vector3 position, Quaternion rotation, bool shouldBeActive = true, Player previousOwner = null)
+ where T : Projectile => CreateAndSpawn(type, position, rotation, shouldBeActive, previousOwner) as T;
+
///
/// Activates the current .
///
diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs
index def165726..0885000f7 100644
--- a/EXILED/Exiled.API/Features/Player.cs
+++ b/EXILED/Exiled.API/Features/Player.cs
@@ -80,6 +80,16 @@ public class Player : TypeCastObject, IEntity, IWorldSpace
/// A list of the player's items.
///
internal readonly List- ItemsValue = new(8);
+
+ ///
+ /// A dictionary of custom item category limits.
+ ///
+ internal Dictionary CustomCategoryLimits = new();
+
+ ///
+ /// A dictionary of custom ammo limits.
+ ///
+ internal Dictionary CustomAmmoLimits = new();
#pragma warning restore SA1401
private readonly HashSet componentsInChildren = new();
@@ -282,6 +292,7 @@ public AuthenticationType AuthenticationType
"northwood" => AuthenticationType.Northwood,
"localhost" => AuthenticationType.LocalHost,
"ID_Dedicated" => AuthenticationType.DedicatedServer,
+ "offline" => AuthenticationType.Offline,
_ => AuthenticationType.Unknown,
};
}
@@ -503,7 +514,7 @@ public Player Cuffer
///
///
///
- public Vector3 Position
+ public virtual Vector3 Position
{
get => Transform.position;
set => ReferenceHub.TryOverridePosition(value, Vector3.zero);
@@ -958,7 +969,7 @@ public Item CurrentItem
///
/// Gets the armor that the player is currently wearing. Value will be if the player is not wearing any armor.
///
- public Armor CurrentArmor => Inventory.TryGetBodyArmor(out BodyArmor armor) ? (Armor)Item.Get(armor) : null;
+ public Armor CurrentArmor => Inventory.TryGetBodyArmor(out BodyArmor armor) ? Item.Get(armor) : null;
///
/// Gets the class.
@@ -1266,8 +1277,13 @@ public static Player Get(GameObject gameObject)
if (Dictionary.TryGetValue(gameObject, out Player player))
return player;
- UnverifiedPlayers.TryGetValue(gameObject, out player);
- return player;
+ if (UnverifiedPlayers.TryGetValue(gameObject, out player))
+ return player;
+
+ if (ReferenceHub.TryGetHub(gameObject, out ReferenceHub hub))
+ return new(hub);
+
+ return null;
}
///
@@ -1295,7 +1311,7 @@ public static Player Get(string args)
if (int.TryParse(args, out int id))
return Get(id);
- if (args.EndsWith("@steam") || args.EndsWith("@discord") || args.EndsWith("@northwood"))
+ if (args.EndsWith("@steam") || args.EndsWith("@discord") || args.EndsWith("@northwood") || args.EndsWith("@offline"))
{
foreach (Player player in Dictionary.Values)
{
@@ -2319,6 +2335,16 @@ public void Broadcast(ushort duration, string message, global::Broadcast.Broadca
public void AddAmmo(AmmoType ammoType, ushort amount) =>
Inventory.ServerAddAmmo(ammoType.GetItemType(), amount);
+ ///
+ /// Adds the amount of a specified ammo type to player's inventory.
+ ///
+ /// A dictionary of ammo types that will be added.
+ public void AddAmmo(Dictionary ammoBag)
+ {
+ foreach (KeyValuePair kvp in ammoBag)
+ AddAmmo(kvp.Key, kvp.Value);
+ }
+
///
/// Adds the amount of a weapon's ammo type to the player's inventory.
///
@@ -2338,6 +2364,16 @@ public void SetAmmo(AmmoType ammoType, ushort amount)
Inventory.ServerSetAmmo(itemType, amount);
}
+ ///
+ /// Sets the amount of a specified ammo type to player's inventory.
+ ///
+ /// A dictionary of ammo types that will be added.
+ public void SetAmmo(Dictionary ammoBag)
+ {
+ foreach (KeyValuePair kvp in ammoBag)
+ SetAmmo(kvp.Key, kvp.Value);
+ }
+
///
/// Gets the ammo count of a specified ammo type in a player's inventory.
///
@@ -2357,21 +2393,170 @@ public bool DropAmmo(AmmoType ammoType, ushort amount, bool checkMinimals = fals
///
/// Gets the maximum amount of ammo the player can hold, given the ammo .
- /// This method factors in the armor the player is wearing, as well as server configuration.
- /// For the maximum amount of ammo that can be given regardless of worn armor and server configuration, see .
///
/// The of the ammo to check.
- /// The maximum amount of ammo this player can carry. Guaranteed to be between 0 and .
- public int GetAmmoLimit(AmmoType type) =>
- InventorySystem.Configs.InventoryLimits.GetAmmoLimit(type.GetItemType(), referenceHub);
+ /// If the method should ignore the armor the player is wearing.
+ /// The maximum amount of ammo this player can carry.
+ public ushort GetAmmoLimit(AmmoType type, bool ignoreArmor = false)
+ {
+ if (ignoreArmor)
+ {
+ if (CustomAmmoLimits.TryGetValue(type, out ushort limit))
+ return limit;
+
+ ItemType itemType = type.GetItemType();
+ return ServerConfigSynchronizer.Singleton.AmmoLimitsSync.FirstOrDefault(x => x.AmmoType == itemType).Limit;
+ }
+
+ return InventorySystem.Configs.InventoryLimits.GetAmmoLimit(type.GetItemType(), referenceHub);
+ }
+
+ ///
+ /// Gets the maximum amount of ammo the player can hold, given the ammo .
+ /// This limit will scale with the armor the player is wearing.
+ /// For armor ammo limits, see .
+ ///
+ /// The of the ammo to check.
+ /// The number that will define the new limit.
+ public void SetAmmoLimit(AmmoType ammoType, ushort limit)
+ {
+ CustomAmmoLimits[ammoType] = limit;
+
+ ItemType itemType = ammoType.GetItemType();
+ int index = ServerConfigSynchronizer.Singleton.AmmoLimitsSync.FindIndex(x => x.AmmoType == itemType);
+ MirrorExtensions.SendFakeSyncObject(this, ServerConfigSynchronizer.Singleton.netIdentity, typeof(ServerConfigSynchronizer), writer =>
+ {
+ writer.WriteULong(2ul);
+ writer.WriteUInt(1);
+ writer.WriteByte((byte)SyncList.Operation.OP_SET);
+ writer.WriteInt(index);
+ writer.WriteAmmoLimit(new() { Limit = limit, AmmoType = itemType, });
+ });
+ }
+
+ ///
+ /// Reset a custom limit.
+ ///
+ /// The of the ammo to reset.
+ public void ResetAmmoLimit(AmmoType ammoType)
+ {
+ if (!HasCustomAmmoLimit(ammoType))
+ {
+ Log.Error($"{nameof(Player)}.{nameof(ResetAmmoLimit)}(AmmoType): AmmoType.{ammoType} does not have a custom limit.");
+ return;
+ }
+
+ CustomAmmoLimits.Remove(ammoType);
+
+ ItemType itemType = ammoType.GetItemType();
+ int index = ServerConfigSynchronizer.Singleton.AmmoLimitsSync.FindIndex(x => x.AmmoType == itemType);
+ MirrorExtensions.SendFakeSyncObject(this, ServerConfigSynchronizer.Singleton.netIdentity, typeof(ServerConfigSynchronizer), writer =>
+ {
+ writer.WriteULong(2ul);
+ writer.WriteUInt(1);
+ writer.WriteByte((byte)SyncList.Operation.OP_SET);
+ writer.WriteInt(index);
+ writer.WriteAmmoLimit(ServerConfigSynchronizer.Singleton.AmmoLimitsSync[index]);
+ });
+ }
+
+ ///
+ /// Check if the player has a custom limit for a specific .
+ ///
+ /// The to check.
+ /// If the player has a custom limit for the specific .
+ public bool HasCustomAmmoLimit(AmmoType ammoType) => CustomAmmoLimits.ContainsKey(ammoType);
///
/// Gets the maximum amount of an the player can hold, based on the armor the player is wearing, as well as server configuration.
///
/// The to check.
+ /// If the method should ignore the armor the player is wearing.
/// The maximum amount of items in the category that the player can hold.
- public int GetCategoryLimit(ItemCategory category) =>
- InventorySystem.Configs.InventoryLimits.GetCategoryLimit(category, referenceHub);
+ public sbyte GetCategoryLimit(ItemCategory category, bool ignoreArmor = false)
+ {
+ int index = InventorySystem.Configs.InventoryLimits.StandardCategoryLimits.Where(x => x.Value >= 0).OrderBy(x => x.Key).ToList().FindIndex(x => x.Key == category);
+
+ if (ignoreArmor && index != -1)
+ {
+ if (CustomCategoryLimits.TryGetValue(category, out sbyte customLimit))
+ return customLimit;
+
+ return ServerConfigSynchronizer.Singleton.CategoryLimits[index];
+ }
+
+ sbyte limit = InventorySystem.Configs.InventoryLimits.GetCategoryLimit(category, referenceHub);
+
+ return limit == -1 ? (sbyte)1 : limit;
+ }
+
+ ///
+ /// Set the maximum amount of an the player can hold. Only works with , , , and .
+ /// This limit will scale with the armor the player is wearing.
+ /// For armor category limits, see .
+ ///
+ /// The to check.
+ /// The number that will define the new limit.
+ public void SetCategoryLimit(ItemCategory category, sbyte limit)
+ {
+ int index = InventorySystem.Configs.InventoryLimits.StandardCategoryLimits.Where(x => x.Value >= 0).OrderBy(x => x.Key).ToList().FindIndex(x => x.Key == category);
+
+ if (index == -1)
+ {
+ Log.Error($"{nameof(Player)}.{nameof(SetCategoryLimit)}(ItemCategory, sbyte): Cannot set category limit for ItemCategory.{category}.");
+ return;
+ }
+
+ CustomCategoryLimits[category] = limit;
+
+ MirrorExtensions.SendFakeSyncObject(this, ServerConfigSynchronizer.Singleton.netIdentity, typeof(ServerConfigSynchronizer), writer =>
+ {
+ writer.WriteULong(1ul);
+ writer.WriteUInt(1);
+ writer.WriteByte((byte)SyncList.Operation.OP_SET);
+ writer.WriteInt(index);
+ writer.WriteSByte(limit);
+ });
+ }
+
+ ///
+ /// Reset a custom limit. Only works with , , , and .
+ ///
+ /// The of the category to reset.
+ public void ResetCategoryLimit(ItemCategory category)
+ {
+ int index = InventorySystem.Configs.InventoryLimits.StandardCategoryLimits.Where(x => x.Value >= 0).OrderBy(x => x.Key).ToList().FindIndex(x => x.Key == category);
+
+ if (index == -1)
+ {
+ Log.Error($"{nameof(Player)}.{nameof(ResetCategoryLimit)}(ItemCategory, sbyte): Cannot reset category limit for ItemCategory.{category}.");
+ return;
+ }
+
+ if (!HasCustomCategoryLimit(category))
+ {
+ Log.Error($"{nameof(Player)}.{nameof(ResetCategoryLimit)}(ItemCategory): ItemCategory.{category} does not have a custom limit.");
+ return;
+ }
+
+ CustomCategoryLimits.Remove(category);
+
+ MirrorExtensions.SendFakeSyncObject(this, ServerConfigSynchronizer.Singleton.netIdentity, typeof(ServerConfigSynchronizer), writer =>
+ {
+ writer.WriteULong(1ul);
+ writer.WriteUInt(1);
+ writer.WriteByte((byte)SyncList.Operation.OP_SET);
+ writer.WriteInt(index);
+ writer.WriteSByte(ServerConfigSynchronizer.Singleton.CategoryLimits[index]);
+ });
+ }
+
+ ///
+ /// Check if the player has a custom limit for a specific .
+ ///
+ /// The to check.
+ /// If the player has a custom limit for the specific .
+ public bool HasCustomCategoryLimit(ItemCategory category) => CustomCategoryLimits.ContainsKey(category);
///
/// Adds an item of the specified type with default durability(ammo/charge) and no mods to the player's inventory.
@@ -2582,7 +2767,7 @@ public void AddItem(Firearm item, IEnumerable identifiers)
/// The that was added.
public Item AddItem(FirearmPickup pickup, IEnumerable identifiers)
{
- Firearm firearm = (Firearm)Item.Get(Inventory.ServerAddItem(pickup.Type, pickup.Serial, pickup.Base));
+ Firearm firearm = Item.Get(Inventory.ServerAddItem(pickup.Type, pickup.Serial, pickup.Base));
if (identifiers is not null)
firearm.AddAttachment(identifiers);
@@ -3221,7 +3406,7 @@ public void ChangeEffectIntensity(string effectName, byte intensity, float durat
/// Whether or not the tantrum will apply the effect.
/// If is , the tantrum is moved slightly up from its original position. Otherwise, the collision will not be detected and the slowness will not work.
/// The instance..
- public TantrumHazard PlaceTantrum(bool isActive = true) => Map.PlaceTantrum(Position, isActive);
+ public TantrumHazard PlaceTantrum(bool isActive = true) => TantrumHazard.PlaceTantrum(Position, isActive);
///
/// Gives a new to the player.
diff --git a/EXILED/Exiled.API/Features/PrefabHelper.cs b/EXILED/Exiled.API/Features/PrefabHelper.cs
index de97e15d5..fd18e9fe9 100644
--- a/EXILED/Exiled.API/Features/PrefabHelper.cs
+++ b/EXILED/Exiled.API/Features/PrefabHelper.cs
@@ -41,6 +41,21 @@ public static PrefabAttribute GetPrefabAttribute(this PrefabType prefabType)
return type.GetField(Enum.GetName(type, prefabType)).GetCustomAttribute();
}
+ ///
+ /// Gets the prefab of the specified .
+ ///
+ /// The to get prefab of.
+ /// The to get.
+ /// Returns the prefab component as {T}.
+ public static T GetPrefab(PrefabType type)
+ where T : Component
+ {
+ if (!Stored.TryGetValue(type, out GameObject gameObject) || !gameObject.TryGetComponent(out T component))
+ return null;
+
+ return component;
+ }
+
///
/// Spawns a prefab on server.
///
@@ -68,9 +83,7 @@ public static GameObject Spawn(PrefabType prefabType, Vector3 position = default
public static T Spawn(PrefabType prefabType, Vector3 position = default, Quaternion rotation = default)
where T : Component
{
- if (!Stored.TryGetValue(prefabType, out GameObject gameObject) || !gameObject.TryGetComponent(out T component))
- return null;
- T obj = UnityEngine.Object.Instantiate(component, position, rotation);
+ T obj = UnityEngine.Object.Instantiate(GetPrefab(prefabType), position, rotation);
NetworkServer.Spawn(obj.gameObject);
return obj;
}
diff --git a/EXILED/Exiled.API/Features/Ragdoll.cs b/EXILED/Exiled.API/Features/Ragdoll.cs
index 9d73104fe..e1aefa56a 100644
--- a/EXILED/Exiled.API/Features/Ragdoll.cs
+++ b/EXILED/Exiled.API/Features/Ragdoll.cs
@@ -40,7 +40,7 @@ public class Ragdoll : IWrapper, IWorldSpace
///
/// A containing all known s and their corresponding .
///
- internal static readonly Dictionary BasicRagdollToRagdoll = new(250);
+ internal static readonly Dictionary BasicRagdollToRagdoll = new(250, new ComponentsEqualityComparer());
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Recontainer.cs b/EXILED/Exiled.API/Features/Recontainer.cs
index bdbbfcdf7..a63f8ada5 100644
--- a/EXILED/Exiled.API/Features/Recontainer.cs
+++ b/EXILED/Exiled.API/Features/Recontainer.cs
@@ -35,6 +35,11 @@ public static class Recontainer
///
public static bool IsCassieBusy => Base.CassieBusy;
+ ///
+ /// Gets a value about how many generator have been activated.
+ ///
+ public static int EngagedGeneratorCount => Base._prevEngaged;
+
///
/// Gets or sets a value indicating whether the containment zone is open.
///
diff --git a/EXILED/Exiled.API/Features/Roles/FpcRole.cs b/EXILED/Exiled.API/Features/Roles/FpcRole.cs
index 27abeded8..4ca71d8c8 100644
--- a/EXILED/Exiled.API/Features/Roles/FpcRole.cs
+++ b/EXILED/Exiled.API/Features/Roles/FpcRole.cs
@@ -8,9 +8,11 @@
namespace Exiled.API.Features.Roles
{
using System.Collections.Generic;
+ using System.Reflection;
using Exiled.API.Features.Pools;
+ using HarmonyLib;
using PlayerRoles;
using PlayerRoles.FirstPersonControl;
@@ -24,6 +26,7 @@ namespace Exiled.API.Features.Roles
///
public abstract class FpcRole : Role
{
+ private static FieldInfo enableFallDamageField;
private bool isUsingStamina = true;
///
@@ -47,14 +50,36 @@ protected FpcRole(FpcStandardRoleBase baseRole)
public FpcStandardRoleBase FirstPersonController { get; }
///
- /// Gets or sets the player's relative position.
+ /// Gets or sets the player's relative position as perceived by the server.
///
public RelativePosition RelativePosition
+ {
+ get => new(Owner.Position);
+ set => Owner.Position = value.Position;
+ }
+
+ ///
+ /// Gets or sets the player's relative position as perceived by the client.
+ ///
+ public RelativePosition ClientRelativePosition
{
get => FirstPersonController.FpcModule.Motor.ReceivedPosition;
set => FirstPersonController.FpcModule.Motor.ReceivedPosition = value;
}
+ ///
+ /// Gets or sets a value indicating whether if the player should get damage.
+ ///
+ public bool IsFallDamageEnable
+ {
+ get => FirstPersonController.FpcModule.Motor._enableFallDamage;
+ set
+ {
+ enableFallDamageField ??= AccessTools.Field(typeof(FpcMotor), nameof(FpcMotor._enableFallDamage));
+ enableFallDamageField.SetValue(FirstPersonController.FpcModule.Motor, value);
+ }
+ }
+
///
/// Gets or sets a value indicating whether if a rotation is detected on the player.
///
diff --git a/EXILED/Exiled.API/Features/Roles/Scp049Role.cs b/EXILED/Exiled.API/Features/Roles/Scp049Role.cs
index aecc872d4..eff90bc44 100644
--- a/EXILED/Exiled.API/Features/Roles/Scp049Role.cs
+++ b/EXILED/Exiled.API/Features/Roles/Scp049Role.cs
@@ -25,7 +25,7 @@ namespace Exiled.API.Features.Roles
///
/// Defines a role that represents SCP-049.
///
- public class Scp049Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole
+ public class Scp049Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp
{
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Roles/Scp079Role.cs b/EXILED/Exiled.API/Features/Roles/Scp079Role.cs
index 8782137a7..a3de5059c 100644
--- a/EXILED/Exiled.API/Features/Roles/Scp079Role.cs
+++ b/EXILED/Exiled.API/Features/Roles/Scp079Role.cs
@@ -16,6 +16,7 @@ namespace Exiled.API.Features.Roles
using MapGeneration;
using Mirror;
using PlayerRoles;
+ using PlayerRoles.PlayableScps;
using PlayerRoles.PlayableScps.Scp079;
using PlayerRoles.PlayableScps.Scp079.Cameras;
using PlayerRoles.PlayableScps.Scp079.Pinging;
@@ -31,7 +32,7 @@ namespace Exiled.API.Features.Roles
///
/// Defines a role that represents SCP-079.
///
- public class Scp079Role : Role, ISubroutinedScpRole
+ public class Scp079Role : Role, ISubroutinedScpRole, ISpawnableScp
{
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Roles/Scp096Role.cs b/EXILED/Exiled.API/Features/Roles/Scp096Role.cs
index 6d2662ad3..f30342b17 100644
--- a/EXILED/Exiled.API/Features/Roles/Scp096Role.cs
+++ b/EXILED/Exiled.API/Features/Roles/Scp096Role.cs
@@ -11,6 +11,7 @@ namespace Exiled.API.Features.Roles
using System.Linq;
using PlayerRoles;
+ using PlayerRoles.PlayableScps;
using PlayerRoles.PlayableScps.HumeShield;
using PlayerRoles.PlayableScps.Scp096;
using PlayerRoles.Subroutines;
@@ -20,7 +21,7 @@ namespace Exiled.API.Features.Roles
///
/// Defines a role that represents SCP-096.
///
- public class Scp096Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole
+ public class Scp096Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp
{
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Roles/Scp106Role.cs b/EXILED/Exiled.API/Features/Roles/Scp106Role.cs
index 826e756a5..9480a37bc 100644
--- a/EXILED/Exiled.API/Features/Roles/Scp106Role.cs
+++ b/EXILED/Exiled.API/Features/Roles/Scp106Role.cs
@@ -11,6 +11,7 @@ namespace Exiled.API.Features.Roles
using Exiled.API.Enums;
using PlayerRoles;
+ using PlayerRoles.PlayableScps;
using PlayerRoles.PlayableScps.HumeShield;
using PlayerRoles.PlayableScps.Scp049;
using PlayerRoles.PlayableScps.Scp106;
@@ -24,7 +25,7 @@ namespace Exiled.API.Features.Roles
///
/// Defines a role that represents SCP-106.
///
- public class Scp106Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole
+ public class Scp106Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp
{
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Roles/Scp173Role.cs b/EXILED/Exiled.API/Features/Roles/Scp173Role.cs
index 23536f696..3a5f625ca 100644
--- a/EXILED/Exiled.API/Features/Roles/Scp173Role.cs
+++ b/EXILED/Exiled.API/Features/Roles/Scp173Role.cs
@@ -13,6 +13,7 @@ namespace Exiled.API.Features.Roles
using Exiled.API.Features.Hazards;
using Mirror;
using PlayerRoles;
+ using PlayerRoles.PlayableScps;
using PlayerRoles.PlayableScps.HumeShield;
using PlayerRoles.PlayableScps.Scp173;
using PlayerRoles.Subroutines;
@@ -23,7 +24,7 @@ namespace Exiled.API.Features.Roles
///
/// Defines a role that represents SCP-173.
///
- public class Scp173Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole
+ public class Scp173Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp
{
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Roles/Scp3114Role.cs b/EXILED/Exiled.API/Features/Roles/Scp3114Role.cs
index 3f7f6fae1..0d23e48ba 100644
--- a/EXILED/Exiled.API/Features/Roles/Scp3114Role.cs
+++ b/EXILED/Exiled.API/Features/Roles/Scp3114Role.cs
@@ -23,7 +23,7 @@ namespace Exiled.API.Features.Roles
///
/// Defines a role that represents SCP-3114.
///
- public class Scp3114Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole
+ public class Scp3114Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp
{
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Roles/Scp939Role.cs b/EXILED/Exiled.API/Features/Roles/Scp939Role.cs
index b6dca72b8..ec404f349 100644
--- a/EXILED/Exiled.API/Features/Roles/Scp939Role.cs
+++ b/EXILED/Exiled.API/Features/Roles/Scp939Role.cs
@@ -13,6 +13,7 @@ namespace Exiled.API.Features.Roles
using Exiled.API.Features.Pools;
using PlayerRoles;
+ using PlayerRoles.PlayableScps;
using PlayerRoles.PlayableScps.HumeShield;
using PlayerRoles.PlayableScps.Scp939;
using PlayerRoles.PlayableScps.Scp939.Mimicry;
@@ -28,7 +29,7 @@ namespace Exiled.API.Features.Roles
///
/// Defines a role that represents SCP-939.
///
- public class Scp939Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole
+ public class Scp939Role : FpcRole, ISubroutinedScpRole, IHumeShieldRole, ISpawnableScp
{
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.API/Features/Room.cs b/EXILED/Exiled.API/Features/Room.cs
index 8d0b70fe3..4ca8bc4d3 100644
--- a/EXILED/Exiled.API/Features/Room.cs
+++ b/EXILED/Exiled.API/Features/Room.cs
@@ -32,7 +32,7 @@ public class Room : MonoBehaviour, IWorldSpace
///
/// A containing all known s and their corresponding .
///
- internal static readonly Dictionary RoomIdentifierToRoom = new(250);
+ internal static readonly Dictionary RoomIdentifierToRoom = new(250, new ComponentsEqualityComparer());
///
/// Gets a of which contains all the instances.
diff --git a/EXILED/Exiled.API/Features/Server.cs b/EXILED/Exiled.API/Features/Server.cs
index 908096434..b1b05e8c1 100644
--- a/EXILED/Exiled.API/Features/Server.cs
+++ b/EXILED/Exiled.API/Features/Server.cs
@@ -111,6 +111,15 @@ public static string Name
///
public static double Tps => Math.Round(1f / Time.smoothDeltaTime);
+ ///
+ /// Gets or sets the max ticks per second of the server.
+ ///
+ public static short MaxTps
+ {
+ get => ServerStatic.ServerTickrate;
+ set => ServerStatic.ServerTickrate = value;
+ }
+
///
/// Gets the actual frametime of the server.
///
diff --git a/EXILED/Exiled.API/Features/TeslaGate.cs b/EXILED/Exiled.API/Features/TeslaGate.cs
index 7ebdf6480..4d7909592 100644
--- a/EXILED/Exiled.API/Features/TeslaGate.cs
+++ b/EXILED/Exiled.API/Features/TeslaGate.cs
@@ -27,7 +27,7 @@ public class TeslaGate : IWrapper, IWorldSpace
///
/// A containing all known s and their corresponding .
///
- internal static readonly Dictionary BaseTeslaGateToTeslaGate = new(10);
+ internal static readonly Dictionary BaseTeslaGateToTeslaGate = new(10, new ComponentsEqualityComparer());
///
/// Initializes a new instance of the class.
@@ -178,7 +178,7 @@ public bool UseInstantBurst
///
/// Gets a of which contains all the tantrums to destroy.
///
- public IEnumerable TantrumsToDestroy => Base.TantrumsToBeDestroyed.Select(x => Hazard.Get(x) as TantrumHazard);
+ public IEnumerable TantrumsToDestroy => Base.TantrumsToBeDestroyed.Select(x => Hazard.Get(x));
///
/// Gets a of which contains all the players inside the hurt range.
diff --git a/EXILED/Exiled.API/Features/Toys/AdminToy.cs b/EXILED/Exiled.API/Features/Toys/AdminToy.cs
index fdbe03da1..5287096a1 100644
--- a/EXILED/Exiled.API/Features/Toys/AdminToy.cs
+++ b/EXILED/Exiled.API/Features/Toys/AdminToy.cs
@@ -7,6 +7,7 @@
namespace Exiled.API.Features.Toys
{
+ using System.Collections.Generic;
using System.Linq;
using AdminToys;
@@ -14,6 +15,7 @@ namespace Exiled.API.Features.Toys
using Enums;
using Exiled.API.Interfaces;
using Footprinting;
+ using InventorySystem.Items;
using Mirror;
using UnityEngine;
@@ -23,6 +25,11 @@ namespace Exiled.API.Features.Toys
///
public abstract class AdminToy : IWorldSpace
{
+ ///
+ /// A dictionary of all 's that have been converted into .
+ ///
+ internal static readonly Dictionary BaseToAdminToy = new(new ComponentsEqualityComparer());
+
///
/// Initializes a new instance of the class.
///
@@ -33,9 +40,14 @@ internal AdminToy(AdminToyBase toyAdminToyBase, AdminToyType type)
AdminToyBase = toyAdminToyBase;
ToyType = type;
- Map.ToysValue.Add(this);
+ BaseToAdminToy.Add(toyAdminToyBase, this);
}
+ ///
+ /// Gets a list of all 's on the server.
+ ///
+ public static IReadOnlyCollection List => BaseToAdminToy.Values;
+
///
/// Gets the original .
///
@@ -130,7 +142,31 @@ public bool IsStatic
///
/// The instance.
/// The corresponding instance.
- public static AdminToy Get(AdminToyBase adminToyBase) => Map.Toys.FirstOrDefault(x => x.AdminToyBase == adminToyBase);
+ public static AdminToy Get(AdminToyBase adminToyBase)
+ {
+ if (adminToyBase == null)
+ return null;
+
+ if (BaseToAdminToy.TryGetValue(adminToyBase, out AdminToy adminToy))
+ return adminToy;
+
+ return adminToyBase switch
+ {
+ LightSourceToy lightSourceToy => new Light(lightSourceToy),
+ PrimitiveObjectToy primitiveObjectToy => new Primitive(primitiveObjectToy),
+ ShootingTarget shootingTarget => new ShootingTargetToy(shootingTarget),
+ _ => throw new System.NotImplementedException()
+ };
+ }
+
+ ///
+ /// Gets the by .
+ ///
+ /// The to convert into an admintoy.
+ /// The specified type.
+ /// The admintoy wrapper for the given .
+ public static T Get(AdminToyBase adminToyBase)
+ where T : AdminToy => Get(adminToyBase) as T;
///
/// Spawns the toy into the game. Use to remove it.
@@ -147,7 +183,7 @@ public bool IsStatic
///
public void Destroy()
{
- Map.ToysValue.Remove(this);
+ BaseToAdminToy.Remove(AdminToyBase);
NetworkServer.Destroy(AdminToyBase.gameObject);
}
}
diff --git a/EXILED/Exiled.API/Features/Window.cs b/EXILED/Exiled.API/Features/Window.cs
index 961fbab88..63ca362fd 100644
--- a/EXILED/Exiled.API/Features/Window.cs
+++ b/EXILED/Exiled.API/Features/Window.cs
@@ -25,7 +25,7 @@ public class Window : IWrapper, IWorldSpace
///
/// A containing all known s and their corresponding .
///
- internal static readonly Dictionary BreakableWindowToWindow = new();
+ internal static readonly Dictionary BreakableWindowToWindow = new(new ComponentsEqualityComparer());
///
/// Initializes a new instance of the class.
diff --git a/EXILED/Exiled.CreditTags/Features/DatabaseHandler.cs b/EXILED/Exiled.CreditTags/Features/DatabaseHandler.cs
index d052b914c..e31fe2615 100644
--- a/EXILED/Exiled.CreditTags/Features/DatabaseHandler.cs
+++ b/EXILED/Exiled.CreditTags/Features/DatabaseHandler.cs
@@ -1,4 +1,4 @@
-// -----------------------------------------------------------------------
+// -----------------------------------------------------------------------
//
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
@@ -17,7 +17,7 @@ namespace Exiled.CreditTags.Features
public static class DatabaseHandler
{
- private const string Url = "https://raw.githubusercontent.com/Exiled-Official/CreditTags/main/data.yml";
+ private const string Url = "https://raw.githubusercontent.com/ExMod-Team/CreditTags/main/data.yml";
private const string ETagCacheFileName = "etag_cache.txt";
private const string DatabaseCacheFileName = "data.yml";
private const int CacheTimeInMinutes = 5;
diff --git a/EXILED/Exiled.CustomItems/Patches/PlayerInventorySee.cs b/EXILED/Exiled.CustomItems/Patches/PlayerInventorySee.cs
index 8f35712fb..1620477ba 100644
--- a/EXILED/Exiled.CustomItems/Patches/PlayerInventorySee.cs
+++ b/EXILED/Exiled.CustomItems/Patches/PlayerInventorySee.cs
@@ -8,6 +8,7 @@
namespace Exiled.CustomItems.Patches
{
using System.Collections.Generic;
+ using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
@@ -52,7 +53,7 @@ private static IEnumerable Transpiler(IEnumerable !x.IsGenericMethod && x.Name is nameof(Item.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemBase))),
new(OpCodes.Dup),
new(OpCodes.Stloc_S, item.LocalIndex),
new(OpCodes.Brfalse_S, continueLabel),
diff --git a/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs b/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs
index 51e4ac008..61f88ec2c 100644
--- a/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs
+++ b/EXILED/Exiled.CustomRoles/API/Features/CustomRole.cs
@@ -537,6 +537,16 @@ public virtual void AddRole(Player player)
Log.Debug($"{Name}: Adding {itemName} to inventory.");
TryAddItem(player, itemName);
}
+
+ if (Ammo.Count > 0)
+ {
+ Log.Debug($"{Name}: Adding Ammo to {player.Nickname} inventory.");
+ foreach (AmmoType type in EnumUtils.Values)
+ {
+ if (type != AmmoType.None)
+ player.SetAmmo(type, Ammo.ContainsKey(type) ? Ammo[type] == ushort.MaxValue ? InventoryLimits.GetAmmoLimit(type.GetItemType(), player.ReferenceHub) : Ammo[type] : (ushort)0);
+ }
+ }
});
Log.Debug($"{Name}: Setting health values.");
@@ -910,25 +920,6 @@ private void OnInternalChangingRole(ChangingRoleEventArgs ev)
{
RemoveRole(ev.Player);
}
- else if (Check(ev.Player))
- {
- Log.Debug($"{Name}: Checking ammo stuff {Ammo.Count}");
- if (Ammo.Count > 0)
- {
- Log.Debug($"{Name}: Clearing ammo");
- ev.Ammo.Clear();
- Timing.CallDelayed(
- 0.5f,
- () =>
- {
- foreach (AmmoType type in Enum.GetValues(typeof(AmmoType)))
- {
- if (type != AmmoType.None)
- ev.Player.SetAmmo(type, Ammo.ContainsKey(type) ? Ammo[type] == ushort.MaxValue ? InventoryLimits.GetAmmoLimit(type.GetItemType(), ev.Player.ReferenceHub) : Ammo[type] : (ushort)0);
- }
- });
- }
- }
}
private void OnSpawningRagdoll(SpawningRagdollEventArgs ev)
diff --git a/EXILED/Exiled.Events/Commands/TpsCommand.cs b/EXILED/Exiled.Events/Commands/TpsCommand.cs
new file mode 100644
index 000000000..2d7412919
--- /dev/null
+++ b/EXILED/Exiled.Events/Commands/TpsCommand.cs
@@ -0,0 +1,46 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Commands
+{
+ using System;
+
+ using CommandSystem;
+ using Exiled.API.Features;
+
+ ///
+ /// Command for showing current server TPS.
+ ///
+ [CommandHandler(typeof(RemoteAdminCommandHandler))]
+ [CommandHandler(typeof(GameConsoleCommandHandler))]
+ public class TpsCommand : ICommand
+ {
+ ///
+ public string Command { get; } = "tps";
+
+ ///
+ public string[] Aliases { get; } = Array.Empty();
+
+ ///
+ public string Description { get; } = "Shows the current TPS of the server";
+
+ ///
+ public bool Execute(ArraySegment arguments, ICommandSender sender, out string response)
+ {
+ double diff = Server.Tps / Server.MaxTps;
+ string color = diff switch
+ {
+ > 0.9 => "green",
+ > 0.5 => "yellow",
+ _ => "red"
+ };
+
+ response = $"{Server.Tps}/{Server.MaxTps}";
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/EventArgs/Interfaces/IHazardEvent.cs b/EXILED/Exiled.Events/EventArgs/Interfaces/IHazardEvent.cs
index 54f6a3f74..d10d34631 100644
--- a/EXILED/Exiled.Events/EventArgs/Interfaces/IHazardEvent.cs
+++ b/EXILED/Exiled.Events/EventArgs/Interfaces/IHazardEvent.cs
@@ -10,7 +10,7 @@ namespace Exiled.Events.EventArgs.Interfaces
using Exiled.API.Features.Hazards;
///
- /// Event args for all related events.
+ /// Event args for all related events.
///
public interface IHazardEvent : IExiledEvent
{
diff --git a/EXILED/Exiled.Events/EventArgs/Interfaces/IScp330Event.cs b/EXILED/Exiled.Events/EventArgs/Interfaces/IScp330Event.cs
new file mode 100644
index 000000000..45a3072e7
--- /dev/null
+++ b/EXILED/Exiled.Events/EventArgs/Interfaces/IScp330Event.cs
@@ -0,0 +1,22 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.EventArgs.Interfaces
+{
+ using Exiled.API.Features.Items;
+
+ ///
+ /// Event args used for all related events.
+ ///
+ public interface IScp330Event : IItemEvent
+ {
+ ///
+ /// Gets the triggering the event.
+ ///
+ public Scp330 Scp330 { get; }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/EventArgs/Interfaces/IUsableEvent.cs b/EXILED/Exiled.Events/EventArgs/Interfaces/IUsableEvent.cs
index 6c34c16e8..67b28f5b6 100644
--- a/EXILED/Exiled.Events/EventArgs/Interfaces/IUsableEvent.cs
+++ b/EXILED/Exiled.Events/EventArgs/Interfaces/IUsableEvent.cs
@@ -10,12 +10,12 @@ namespace Exiled.Events.EventArgs.Interfaces
using Exiled.API.Features.Items;
///
- /// Event args used for all related events.
+ /// Event args used for all related events.
///
public interface IUsableEvent : IItemEvent
{
///
- /// Gets the triggering the event.
+ /// Gets the triggering the event.
///
public Usable Usable { get; }
}
diff --git a/EXILED/Exiled.Events/EventArgs/Item/ChangingAttachmentsEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Item/ChangingAttachmentsEventArgs.cs
index 0e1c8fbed..fad3ec0b5 100644
--- a/EXILED/Exiled.Events/EventArgs/Item/ChangingAttachmentsEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Item/ChangingAttachmentsEventArgs.cs
@@ -38,17 +38,13 @@ public class ChangingAttachmentsEventArgs : IPlayerEvent, IDeniableEvent, IFirea
///
///
///
- public ChangingAttachmentsEventArgs(
- Player player,
- Firearm firearm,
- uint code,
- bool isAllowed = true)
+ public ChangingAttachmentsEventArgs(Player player, InventorySystem.Items.Firearms.Firearm firearm, uint code, bool isAllowed = true)
{
Player = player;
- Firearm = firearm;
- CurrentAttachmentIdentifiers = firearm.AttachmentIdentifiers;
- NewAttachmentIdentifiers = firearm.FirearmType.GetAttachmentIdentifiers(code).ToList();
- CurrentCode = firearm.Base.GetCurrentAttachmentsCode();
+ Firearm = Item.Get(firearm);
+ CurrentAttachmentIdentifiers = Firearm.AttachmentIdentifiers;
+ NewAttachmentIdentifiers = Firearm.FirearmType.GetAttachmentIdentifiers(code).ToList();
+ CurrentCode = firearm.GetCurrentAttachmentsCode();
NewCode = code;
IsAllowed = isAllowed;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Item/ChargingJailbirdEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Item/ChargingJailbirdEventArgs.cs
index 38f6ea7f8..6ac613800 100644
--- a/EXILED/Exiled.Events/EventArgs/Item/ChargingJailbirdEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Item/ChargingJailbirdEventArgs.cs
@@ -27,7 +27,7 @@ public class ChargingJailbirdEventArgs : IPlayerEvent, IItemEvent, IDeniableEven
public ChargingJailbirdEventArgs(ReferenceHub player, InventorySystem.Items.ItemBase swingItem, bool isAllowed = true)
{
Player = Player.Get(player);
- Item = Item.Get(swingItem);
+ Jailbird = (Jailbird)Item.Get(swingItem);
#pragma warning disable CS0618
IsAllowed = isAllowed;
#pragma warning restore CS0618
@@ -39,9 +39,14 @@ public ChargingJailbirdEventArgs(ReferenceHub player, InventorySystem.Items.Item
public Player Player { get; }
///
- /// Gets the that is being charged. This will always be a .
+ /// Gets the that is being charged.
///
- public Item Item { get; }
+ public Jailbird Jailbird { get; }
+
+ ///
+ /// Gets the that is being charged.
+ ///
+ public Item Item => Jailbird;
///
/// Gets or sets a value indicating whether or not the Jailbird can be charged.
diff --git a/EXILED/Exiled.Events/EventArgs/Item/SwingingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Item/SwingingEventArgs.cs
index 17c0319b0..6c8f0fce8 100644
--- a/EXILED/Exiled.Events/EventArgs/Item/SwingingEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Item/SwingingEventArgs.cs
@@ -25,7 +25,7 @@ public class SwingingEventArgs : IPlayerEvent, IItemEvent, IDeniableEvent
public SwingingEventArgs(ReferenceHub player, InventorySystem.Items.ItemBase swingItem, bool isAllowed = true)
{
Player = Player.Get(player);
- Item = Item.Get(swingItem);
+ Jailbird = (Jailbird)Item.Get(swingItem);
IsAllowed = isAllowed;
}
@@ -34,10 +34,15 @@ public SwingingEventArgs(ReferenceHub player, InventorySystem.Items.ItemBase swi
///
public Player Player { get; }
+ ///
+ /// Gets the that is being swung.
+ ///
+ public Jailbird Jailbird { get; }
+
///
/// Gets the that is being swung.
///
- public Item Item { get; }
+ public Item Item => Jailbird;
///
/// Gets or sets a value indicating whether or not the item can be swung.
diff --git a/EXILED/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs
index 28b7b30f0..a0dbd7920 100644
--- a/EXILED/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Map/ExplodingGrenadeEventArgs.cs
@@ -37,7 +37,7 @@ public class ExplodingGrenadeEventArgs : IPlayerEvent, IDeniableEvent
public ExplodingGrenadeEventArgs(Footprint thrower, Vector3 position, ExplosionGrenade grenade, Collider[] targets)
{
Player = Player.Get(thrower.Hub);
- Projectile = (EffectGrenadeProjectile)Pickup.Get(grenade);
+ Projectile = Pickup.Get(grenade);
Position = position;
TargetsToAffect = ListPool.Pool.Get();
@@ -97,7 +97,7 @@ public ExplodingGrenadeEventArgs(Footprint thrower, Vector3 position, ExplosionG
public ExplodingGrenadeEventArgs(Player thrower, EffectGrenade grenade, List targetsToAffect, bool isAllowed = true)
{
Player = thrower ?? Server.Host;
- Projectile = (EffectGrenadeProjectile)Pickup.Get(grenade);
+ Projectile = Pickup.Get(grenade);
Position = Projectile.Position;
TargetsToAffect = ListPool.Pool.Get(targetsToAffect ?? new());
IsAllowed = isAllowed;
diff --git a/EXILED/Exiled.Events/EventArgs/Map/Scp244SpawningEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Map/Scp244SpawningEventArgs.cs
index 1ed101269..fd6a6fa76 100644
--- a/EXILED/Exiled.Events/EventArgs/Map/Scp244SpawningEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Map/Scp244SpawningEventArgs.cs
@@ -1,4 +1,4 @@
-// -----------------------------------------------------------------------
+// -----------------------------------------------------------------------
//
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
@@ -10,6 +10,8 @@ namespace Exiled.Events.EventArgs.Map
using API.Features;
using Exiled.API.Features.Pickups;
using Interfaces;
+ using InventorySystem.Items.Usables.Scp244;
+ using MapGeneration;
///
/// Contains all information up to spawning Scp244.
@@ -25,11 +27,10 @@ public class Scp244SpawningEventArgs : IRoomEvent, IPickupEvent, IDeniableEvent
///
///
///
- public Scp244SpawningEventArgs(Room room, Pickup scp244Pickup)
+ public Scp244SpawningEventArgs(RoomIdentifier room, Scp244DeployablePickup scp244Pickup)
{
- Room = room;
- Pickup = scp244Pickup;
- Scp244Pickup = scp244Pickup.As();
+ Room = Room.Get(room);
+ Scp244Pickup = Pickup.Get(scp244Pickup);
}
///
@@ -38,7 +39,7 @@ public Scp244SpawningEventArgs(Room room, Pickup scp244Pickup)
public Room Room { get; }
///
- public Pickup Pickup { get; }
+ public Pickup Pickup => Scp244Pickup;
///
/// Gets a value indicating the pickup being spawning.
diff --git a/EXILED/Exiled.Events/EventArgs/Player/CancellingItemUseEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/CancellingItemUseEventArgs.cs
index ac07393e7..f3f623f40 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/CancellingItemUseEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/CancellingItemUseEventArgs.cs
@@ -27,7 +27,7 @@ public class CancellingItemUseEventArgs : IPlayerEvent, IDeniableEvent, IUsableE
public CancellingItemUseEventArgs(Player player, UsableItem item)
{
Player = player;
- Usable = Item.Get(item) is Usable usable ? usable : null;
+ Usable = Item.Get(item);
}
///
diff --git a/EXILED/Exiled.Events/EventArgs/Player/ChangingItemEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ChangingItemEventArgs.cs
index 403f5f705..2265309e4 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/ChangingItemEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/ChangingItemEventArgs.cs
@@ -46,7 +46,7 @@ public Item Item
get => newItem;
set
{
- if (!Player.Inventory.UserInventory.Items.TryGetValue(value.Serial, out _))
+ if (value != null && !Player.Inventory.UserInventory.Items.TryGetValue(value.Serial, out _))
throw new InvalidOperationException("ev.NewItem cannot be set to an item they do not have.");
newItem = value;
diff --git a/EXILED/Exiled.Events/EventArgs/Player/ChangingMicroHIDStateEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ChangingMicroHIDStateEventArgs.cs
index 4cb941b0a..2b9bacdbf 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/ChangingMicroHIDStateEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/ChangingMicroHIDStateEventArgs.cs
@@ -40,7 +40,7 @@ public class ChangingMicroHIDStateEventArgs : IPlayerEvent, IDeniableEvent
public ChangingMicroHIDStateEventArgs(Player player, MicroHIDItem microHID, HidState oldState, HidState newState, bool isAllowed = true)
{
Player = player;
- MicroHID = (MicroHid)Item.Get(microHID);
+ MicroHID = Item.Get(microHID);
OldState = oldState;
NewState = newState;
IsAllowed = isAllowed;
diff --git a/EXILED/Exiled.Events/EventArgs/Player/ChangingRadioPresetEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ChangingRadioPresetEventArgs.cs
index 7b59f2fb9..70af9b581 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/ChangingRadioPresetEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/ChangingRadioPresetEventArgs.cs
@@ -44,7 +44,7 @@ public class ChangingRadioPresetEventArgs : IPlayerEvent, IItemEvent, IDeniableE
public ChangingRadioPresetEventArgs(Player player, RadioItem item, RadioRangeLevel oldValue, RadioRangeLevel newValue, bool isAllowed = true)
{
Player = player;
- Radio = (Radio)Item.Get(item);
+ Radio = Item.Get(item);
OldValue = (RadioRange)oldValue;
NewValue = (RadioRange)newValue;
IsAllowed = isAllowed;
diff --git a/EXILED/Exiled.Events/EventArgs/Player/ChangingRoleEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ChangingRoleEventArgs.cs
index 552d70a73..c8ab4d9ba 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/ChangingRoleEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/ChangingRoleEventArgs.cs
@@ -11,11 +11,10 @@ namespace Exiled.Events.EventArgs.Player
using API.Enums;
using API.Features;
+ using Exiled.API.Extensions;
using Exiled.API.Features.Pools;
using Interfaces;
-
- using InventorySystem.Configs;
-
+ using InventorySystem;
using PlayerRoles;
///
@@ -70,17 +69,16 @@ public RoleTypeId NewRole
get => newRole;
set
{
- if (StartingInventories.DefinedInventories.ContainsKey(value))
- {
- Items.Clear();
- Ammo.Clear();
+ InventoryRoleInfo inventory = value.GetInventory();
+
+ Items.Clear();
+ Ammo.Clear();
- foreach (ItemType itemType in StartingInventories.DefinedInventories[value].Items)
- Items.Add(itemType);
+ foreach (ItemType itemType in inventory.Items)
+ Items.Add(itemType);
- foreach (KeyValuePair ammoPair in StartingInventories.DefinedInventories[value].Ammo)
- Ammo.Add(ammoPair.Key, ammoPair.Value);
- }
+ foreach (KeyValuePair ammoPair in inventory.Ammo)
+ Ammo.Add(ammoPair.Key, ammoPair.Value);
newRole = value;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/DroppedItemEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/DroppedItemEventArgs.cs
index 14d298c84..0109a4bba 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/DroppedItemEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/DroppedItemEventArgs.cs
@@ -10,6 +10,7 @@ namespace Exiled.Events.EventArgs.Player
using API.Features;
using Exiled.API.Features.Pickups;
using Interfaces;
+ using InventorySystem.Items.Pickups;
///
/// Contains all information after a player drops an item.
@@ -28,10 +29,10 @@ public class DroppedItemEventArgs : IPlayerEvent, IPickupEvent
///
///
///
- public DroppedItemEventArgs(Player player, Pickup pickup, bool wasThrown)
+ public DroppedItemEventArgs(Player player, ItemPickupBase pickup, bool wasThrown)
{
Player = player;
- Pickup = pickup;
+ Pickup = Pickup.Get(pickup);
WasThrown = wasThrown;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/LocalReportingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/LocalReportingEventArgs.cs
index 316be5039..97a443ea7 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/LocalReportingEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/LocalReportingEventArgs.cs
@@ -5,7 +5,7 @@
//
// -----------------------------------------------------------------------
-namespace Exiled.Events.EventArgs.Player
+namespace Exiled.Events.EventArgs.Player // TODO: Wrong namespace should be Server
{
using API.Features;
diff --git a/EXILED/Exiled.Events/EventArgs/Player/RemovedHandcuffsEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/RemovedHandcuffsEventArgs.cs
new file mode 100644
index 000000000..ee152fb34
--- /dev/null
+++ b/EXILED/Exiled.Events/EventArgs/Player/RemovedHandcuffsEventArgs.cs
@@ -0,0 +1,47 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.EventArgs.Player
+{
+ using API.Enums;
+ using API.Features;
+ using Exiled.Events.EventArgs.Interfaces;
+
+ ///
+ /// Contains all information after freeing a handcuffed player.
+ ///
+ public class RemovedHandcuffsEventArgs : IPlayerEvent
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The cuffer player.
+ /// The target player was uncuffed.
+ /// The reason for removing the handcuffs.
+ public RemovedHandcuffsEventArgs(Player cuffer, Player target, UncuffReason uncuffReason)
+ {
+ Player = cuffer;
+ Target = target;
+ UncuffReason = uncuffReason;
+ }
+
+ ///
+ /// Gets the target player to be cuffed.
+ ///
+ public Player Target { get; }
+
+ ///
+ /// Gets the cuffer player.
+ ///
+ public Player Player { get; }
+
+ ///
+ /// Gets the reason for removing handcuffs.
+ ///
+ public UncuffReason UncuffReason { get; }
+ }
+}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/RemovingHandcuffsEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/RemovingHandcuffsEventArgs.cs
index c6a90a835..c375bb4b7 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/RemovingHandcuffsEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/RemovingHandcuffsEventArgs.cs
@@ -7,6 +7,7 @@
namespace Exiled.Events.EventArgs.Player
{
+ using API.Enums;
using API.Features;
using Exiled.Events.EventArgs.Interfaces;
@@ -20,11 +21,13 @@ public class RemovingHandcuffsEventArgs : IPlayerEvent, IDeniableEvent
///
/// The cuffer player.
/// The target player to be uncuffed.
+ /// The reason of removing handcuffs.
/// Indicates whether the event can be executed or not.
- public RemovingHandcuffsEventArgs(Player cuffer, Player target, bool isAllowed = true)
+ public RemovingHandcuffsEventArgs(Player cuffer, Player target, UncuffReason uncuffReason, bool isAllowed = true)
{
Player = cuffer;
Target = target;
+ UncuffReason = uncuffReason;
IsAllowed = isAllowed;
}
@@ -34,13 +37,19 @@ public RemovingHandcuffsEventArgs(Player cuffer, Player target, bool isAllowed =
public Player Target { get; }
///
- /// Gets or sets a value indicating whether or not the player can be handcuffed.
+ /// Gets or sets a value indicating whether or not the player can be handcuffed. Denying the event will only have an effect when is until next major update.
///
+ /// TODO: Update docs and patches
public bool IsAllowed { get; set; }
///
/// Gets the cuffer player.
///
public Player Player { get; }
+
+ ///
+ /// Gets the reason of removing handcuffs.
+ ///
+ public UncuffReason UncuffReason { get; }
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/EventArgs/Player/ThrowingRequestEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ThrowingRequestEventArgs.cs
index 273763fda..089695333 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/ThrowingRequestEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/ThrowingRequestEventArgs.cs
@@ -28,7 +28,7 @@ public class ThrowingRequestEventArgs : IPlayerEvent, IItemEvent
public ThrowingRequestEventArgs(Player player, ThrowableItem item, ThrowableNetworkHandler.RequestType request)
{
Player = player;
- Throwable = (Throwable)Item.Get(item);
+ Throwable = Item.Get(item);
RequestType = (ThrowRequest)request;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/ThrownProjectileEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/ThrownProjectileEventArgs.cs
index aaf654d42..26984b839 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/ThrownProjectileEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/ThrownProjectileEventArgs.cs
@@ -29,8 +29,8 @@ public class ThrownProjectileEventArgs : IPlayerEvent, IItemEvent, IPickupEvent
public ThrownProjectileEventArgs(ThrownProjectile projectile, Player player, ThrowableItem item)
{
Player = player;
- Throwable = (Throwable)Item.Get(item);
- Projectile = (Projectile)Pickup.Get(projectile);
+ Throwable = Item.Get(item);
+ Projectile = Pickup.Get(projectile);
}
///
diff --git a/EXILED/Exiled.Events/EventArgs/Player/TogglingFlashlightEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/TogglingFlashlightEventArgs.cs
index b8990f85d..3a87a66a1 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/TogglingFlashlightEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/TogglingFlashlightEventArgs.cs
@@ -35,7 +35,7 @@ public class TogglingFlashlightEventArgs : IPlayerEvent, IDeniableEvent, IItemEv
public TogglingFlashlightEventArgs(ReferenceHub hub, ToggleableLightItemBase flashlight, bool newState)
{
Player = Player.Get(hub);
- Flashlight = (Flashlight)Item.Get(flashlight);
+ Flashlight = Item.Get(flashlight);
initialState = newState;
NewState = newState;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/TogglingRadioEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/TogglingRadioEventArgs.cs
index 67db54505..38d0fe09b 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/TogglingRadioEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/TogglingRadioEventArgs.cs
@@ -36,7 +36,7 @@ public class TogglingRadioEventArgs : IPlayerEvent, IDeniableEvent, IItemEvent
public TogglingRadioEventArgs(Player player, RadioItem radio, bool newState, bool isAllowed = true)
{
Player = player;
- Radio = (Radio)Item.Get(radio);
+ Radio = Item.Get(radio);
NewState = newState;
IsAllowed = isAllowed;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Player/UsingMicroHIDEnergyEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/UsingMicroHIDEnergyEventArgs.cs
index d98f014ed..402d20e1b 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/UsingMicroHIDEnergyEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/UsingMicroHIDEnergyEventArgs.cs
@@ -40,7 +40,7 @@ public class UsingMicroHIDEnergyEventArgs : IPlayerEvent, IDeniableEvent, IItemE
public UsingMicroHIDEnergyEventArgs(Player player, MicroHIDItem microHIDitem, HidState currentState, float drain, bool isAllowed = true)
{
Player = player;
- MicroHID = (MicroHid)Item.Get(microHIDitem);
+ MicroHID = Item.Get(microHIDitem);
CurrentState = currentState;
Drain = drain;
IsAllowed = isAllowed;
diff --git a/EXILED/Exiled.Events/EventArgs/Player/UsingRadioBatteryEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/UsingRadioBatteryEventArgs.cs
index 90369883f..1f07dadde 100644
--- a/EXILED/Exiled.Events/EventArgs/Player/UsingRadioBatteryEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Player/UsingRadioBatteryEventArgs.cs
@@ -36,7 +36,7 @@ public class UsingRadioBatteryEventArgs : IPlayerEvent, IDeniableEvent, IItemEve
///
public UsingRadioBatteryEventArgs(RadioItem radio, Player player, float drain, bool isAllowed = true)
{
- Radio = (Radio)Item.Get(radio);
+ Radio = Item.Get(radio);
Player = player;
Drain = drain;
IsAllowed = isAllowed;
diff --git a/EXILED/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs
index e2c2c0728..e897dbb04 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp049/FinishingRecallEventArgs.cs
@@ -38,7 +38,7 @@ public FinishingRecallEventArgs(Player target, Player scp049, BasicRagdoll ragdo
Scp049 = Player.Role.As();
Target = target;
Ragdoll = Ragdoll.Get(ragdoll);
- IsAllowed = isAllowed && Target.Role is SpectatorRole spectatorRole && spectatorRole.IsReadyToRespawn;
+ IsAllowed = isAllowed;
}
///
diff --git a/EXILED/Exiled.Events/EventArgs/Scp079/RecontainedEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp079/RecontainedEventArgs.cs
index 84a2e93ad..49b5dbebd 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp079/RecontainedEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp079/RecontainedEventArgs.cs
@@ -22,10 +22,16 @@ public class RecontainedEventArgs : IScp079Event
///
///
///
- public RecontainedEventArgs(Player player)
+ ///
+ ///
+ ///
+ public RecontainedEventArgs(Player player, PlayerRoles.PlayableScps.Scp079.Scp079Recontainer scp079Recontainer)
{
Player = player;
Scp079 = player.Role.As();
+ Recontainer = scp079Recontainer;
+ Attacker = Player.Get(scp079Recontainer._activatorGlass.LastAttacker);
+ IsAutomatic = scp079Recontainer._activatorGlass.LastAttacker.IsSet;
}
///
@@ -35,5 +41,20 @@ public RecontainedEventArgs(Player player)
///
public Scp079Role Scp079 { get; }
+
+ ///
+ /// Gets the instance that handle SCP-079 recontained proccess.
+ ///
+ public PlayerRoles.PlayableScps.Scp079.Scp079Recontainer Recontainer { get; }
+
+ ///
+ /// Gets the player who recontained SCP-079.
+ ///
+ public Player Attacker { get; }
+
+ ///
+ /// Gets a value indicating whether the recontainment has been made automatically or by triggering the process.
+ ///
+ public bool IsAutomatic { get; }
}
-}
\ No newline at end of file
+}
diff --git a/EXILED/Exiled.Events/EventArgs/Scp096/StartPryingGateEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp096/StartPryingGateEventArgs.cs
index f40e1e874..be8cef0ce 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp096/StartPryingGateEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp096/StartPryingGateEventArgs.cs
@@ -36,7 +36,7 @@ public StartPryingGateEventArgs(Player player, PryableDoor gate, bool isAllowed
{
Player = player;
Scp096 = player.Role.As();
- Gate = Door.Get(gate).As();
+ Gate = Door.Get(gate);
IsAllowed = isAllowed;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Scp244/UsingScp244EventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp244/UsingScp244EventArgs.cs
index c3077777c..5dca87a99 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp244/UsingScp244EventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp244/UsingScp244EventArgs.cs
@@ -33,7 +33,7 @@ public class UsingScp244EventArgs : IPlayerEvent, IDeniableEvent
///
public UsingScp244EventArgs(Scp244Item scp244, Player player, bool isAllowed = true)
{
- Scp244 = (Scp244)Item.Get(scp244);
+ Scp244 = Item.Get(scp244);
Player = player;
IsAllowed = isAllowed;
}
diff --git a/EXILED/Exiled.Events/EventArgs/Scp330/DroppingScp330EventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp330/DroppingScp330EventArgs.cs
index 5aabbd226..985e90337 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp330/DroppingScp330EventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp330/DroppingScp330EventArgs.cs
@@ -17,7 +17,7 @@ namespace Exiled.Events.EventArgs.Scp330
///
/// Contains all information before a player drops a SCP-330 candy.
///
- public class DroppingScp330EventArgs : IPlayerEvent, IDeniableEvent
+ public class DroppingScp330EventArgs : IPlayerEvent, IScp330Event, IDeniableEvent
{
///
/// Initializes a new instance of the class.
@@ -34,14 +34,17 @@ public class DroppingScp330EventArgs : IPlayerEvent, IDeniableEvent
public DroppingScp330EventArgs(Player player, Scp330Bag scp330, CandyKindID candy)
{
Player = player;
- Scp330 = (Scp330)Item.Get(scp330);
+ Scp330 = Item.Get(scp330);
Candy = candy;
}
///
- /// Gets or sets a value representing the being picked up.
+ /// Gets or sets a value representing the being picked up.
///
- public Scp330 Scp330 { get; set; }
+ public Scp330 Scp330 { get; set; } // Todo Remove set
+
+ ///
+ public Item Item => Scp330;
///
/// Gets or sets a value indicating whether or not the type of candy drop.
diff --git a/EXILED/Exiled.Events/EventArgs/Scp330/EatenScp330EventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp330/EatenScp330EventArgs.cs
index 3fa09415b..29beac357 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp330/EatenScp330EventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp330/EatenScp330EventArgs.cs
@@ -8,7 +8,7 @@
namespace Exiled.Events.EventArgs.Scp330
{
using API.Features;
-
+ using Exiled.API.Features.Items;
using Interfaces;
using InventorySystem.Items.Usables.Scp330;
@@ -16,16 +16,18 @@ namespace Exiled.Events.EventArgs.Scp330
///
/// Contains all information after a player has eaten SCP-330.
///
- public class EatenScp330EventArgs : IPlayerEvent
+ public class EatenScp330EventArgs : IPlayerEvent, IScp330Event
{
///
/// Initializes a new instance of the class.
///
/// .
+ /// .
/// .
- public EatenScp330EventArgs(Player player, ICandy candy)
+ public EatenScp330EventArgs(Player player, Scp330Bag scp330, ICandy candy)
{
Player = player;
+ Scp330 = (Scp330)Item.Get(scp330);
Candy = candy;
}
@@ -38,5 +40,11 @@ public EatenScp330EventArgs(Player player, ICandy candy)
/// Gets the player who has eaten SCP-330.
///
public Player Player { get; }
+
+ ///
+ public Scp330 Scp330 { get; }
+
+ ///
+ public Item Item => Scp330;
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/EventArgs/Scp330/EatingScp330EventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp330/EatingScp330EventArgs.cs
index d24a57ad4..825f8424b 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp330/EatingScp330EventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp330/EatingScp330EventArgs.cs
@@ -8,7 +8,7 @@
namespace Exiled.Events.EventArgs.Scp330
{
using API.Features;
-
+ using Exiled.API.Features.Items;
using Interfaces;
using InventorySystem.Items.Usables.Scp330;
@@ -16,17 +16,19 @@ namespace Exiled.Events.EventArgs.Scp330
///
/// Contains all information before a player eats SCP-330.
///
- public class EatingScp330EventArgs : IPlayerEvent, IDeniableEvent
+ public class EatingScp330EventArgs : IPlayerEvent, IScp330Event, IDeniableEvent
{
///
/// Initializes a new instance of the class.
///
/// .
- /// .
+ /// .
+ /// .
/// .
- public EatingScp330EventArgs(Player player, ICandy candy, bool isAllowed = true)
+ public EatingScp330EventArgs(Player player, Scp330Bag scp330, ICandy candy, bool isAllowed = true)
{
Player = player;
+ Scp330 = (Scp330)Item.Get(scp330);
Candy = candy;
IsAllowed = isAllowed;
}
@@ -45,5 +47,11 @@ public EatingScp330EventArgs(Player player, ICandy candy, bool isAllowed = true)
/// Gets the player who's eating SCP-330.
///
public Player Player { get; }
+
+ ///
+ public Scp330 Scp330 { get; }
+
+ ///
+ public Item Item => Scp330;
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs
index 30cd0a1ac..b87238842 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs
@@ -8,15 +8,16 @@
namespace Exiled.Events.EventArgs.Scp330
{
using API.Features;
-
+ using Exiled.API.Features.Items;
using Interfaces;
using InventorySystem.Items.Usables.Scp330;
+ using YamlDotNet.Core.Tokens;
///
/// Contains all information before a player interacts with SCP-330.
///
- public class InteractingScp330EventArgs : IPlayerEvent, IDeniableEvent
+ public class InteractingScp330EventArgs : IPlayerEvent, IScp330Event, IDeniableEvent
{
///
/// Initializes a new instance of the class.
@@ -30,6 +31,7 @@ public class InteractingScp330EventArgs : IPlayerEvent, IDeniableEvent
public InteractingScp330EventArgs(Player player, int usage)
{
Player = player;
+ Scp330 = Scp330Bag.TryGetBag(player.ReferenceHub, out Scp330Bag scp330Bag) ? (Scp330)Item.Get(scp330Bag) : null;
Candy = Scp330Candies.GetRandom();
UsageCount = usage;
ShouldSever = usage >= 2;
@@ -60,5 +62,13 @@ public InteractingScp330EventArgs(Player player, int usage)
/// Gets the triggering the event.
///
public Player Player { get; }
+
+ ///
+ /// This value can be null.
+ public Scp330 Scp330 { get; }
+
+ ///
+ /// This value can be null.
+ public Item Item => Scp330;
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/EventArgs/Scp939/PlacedAmnesticCloudEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp939/PlacedAmnesticCloudEventArgs.cs
new file mode 100644
index 000000000..107a7c869
--- /dev/null
+++ b/EXILED/Exiled.Events/EventArgs/Scp939/PlacedAmnesticCloudEventArgs.cs
@@ -0,0 +1,51 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.EventArgs.Scp939
+{
+ using API.Features;
+ using API.Features.Hazards;
+ using Interfaces;
+ using PlayerRoles.PlayableScps.Scp939;
+
+ using Scp939Role = API.Features.Roles.Scp939Role;
+
+ ///
+ /// Contains all information after SCP-939 used its amnestic cloud ability.
+ ///
+ public class PlacedAmnesticCloudEventArgs : IScp939Event
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public PlacedAmnesticCloudEventArgs(ReferenceHub hub, Scp939AmnesticCloudInstance cloud)
+ {
+ Player = Player.Get(hub);
+ AmnesticCloud = new AmnesticCloudHazard(cloud);
+ Scp939 = Player.Role.As();
+ }
+
+ ///
+ /// Gets the player who's controlling SCP-939.
+ ///
+ public Player Player { get; }
+
+ ///
+ /// Gets the instance.
+ ///
+ public AmnesticCloudHazard AmnesticCloud { get; }
+
+ ///
+ public Scp939Role Scp939 { get; }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/EventArgs/Server/RespawnedTeamEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Server/RespawnedTeamEventArgs.cs
new file mode 100644
index 000000000..4aaa2b363
--- /dev/null
+++ b/EXILED/Exiled.Events/EventArgs/Server/RespawnedTeamEventArgs.cs
@@ -0,0 +1,43 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.EventArgs.Server
+{
+ using System.Collections.Generic;
+ using System.Linq;
+
+ using Exiled.API.Features;
+ using Exiled.Events.EventArgs.Interfaces;
+ using Respawning;
+
+ ///
+ /// Contains all information after team spawns.
+ ///
+ public class RespawnedTeamEventArgs : IExiledEvent
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ public RespawnedTeamEventArgs(SpawnableTeamType team, IEnumerable hubs)
+ {
+ Players = hubs.Select(Player.Get);
+ Team = team;
+ }
+
+ ///
+ /// Gets the list of spawned players.
+ ///
+ public IEnumerable Players { get; }
+
+ ///
+ /// Gets the spawned team.
+ ///
+ public SpawnableTeamType Team { get; }
+ }
+}
diff --git a/EXILED/Exiled.Events/Events.cs b/EXILED/Exiled.Events/Events.cs
index 178071bb7..4c5fcabc5 100644
--- a/EXILED/Exiled.Events/Events.cs
+++ b/EXILED/Exiled.Events/Events.cs
@@ -21,6 +21,7 @@ namespace Exiled.Events
using PlayerRoles.Ragdolls;
using PlayerRoles.RoleAssign;
using PluginAPI.Events;
+ using Respawning;
using UnityEngine.SceneManagement;
///
@@ -70,7 +71,7 @@ public override void OnEnabled()
Handlers.Map.ChangedIntoGrenade += Handlers.Internal.ExplodingGrenade.OnChangedIntoGrenade;
CharacterClassManager.OnRoundStarted += Handlers.Server.OnRoundStarted;
-
+ RespawnManager.ServerOnRespawned += Handlers.Server.OnRespawnedTeam;
InventorySystem.InventoryExtensions.OnItemAdded += Handlers.Player.OnItemAdded;
InventorySystem.InventoryExtensions.OnItemRemoved += Handlers.Player.OnItemRemoved;
@@ -105,7 +106,7 @@ public override void OnDisabled()
InventorySystem.InventoryExtensions.OnItemAdded -= Handlers.Player.OnItemAdded;
InventorySystem.InventoryExtensions.OnItemRemoved -= Handlers.Player.OnItemRemoved;
-
+ RespawnManager.ServerOnRespawned -= Handlers.Server.OnRespawnedTeam;
RagdollManager.OnRagdollSpawned -= Handlers.Internal.RagdollList.OnSpawnedRagdoll;
RagdollManager.OnRagdollRemoved -= Handlers.Internal.RagdollList.OnRemovedRagdoll;
ItemPickupBase.OnPickupAdded -= Handlers.Internal.PickupEvent.OnSpawnedPickup;
diff --git a/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs b/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs
index 1b1dadf46..f75f2e91f 100644
--- a/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs
+++ b/EXILED/Exiled.Events/Handlers/Internal/MapGenerated.cs
@@ -48,7 +48,7 @@ public static void OnMapGenerated()
Map.ClearCache();
PrefabHelper.LoadPrefabs();
- // TODO: Fix For (https://trello.com/c/cUwpZDLs/5003-config-teamrespawnqueue-in-configgameplay-is-not-working-as-expected)
+ // TODO: Fix For (https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/377)
PlayerRoles.RoleAssign.HumanSpawner.Handlers[PlayerRoles.Team.ChaosInsurgency] = new PlayerRoles.RoleAssign.OneRoleHumanSpawner(PlayerRoles.RoleTypeId.ChaosConscript);
PlayerRoles.RoleAssign.HumanSpawner.Handlers[PlayerRoles.Team.OtherAlive] = new PlayerRoles.RoleAssign.OneRoleHumanSpawner(PlayerRoles.RoleTypeId.Tutorial);
PlayerRoles.RoleAssign.HumanSpawner.Handlers[PlayerRoles.Team.Dead] = new PlayerRoles.RoleAssign.OneRoleHumanSpawner(PlayerRoles.RoleTypeId.Spectator);
diff --git a/EXILED/Exiled.Events/Handlers/Internal/Round.cs b/EXILED/Exiled.Events/Handlers/Internal/Round.cs
index 400eb044c..8f9bd58b2 100644
--- a/EXILED/Exiled.Events/Handlers/Internal/Round.cs
+++ b/EXILED/Exiled.Events/Handlers/Internal/Round.cs
@@ -86,7 +86,7 @@ public static void OnVerified(VerifiedEventArgs ev)
{
RoleAssigner.CheckLateJoin(ev.Player.ReferenceHub, ClientInstanceMode.ReadyClient);
- // TODO: Remove if this has been fixed for https://trello.com/c/CzPD304L/5983-networking-blackout-is-not-synchronized-for-the-new-players
+ // TODO: Remove if this has been fixed for https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/52
foreach (Room room in Room.List.Where(current => current.AreLightsOff))
{
ev.Player.SendFakeSyncVar(room.RoomLightControllerNetIdentity, typeof(RoomLightController), nameof(RoomLightController.NetworkLightsEnabled), true);
diff --git a/EXILED/Exiled.Events/Handlers/Internal/SceneUnloaded.cs b/EXILED/Exiled.Events/Handlers/Internal/SceneUnloaded.cs
index 9bcb75cc8..4ecdc5f93 100644
--- a/EXILED/Exiled.Events/Handlers/Internal/SceneUnloaded.cs
+++ b/EXILED/Exiled.Events/Handlers/Internal/SceneUnloaded.cs
@@ -8,7 +8,7 @@
namespace Exiled.Events.Handlers.Internal
{
using API.Features;
-
+ using Exiled.API.Features.Toys;
using UnityEngine.SceneManagement;
#pragma warning disable SA1611 // Element parameters should be documented
@@ -35,7 +35,7 @@ public static void OnSceneUnloaded(Scene _)
{
Player.UserIdsCache.Clear();
Player.Dictionary.Clear();
- Map.ToysValue.Clear();
+ AdminToy.BaseToAdminToy.Clear();
}
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Handlers/Player.cs b/EXILED/Exiled.Events/Handlers/Player.cs
index aca07ce6e..016bdf644 100644
--- a/EXILED/Exiled.Events/Handlers/Player.cs
+++ b/EXILED/Exiled.Events/Handlers/Player.cs
@@ -223,6 +223,11 @@ public class Player
///
public static Event RemovingHandcuffs { get; set; } = new();
+ ///
+ /// Invoked after freeing a handcuffed .
+ ///
+ public static Event RemovedHandcuffs { get; set; } = new();
+
///
/// Invoked before a escapes.
///
@@ -704,6 +709,12 @@ public class Player
/// The instance.
public static void OnRemovingHandcuffs(RemovingHandcuffsEventArgs ev) => RemovingHandcuffs.InvokeSafely(ev);
+ ///
+ /// Called after freeing a handcuffed .
+ ///
+ /// The instance.
+ public static void OnRemovedHandcuffs(RemovedHandcuffsEventArgs ev) => RemovedHandcuffs.InvokeSafely(ev);
+
///
/// Called before a escapes.
///
diff --git a/EXILED/Exiled.Events/Handlers/Scp939.cs b/EXILED/Exiled.Events/Handlers/Scp939.cs
index 14d1245c6..e82e94a80 100644
--- a/EXILED/Exiled.Events/Handlers/Scp939.cs
+++ b/EXILED/Exiled.Events/Handlers/Scp939.cs
@@ -32,6 +32,11 @@ public static class Scp939
///
public static Event PlacingAmnesticCloud { get; set; } = new();
+ ///
+ /// Invoked after SCP-939 used its amnestic cloud ability.
+ ///
+ public static Event PlacedAmnesticCloud { get; set; } = new();
+
///
/// Invoked before SCP-939 plays a stolen voice.
///
@@ -81,6 +86,12 @@ public static class Scp939
/// The instance.
public static void OnPlacingAmnesticCloud(PlacingAmnesticCloudEventArgs ev) => PlacingAmnesticCloud.InvokeSafely(ev);
+ ///
+ /// Called after SCP-939 used its amnestic cloud ability.
+ ///
+ /// The instance.
+ public static void OnPlacedAmnesticCloud(PlacedAmnesticCloudEventArgs ev) => PlacedAmnesticCloud.InvokeSafely(ev);
+
///
/// Called before SCP-939 plays a stolen voice.
///
diff --git a/EXILED/Exiled.Events/Handlers/Server.cs b/EXILED/Exiled.Events/Handlers/Server.cs
index 1b3d0481a..75be4d81c 100644
--- a/EXILED/Exiled.Events/Handlers/Server.cs
+++ b/EXILED/Exiled.Events/Handlers/Server.cs
@@ -7,6 +7,9 @@
namespace Exiled.Events.Handlers
{
+ using System.Collections.Generic;
+
+ using Respawning;
#pragma warning disable SA1623 // Property summary documentation should match accessors
using Exiled.Events.EventArgs.Player;
@@ -53,6 +56,11 @@ public static class Server
///
public static Event RespawningTeam { get; set; } = new();
+ ///
+ /// Invoked after team spawns.
+ ///
+ public static Event RespawnedTeam { get; set; } = new();
+
///
/// Invoked before adding an unit name.
///
@@ -142,6 +150,13 @@ public static class Server
/// The instance.
public static void OnRespawningTeam(RespawningTeamEventArgs ev) => RespawningTeam.InvokeSafely(ev);
+ ///
+ /// Called after team spawns.
+ ///
+ ///
+ ///
+ public static void OnRespawnedTeam(SpawnableTeamType teamType, List hubs) => RespawnedTeam.InvokeSafely(new RespawnedTeamEventArgs(teamType, hubs));
+
///
/// Called before adding an unit name.
///
diff --git a/EXILED/Exiled.Events/Patches/Events/Item/ChangingAttachments.cs b/EXILED/Exiled.Events/Patches/Events/Item/ChangingAttachments.cs
index 3e49274a0..17394e444 100644
--- a/EXILED/Exiled.Events/Patches/Events/Item/ChangingAttachments.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Item/ChangingAttachments.cs
@@ -8,6 +8,7 @@
namespace Exiled.Events.Patches.Events.Item
{
using System.Collections.Generic;
+ using System.Linq;
using System.Reflection.Emit;
using API.Features;
@@ -67,10 +68,8 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions);
- Label continueLabel = generator.DefineLabel();
-
LocalBuilder pickup = generator.DeclareLocal(typeof(ItemPickupBase));
- int index = newInstructions.FindLastIndex(i => i.opcode == OpCodes.Dup);
-
- newInstructions.RemoveRange(index, 1);
-
- int offset = 1;
- index = newInstructions.FindIndex(i => i.opcode == OpCodes.Dup) + offset;
- newInstructions.InsertRange(index, new CodeInstruction[]
- {
- new(OpCodes.Dup),
- new(OpCodes.Stloc_S, pickup.LocalIndex),
- });
+ Label continueLabel = generator.DefineLabel();
- offset = -2;
- index = newInstructions.FindIndex(i => i.Calls(Method(typeof(NetworkServer), nameof(NetworkServer.Spawn), new[] { typeof(GameObject), typeof(NetworkConnection) }))) + offset;
+ int offset = -2;
+ int index = newInstructions.FindIndex(i => i.Calls(Method(typeof(NetworkServer), nameof(NetworkServer.Spawn), new[] { typeof(GameObject), typeof(NetworkConnection) }))) + offset;
newInstructions.InsertRange(index, new[]
{
- new CodeInstruction(OpCodes.Ldsfld, Field(typeof(Scp244Spawner), nameof(Scp244Spawner.CompatibleRooms))).MoveLabelsFrom(newInstructions[index]),
+ // save Pickup from the stack
+ new CodeInstruction(OpCodes.Stloc_S, pickup.LocalIndex).MoveLabelsFrom(newInstructions[index]),
+
+ // Scp244Spawner.CompatibleRooms[num]
+ new(OpCodes.Ldsfld, Field(typeof(Scp244Spawner), nameof(Scp244Spawner.CompatibleRooms))),
new(OpCodes.Ldloc_0),
new(OpCodes.Callvirt, PropertyGetter(typeof(List), "Item")),
- new(OpCodes.Ldloc_S, pickup.LocalIndex),
- new(OpCodes.Call, Method(typeof(Pickup), nameof(Pickup.Get), new[] { typeof(ItemPickupBase) })),
+ // scp244DeployablePickup
+ new(OpCodes.Ldloc_2),
+ // Scp244SpawningEventArgs ev = new(Room, Scp244DeployablePickup)
new(OpCodes.Newobj, GetDeclaredConstructors(typeof(Scp244SpawningEventArgs))[0]),
new(OpCodes.Dup),
new(OpCodes.Call, Method(typeof(Handlers.Map), nameof(Handlers.Map.OnScp244Spawning))),
+ // if (ev.IsAllowed) goto continueLabel;
new(OpCodes.Call, PropertyGetter(typeof(Scp244SpawningEventArgs), nameof(Scp244SpawningEventArgs.IsAllowed))),
new(OpCodes.Brtrue_S, continueLabel),
- new(OpCodes.Ldloc_S, pickup.LocalIndex),
+ // scp244DeployablePickup.gameObject.Destroy()
+ // return;
+ new(OpCodes.Ldloc_2),
new(OpCodes.Callvirt, PropertyGetter(typeof(ItemPickupBase), nameof(ItemPickupBase.gameObject))),
new(OpCodes.Call, Method(typeof(NetworkServer), nameof(NetworkServer.Destroy))),
new(OpCodes.Ret),
- new CodeInstruction(OpCodes.Nop).WithLabels(continueLabel),
- new(OpCodes.Ldloc_S, pickup.LocalIndex),
+ // load pickup back into the stack
+ new CodeInstruction(OpCodes.Ldloc_S, pickup.LocalIndex).WithLabels(continueLabel),
});
for (int z = 0; z < newInstructions.Count; z++)
diff --git a/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs b/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs
index ff3cd5762..e65ced6fb 100644
--- a/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Map/SpawningItem.cs
@@ -40,6 +40,7 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable instruction.IsLdarg(0));
newInstructions[lastIndex].labels.Add(doorSpawn);
+ // Replace
+ // "base.RegisterUnspawnedObject(doorNametagExtension.TargetDoor, itemPickupBase.gameObject);"
+ // with "base.RegisterUnspawnedObject(ev.Door.Base, itemPickupBase.gameObject);"
offset = -1;
index = newInstructions.FindLastIndex(i => i.opcode == OpCodes.Ldfld) + offset;
@@ -122,7 +123,7 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions);
- int e = 0;
+ LocalBuilder fpcRole = generator.DeclareLocal(typeof(FpcStandardRoleBase));
+
+ // replace HumanRole to FpcStandardRoleBase
+ newInstructions.Find(x => x.opcode == OpCodes.Isinst).operand = typeof(FpcStandardRoleBase);
+
+ // after this index all invalid exit are considered Custom
+ int customExit = newInstructions.FindLastIndex(x => x.opcode == OpCodes.Ldarg_0);
for (int i = 0; i < newInstructions.Count; i++)
{
- CodeInstruction codeInstruction = newInstructions[i];
- if (codeInstruction.opcode == OpCodes.Ldc_I4_0)
- {
- e++;
- if (e > 3)
- {
- newInstructions[i].opcode = OpCodes.Ldc_I4_5;
- }
- }
+ OpCode opcode = newInstructions[i].opcode;
+ if (opcode == OpCodes.Stloc_0)
+ newInstructions[i] = new CodeInstruction(OpCodes.Stloc_S, fpcRole.LocalIndex).WithLabels(newInstructions[i].labels);
+ else if (opcode == OpCodes.Ldloc_0)
+ newInstructions[i] = new CodeInstruction(OpCodes.Ldloc_S, fpcRole.LocalIndex).WithLabels(newInstructions[i].labels);
+ else if (opcode == OpCodes.Ldc_I4_0 && i > customExit)
+ newInstructions[i].opcode = OpCodes.Ldc_I4_5;
}
for (int z = 0; z < newInstructions.Count; z++)
diff --git a/EXILED/Exiled.Events/Patches/Events/Player/FirearmRequestReceived.cs b/EXILED/Exiled.Events/Patches/Events/Player/FirearmRequestReceived.cs
index 6ebd931f4..ff0dceace 100644
--- a/EXILED/Exiled.Events/Patches/Events/Player/FirearmRequestReceived.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Player/FirearmRequestReceived.cs
@@ -8,6 +8,7 @@
namespace Exiled.Events.Patches.Events.Player
{
using System.Collections.Generic;
+ using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
@@ -69,7 +70,7 @@ private static IEnumerable Transpiler(IEnumerable !x.IsGenericMethod && x.Name is nameof(API.Features.Items.Item.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemBase))),
new(OpCodes.Isinst, typeof(Firearm)),
new(OpCodes.Dup),
new(OpCodes.Stloc_S, firearm.LocalIndex),
diff --git a/EXILED/Exiled.Events/Patches/Events/Player/Healing.cs b/EXILED/Exiled.Events/Patches/Events/Player/Healing.cs
index 6f17fad14..5acbce087 100644
--- a/EXILED/Exiled.Events/Patches/Events/Player/Healing.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Player/Healing.cs
@@ -1,4 +1,4 @@
-// -----------------------------------------------------------------------
+// -----------------------------------------------------------------------
//
// Copyright (c) Exiled Team. All rights reserved.
// Licensed under the CC BY-SA 3.0 license.
@@ -34,6 +34,8 @@ private static IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions);
Label continueLabel = generator.DefineLabel();
+ Label skipHealing = generator.DefineLabel();
+ Label skipHealed = generator.DefineLabel();
LocalBuilder ev = generator.DeclareLocal(typeof(HealingEventArgs));
LocalBuilder player = generator.DeclareLocal(typeof(Player));
@@ -48,10 +50,14 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable
/// Patches .
- /// Adds the and events.
+ /// Adds the , , and events.
///
[EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.Handcuffing))]
[EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.RemovingHandcuffs))]
+ [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.RemovedHandcuffs))]
[HarmonyPatch(typeof(DisarmingHandlers), nameof(DisarmingHandlers.ServerProcessDisarmMessage))]
internal static class ProcessDisarmMessage
{
@@ -46,6 +48,7 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions);
}
}
+
+ ///
+ /// Patches .
+ /// Invokes and event.
+ ///
+ [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.RemovingHandcuffs))]
+ [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.RemovedHandcuffs))]
+ [HarmonyPatch(typeof(DisarmedPlayers), nameof(DisarmedPlayers.ValidateEntry))]
+ internal static class Uncuff
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+ Label returnLabel = generator.DefineLabel();
+
+ int offset = 2;
+ int index = newInstructions.FindLastIndex(
+ instruction => instruction.Calls(Method(typeof(ReferenceHub), nameof(ReferenceHub.TryGetHubNetID)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // Invoking RemovingHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.CufferDied
+ new(OpCodes.Ldc_I4_2),
+
+ // true
+ new(OpCodes.Ldc_I4_1),
+
+ // RemovingHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.CufferDied, true)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovingHandcuffsEventArgs))[0]),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // new(OpCodes.Dup),
+
+ // Handlers.Player.OnRemovingHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovingHandcuffs))),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // if (!ev.IsAllowed)
+ // return true;
+ // new(OpCodes.Callvirt, PropertyGetter(typeof(RemovingHandcuffsEventArgs), nameof(RemovingHandcuffsEventArgs.IsAllowed))),
+ // new(OpCodes.Brfalse_S, returnLabel),
+
+ // Invoking RemovedHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.CufferDied
+ new(OpCodes.Ldc_I4_2),
+
+ // RemovedHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.CufferDied)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovedHandcuffsEventArgs))[0]),
+
+ // Handlers.Player.OnRemovedHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovedHandcuffs))),
+ });
+
+ offset = 5;
+ index = newInstructions.FindLastIndex(
+ instruction => instruction.Calls(PropertyGetter(typeof(PlayerRoles.PlayerRoleManager), nameof(PlayerRoles.PlayerRoleManager.CurrentRole)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // Invoking RemovingHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.CufferDied
+ new(OpCodes.Ldc_I4_2),
+
+ // true
+ new(OpCodes.Ldc_I4_1),
+
+ // RemovingHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.CufferDied, true)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovingHandcuffsEventArgs))[0]),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // new(OpCodes.Dup),
+
+ // Handlers.Player.OnRemovingHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovingHandcuffs))),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // if (!ev.IsAllowed)
+ // return true;
+ // new(OpCodes.Callvirt, PropertyGetter(typeof(RemovingHandcuffsEventArgs), nameof(RemovingHandcuffsEventArgs.IsAllowed))),
+ // new(OpCodes.Brfalse_S, returnLabel),
+
+ // Invoking RemovedHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.CufferDied
+ new(OpCodes.Ldc_I4_2),
+
+ // RemovedHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.CufferDied)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovedHandcuffsEventArgs))[0]),
+
+ // Handlers.Player.OnRemovedHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovedHandcuffs))),
+ });
+
+ offset = 3;
+ index = newInstructions.FindLastIndex(
+ instruction => instruction.Calls(PropertyGetter(typeof(UnityEngine.Vector3), nameof(UnityEngine.Vector3.sqrMagnitude)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // Invoking RemovingHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.OutOfRange
+ new(OpCodes.Ldc_I4_1),
+
+ // true
+ new(OpCodes.Ldc_I4_1),
+
+ // RemovingHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.OutOfRange, true)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovingHandcuffsEventArgs))[0]),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // new(OpCodes.Dup),
+
+ // Handlers.Player.OnRemovingHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovingHandcuffs))),
+
+ // TODO: Uncomment this part in next major update to prevent breaking changes
+ // if (!ev.IsAllowed)
+ // return true;
+ // new(OpCodes.Callvirt, PropertyGetter(typeof(RemovingHandcuffsEventArgs), nameof(RemovingHandcuffsEventArgs.IsAllowed))),
+ // new(OpCodes.Brfalse_S, returnLabel),
+
+ // Invoking RemovedHandcuffs event
+ // Player.Get(Cuffer)
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // Player.Get(Target)
+ new(OpCodes.Ldloc_0),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // UncuffReason.CufferDied
+ new(OpCodes.Ldc_I4_2),
+
+ // RemovedHandcuffsEventArgs ev = new(Cuffer, Target, UncuffReason.OutOfRange)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RemovedHandcuffsEventArgs))[0]),
+
+ // Handlers.Player.OnRemovedHandcuffs(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnRemovedHandcuffs))),
+ });
+
+ newInstructions[newInstructions.Count - 2].labels.Add(returnLabel);
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Events/Player/Verified.cs b/EXILED/Exiled.Events/Patches/Events/Player/Verified.cs
index 585457a43..ce6697b5d 100644
--- a/EXILED/Exiled.Events/Patches/Events/Player/Verified.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Player/Verified.cs
@@ -7,16 +7,20 @@
namespace Exiled.Events.Patches.Events.Player
{
+#pragma warning disable SA1402 // File may only contain a single type
+#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
using System;
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
using API.Features;
+ using API.Features.Pools;
using CentralAuth;
using Exiled.API.Extensions;
using Exiled.Events.EventArgs.Player;
-
using HarmonyLib;
-#pragma warning disable SA1313 // Parameter names should begin with lower-case letter
+ using static HarmonyLib.AccessTools;
///
/// Patches .
@@ -25,12 +29,16 @@ namespace Exiled.Events.Patches.Events.Player
[HarmonyPatch(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.FinalizeAuthentication))]
internal static class Verified
{
- private static void Postfix(PlayerAuthenticationManager __instance)
+ ///
+ /// Called after the player has been verified.
+ ///
+ /// The player's hub.
+ internal static void PlayerVerified(ReferenceHub hub)
{
- if (!Player.UnverifiedPlayers.TryGetValue(__instance._hub.gameObject, out Player player))
- Joined.CallEvent(__instance._hub, out player);
+ if (!Player.UnverifiedPlayers.TryGetValue(hub.gameObject, out Player player))
+ Joined.CallEvent(hub, out player);
- Player.Dictionary.Add(__instance._hub.gameObject, player);
+ Player.Dictionary.Add(hub.gameObject, player);
player.IsVerified = true;
player.RawUserId = player.UserId.GetRawUserId();
@@ -39,5 +47,41 @@ private static void Postfix(PlayerAuthenticationManager __instance)
Handlers.Player.OnVerified(new VerifiedEventArgs(player));
}
+
+ private static void Postfix(PlayerAuthenticationManager __instance)
+ {
+ PlayerVerified(__instance._hub);
+ }
+ }
+
+ ///
+ /// Patches .
+ /// Adds the event during offline mode.
+ ///
+ [HarmonyPatch(typeof(NicknameSync), nameof(NicknameSync.UserCode_CmdSetNick__String))]
+ internal static class VerifiedOfflineMode
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ const int offset = 1;
+ int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Callvirt && x.OperandIs(Method(typeof(CharacterClassManager), nameof(CharacterClassManager.SyncServerCmdBinding)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // Verified.PlayerVerified(this._hub);
+ new CodeInstruction(OpCodes.Ldarg_0),
+ new CodeInstruction(OpCodes.Ldfld, Field(typeof(NicknameSync), nameof(NicknameSync._hub))),
+ new CodeInstruction(OpCodes.Call, Method(typeof(Verified), nameof(Verified.PlayerVerified))),
+ });
+
+ for (int i = 0; i < newInstructions.Count; i++)
+ yield return newInstructions[i];
+
+ ListPool.Pool.Return(newInstructions);
+ }
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs b/EXILED/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs
index d1458e0fd..2eab40acc 100644
--- a/EXILED/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Scp049/FinishingRecall.cs
@@ -25,11 +25,8 @@ namespace Exiled.Events.Patches.Events.Scp049
///
/// Patches .
/// Adds the event.
- /// Fix bug than Overwatch can get force respawn by Scp049
- /// Bug reported to NW https://trello.com/c/V0uHP2eV/5745-overwatch-overwatch-can-get-respawned-by-scp-049.
- /// The fix is directly inside the .
///
- // [EventPatch(typeof(Handlers.Scp049), nameof(Handlers.Scp049.FinishingRecall))]
+ [EventPatch(typeof(Handlers.Scp049), nameof(Handlers.Scp049.FinishingRecall))]
[HarmonyPatch(typeof(Scp049ResurrectAbility), nameof(Scp049ResurrectAbility.ServerComplete))]
internal static class FinishingRecall
{
diff --git a/EXILED/Exiled.Events/Patches/Events/Scp079/Recontain.cs b/EXILED/Exiled.Events/Patches/Events/Scp079/Recontain.cs
index 991ffc47e..3338c84ab 100644
--- a/EXILED/Exiled.Events/Patches/Events/Scp079/Recontain.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Scp079/Recontain.cs
@@ -37,9 +37,10 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable instruction.Calls(Method(typeof(Scp330Bag), nameof(Scp330Bag.ServerProcessPickup)))) + removeServerProcessOffset;
-
- newInstructions.RemoveRange(removeServerProcessIndex, 3);
-
- // Replace NW server process logic.
- newInstructions.InsertRange(
- removeServerProcessIndex,
- new[]
- {
- // ldarg.1 is already in the stack
-
- // ev.Candy
- new CodeInstruction(OpCodes.Ldloc, ev),
- new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(InteractingScp330EventArgs), nameof(InteractingScp330EventArgs.Candy))),
-
- // bag
- new CodeInstruction(OpCodes.Ldloca_S, 3),
-
- // ServerProcessPickup(ReferenceHub, CandyKindID, Scp330Bag)
- new CodeInstruction(OpCodes.Call, Method(typeof(InteractingScp330), nameof(ServerProcessPickup), new[] { typeof(ReferenceHub), typeof(CandyKindID), typeof(Scp330Bag).MakeByRefType() })),
- });
-
// This is to find the location of RpcMakeSound to remove the original code and add a new sever logic structure (Start point)
- int addShouldSeverOffset = 1;
+ int addShouldSeverOffset = -1;
int addShouldSeverIndex = newInstructions.FindLastIndex(
instruction => instruction.Calls(Method(typeof(Scp330Interobject), nameof(Scp330Interobject.RpcMakeSound)))) + addShouldSeverOffset;
- // This is to find the location of the next return (End point)
- int includeSameLine = 1;
- int nextReturn = newInstructions.FindIndex(addShouldSeverIndex, instruction => instruction.opcode == OpCodes.Ret) + includeSameLine;
- Label originalLabel = newInstructions[addShouldSeverIndex].ExtractLabels()[0];
-
- // Remove original code from after RpcMakeSound to next return and then fully replace it.
- newInstructions.RemoveRange(addShouldSeverIndex, nextReturn - addShouldSeverIndex);
-
- addShouldSeverIndex = newInstructions.FindLastIndex(
- instruction => instruction.Calls(Method(typeof(Scp330Interobject), nameof(Scp330Interobject.RpcMakeSound)))) + addShouldSeverOffset;
+ int serverEffectLocationStart = -1;
+ int enableEffect = newInstructions.FindLastIndex(
+ instruction => instruction.LoadsField(Field(typeof(ReferenceHub), nameof(ReferenceHub.playerEffectsController)))) + serverEffectLocationStart;
+ newInstructions[enableEffect].WithLabels(enableEffectLabel);
newInstructions.InsertRange(
addShouldSeverIndex,
- new CodeInstruction[]
+ new[]
{
// if (!ev.ShouldSever)
// goto shouldNotSever;
- new CodeInstruction(OpCodes.Ldloc, ev.LocalIndex).WithLabels(originalLabel),
+ new CodeInstruction(OpCodes.Ldloc, ev.LocalIndex),
new(OpCodes.Callvirt, PropertyGetter(typeof(InteractingScp330EventArgs), nameof(InteractingScp330EventArgs.ShouldSever))),
new(OpCodes.Brfalse, shouldNotSever),
-
- // ev.Player.EnableEffect("SevereHands", 1, 0f, false)
- new(OpCodes.Ldloc, ev.LocalIndex),
- new(OpCodes.Callvirt, PropertyGetter(typeof(InteractingScp330EventArgs), nameof(InteractingScp330EventArgs.Player))),
- new(OpCodes.Ldstr, nameof(SeveredHands)),
- new(OpCodes.Ldc_I4_1),
- new(OpCodes.Ldc_R4, 0f),
- new(OpCodes.Ldc_I4_0),
- new(OpCodes.Callvirt, Method(typeof(Player), nameof(Player.EnableEffect), new[] { typeof(string), typeof(byte), typeof(float), typeof(bool) })),
- new(OpCodes.Pop),
-
- // return;
- new(OpCodes.Ret),
+ new(OpCodes.Br, enableEffectLabel),
});
// This will let us jump to the taken candies code and lock until ldarg_0, meaning we allow base game logic handle candy adding.
@@ -157,28 +109,5 @@ private static IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions);
}
-
- private static bool ServerProcessPickup(ReferenceHub player, CandyKindID candy, out Scp330Bag bag)
- {
- if (!Scp330Bag.TryGetBag(player, out bag))
- {
- player.inventory.ServerAddItem(ItemType.SCP330);
-
- if (!Scp330Bag.TryGetBag(player, out bag))
- return false;
-
- bag.Candies = new List { candy };
- bag.ServerRefreshBag();
-
- return true;
- }
-
- bool result = bag.TryAddSpecific(candy);
-
- if (bag.AcquisitionAlreadyReceived)
- bag.ServerRefreshBag();
-
- return result;
- }
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Events/Scp939/PlacedAmnesticCloud.cs b/EXILED/Exiled.Events/Patches/Events/Scp939/PlacedAmnesticCloud.cs
new file mode 100644
index 000000000..ac224990f
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Events/Scp939/PlacedAmnesticCloud.cs
@@ -0,0 +1,73 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Events.Scp939
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using Exiled.API.Features.Pools;
+ using Exiled.Events.Attributes;
+ using Exiled.Events.EventArgs.Scp939;
+ using Exiled.Events.Handlers;
+ using HarmonyLib;
+ using PlayerRoles.PlayableScps.Scp939;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches
+ /// to add the event.
+ ///
+ [EventPatch(typeof(Scp939), nameof(Scp939.PlacedAmnesticCloud))]
+ [HarmonyPatch(typeof(Scp939AmnesticCloudAbility), nameof(Scp939AmnesticCloudAbility.OnStateEnabled))]
+ internal static class PlacedAmnesticCloud
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ LocalBuilder cloud = generator.DeclareLocal(typeof(Scp939AmnesticCloudInstance));
+
+ const int offset = -2;
+ int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Callvirt && x.OperandIs(Method(typeof(Scp939AmnesticCloudInstance), nameof(Scp939AmnesticCloudInstance.ServerSetup)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // Scp939AmnesticCloudInstance cloud = Object.Instantiate(this._instancePrefab)
+ new CodeInstruction(OpCodes.Dup),
+ new CodeInstruction(OpCodes.Stloc_S, cloud),
+ });
+
+ index = newInstructions.Count - 1;
+
+ // Scp939.OnPlacedAmnesticCloud(new PlacedAmnesticCloudEventArgs(this.Owner, cloud));
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // this.Owner
+ new CodeInstruction(OpCodes.Ldarg_0),
+ new(OpCodes.Callvirt, PropertyGetter(typeof(Scp939AmnesticCloudAbility), nameof(Scp939AmnesticCloudAbility.Owner))),
+
+ // cloud
+ new CodeInstruction(OpCodes.Ldloc_S, cloud),
+
+ // Scp939.OnPlacedAmnesticCloud(new PlacedAmnesticCloudEventArgs(this.Owner, cloud));
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(PlacedAmnesticCloudEventArgs))[0]),
+ new(OpCodes.Call, Method(typeof(Scp939), nameof(Scp939.OnPlacedAmnesticCloud))),
+ });
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Events/Server/Reporting.cs b/EXILED/Exiled.Events/Patches/Events/Server/Reporting.cs
index bba54c92e..d672d1831 100644
--- a/EXILED/Exiled.Events/Patches/Events/Server/Reporting.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Server/Reporting.cs
@@ -40,8 +40,8 @@ private static IEnumerable Transpiler(IEnumerable instruction.opcode == OpCodes.Newarr) + offset;
+ int offset = 2;
+ int index = newInstructions.FindLastIndex(instruction => instruction.opcode == OpCodes.Ldarg_S && instruction.operand is byte and 4) + offset;
Label ret = generator.DefineLabel();
diff --git a/EXILED/Exiled.Events/Patches/Fixes/ArmorDropPatch.cs b/EXILED/Exiled.Events/Patches/Fixes/ArmorDropPatch.cs
new file mode 100644
index 000000000..a924f6808
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Fixes/ArmorDropPatch.cs
@@ -0,0 +1,55 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Fixes
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using Exiled.API.Features.Pools;
+ using HarmonyLib;
+ using InventorySystem;
+ using InventorySystem.Items;
+ using InventorySystem.Items.Armor;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches to fix https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/230 bug.
+ ///
+ [HarmonyPatch(typeof(BodyArmorUtils), nameof(BodyArmorUtils.RemoveEverythingExceedingLimits))]
+ internal static class ArmorDropPatch
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ Label continueLabel = generator.DefineLabel();
+ int continueIndex = newInstructions.FindIndex(x => x.Is(OpCodes.Call, Method(typeof(Dictionary.Enumerator), nameof(Dictionary.Enumerator.MoveNext)))) - 1;
+ newInstructions[continueIndex].WithLabels(continueLabel);
+
+ // before: if (keyValuePair.Value.Category != ItemCategory.Armor)
+ // after: if (keyValuePair.Value.Category != ItemCategory.Armor && keyValuePair.Value.Category != ItemCategory.None)
+ int index = newInstructions.FindIndex(x => x.Is(OpCodes.Ldc_I4_S, 9));
+
+ newInstructions.InsertRange(index, new CodeInstruction[]
+ {
+ // && keyValuePair.Value.Category != ItemCategory.None)
+ new(OpCodes.Ldloca_S, 4),
+ new(OpCodes.Call, PropertyGetter(typeof(KeyValuePair), nameof(KeyValuePair.Value))),
+ new(OpCodes.Ldfld, Field(typeof(ItemBase), nameof(ItemBase.Category))),
+ new(OpCodes.Ldc_I4_0),
+ new(OpCodes.Beq_S, continueLabel),
+ });
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Fixes/Fix106RegenerationWithScp244.cs b/EXILED/Exiled.Events/Patches/Fixes/Fix106RegenerationWithScp244.cs
new file mode 100644
index 000000000..33c54d5ad
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Fixes/Fix106RegenerationWithScp244.cs
@@ -0,0 +1,72 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Fixes
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using API.Features.Pools;
+ using CustomPlayerEffects;
+ using HarmonyLib;
+ using InventorySystem.Items.Usables.Scp244.Hypothermia;
+ using PlayerRoles;
+ using PlayerRoles.PlayableScps.Scp106;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches the delegate.
+ /// Fix than SCP-106 regenerates slower in SCP-244 even if they are in stalk.
+ /// Bug reported to NW (https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/367).
+ ///
+ [HarmonyPatch(typeof(Hypothermia), nameof(Hypothermia.Update))]
+ internal class Fix106RegenerationWithScp244
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ LocalBuilder scp106Role = generator.DeclareLocal(typeof(Scp106Role));
+ Label continueLabel = generator.DefineLabel();
+
+ int offset = 1;
+ int index = newInstructions.FindLastIndex(x => x.operand == (object)Method(typeof(SpawnProtected), nameof(SpawnProtected.CheckPlayer))) + offset;
+
+ Label skip = (Label)newInstructions[index].operand;
+
+ index += offset;
+
+ newInstructions[index].labels.Add(continueLabel);
+
+ newInstructions.InsertRange(index, new[]
+ {
+ // Scp106Role scp106Role = base.Hub.roleManager.CurrentRole as Scp106Role;
+ new CodeInstruction(OpCodes.Ldarg_0),
+ new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(StatusEffectBase), nameof(StatusEffectBase.Hub))),
+ new CodeInstruction(OpCodes.Ldfld, Field(typeof(ReferenceHub), nameof(ReferenceHub.roleManager))),
+ new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(PlayerRoleManager), nameof(PlayerRoleManager.CurrentRole))),
+ new CodeInstruction(OpCodes.Isinst, typeof(Scp106Role)),
+ new CodeInstruction(OpCodes.Stloc_S, scp106Role.LocalIndex),
+
+ // if (scp106Role is null) goto continueLabel
+ new CodeInstruction(OpCodes.Ldloc_S, scp106Role.LocalIndex),
+ new CodeInstruction(OpCodes.Brfalse_S, continueLabel),
+
+ // if (!scp106Role.IsSubmerged) goto skip
+ new CodeInstruction(OpCodes.Ldloc_S, scp106Role.LocalIndex),
+ new CodeInstruction(OpCodes.Callvirt, PropertyGetter(typeof(Scp106Role), nameof(Scp106Role.IsSubmerged))),
+ new CodeInstruction(OpCodes.Brtrue_S, skip),
+ });
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
diff --git a/EXILED/Exiled.Events/Patches/Fixes/GrenadePropertiesFix.cs b/EXILED/Exiled.Events/Patches/Fixes/GrenadePropertiesFix.cs
index 8c37c4f9a..e37fb8993 100644
--- a/EXILED/Exiled.Events/Patches/Fixes/GrenadePropertiesFix.cs
+++ b/EXILED/Exiled.Events/Patches/Fixes/GrenadePropertiesFix.cs
@@ -8,6 +8,7 @@
namespace Exiled.Events.Patches.Fixes
{
using System.Collections.Generic;
+ using System.Linq;
using System.Reflection.Emit;
using API.Features.Items;
@@ -58,7 +59,7 @@ private static IEnumerable Transpiler(IEnumerable !x.IsGenericMethod && x.Name is nameof(Item.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemBase))),
new(OpCodes.Isinst, typeof(Throwable)),
new(OpCodes.Dup),
new(OpCodes.Stloc_S, throwable.LocalIndex),
diff --git a/EXILED/Exiled.Events/Patches/Fixes/Jailbird914CoarseFix.cs b/EXILED/Exiled.Events/Patches/Fixes/Jailbird914CoarseFix.cs
new file mode 100644
index 000000000..680e40608
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Fixes/Jailbird914CoarseFix.cs
@@ -0,0 +1,61 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Events.Player
+{
+#pragma warning disable IDE0060
+
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using API.Features;
+ using API.Features.Pools;
+
+ using HarmonyLib;
+ using InventorySystem.Items.Jailbird;
+ using Mirror;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches .
+ /// Bug reported to NW (https://trello.com/c/kyr3hV9B).
+ ///
+ [HarmonyPatch(typeof(JailbirdDeteriorationTracker), nameof(JailbirdDeteriorationTracker.Setup))]
+ internal static class Jailbird914CoarseFix
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ int offset = -1;
+ int index = newInstructions.FindIndex(i => i.opcode == OpCodes.Blt_S) + offset;
+
+ List
[HarmonyPatch(typeof(FpcMotor), nameof(FpcMotor.UpdatePosition))]
#pragma warning disable SA1402 // File may only contain a single type
@@ -62,4 +64,4 @@ private static IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions);
}
}
-}
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Fixes/WarheadConfigLockGateFix.cs b/EXILED/Exiled.Events/Patches/Fixes/WarheadConfigLockGateFix.cs
new file mode 100644
index 000000000..2c65c58a8
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Fixes/WarheadConfigLockGateFix.cs
@@ -0,0 +1,45 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Fixes
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using API.Features.Pools;
+ using Footprinting;
+ using HarmonyLib;
+ using Interactables.Interobjects.DoorUtils;
+ using InventorySystem;
+ using InventorySystem.Items.Firearms.Ammo;
+ using InventorySystem.Items.Pickups;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches delegate.
+ /// Fix than NW config "lock_gates_on_countdown"
+ /// reported https://git.scpslgame.com/northwood-qa/scpsl-bug-reporting/-/issues/316.
+ ///
+ [HarmonyPatch(typeof(DoorEventOpenerExtension), nameof(DoorEventOpenerExtension.Trigger))]
+ internal class WarheadConfigLockGateFix
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ // replace Contains with StartWith
+ int index = newInstructions.FindIndex(x => x.operand == (object)Method(typeof(string), nameof(string.Contains), new[] { typeof(string) }));
+ newInstructions[index].operand = Method(typeof(string), nameof(string.StartsWith), new System.Type[] { typeof(string) });
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
diff --git a/EXILED/Exiled.Events/Patches/Generic/DoorList.cs b/EXILED/Exiled.Events/Patches/Generic/DoorList.cs
index 1b63ec329..4f1ce6e33 100644
--- a/EXILED/Exiled.Events/Patches/Generic/DoorList.cs
+++ b/EXILED/Exiled.Events/Patches/Generic/DoorList.cs
@@ -50,7 +50,7 @@ private static void Postfix(DoorVariant __instance)
foreach (DoorVariant subDoor in checkpoint.Base.SubDoors)
{
subDoor.RegisterRooms();
- BreakableDoor targetDoor = Door.Get(subDoor).Cast();
+ BreakableDoor targetDoor = Door.Get(subDoor);
targetDoor.ParentCheckpointDoor = checkpoint;
checkpoint.SubDoorsValue.Add(targetDoor);
diff --git a/EXILED/Exiled.Events/Patches/Generic/GetCustomAmmoLimit.cs b/EXILED/Exiled.Events/Patches/Generic/GetCustomAmmoLimit.cs
new file mode 100644
index 000000000..eb533862e
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Generic/GetCustomAmmoLimit.cs
@@ -0,0 +1,35 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Generic
+{
+ using System;
+
+ using Exiled.API.Extensions;
+ using Exiled.API.Features;
+ using HarmonyLib;
+ using InventorySystem.Configs;
+ using UnityEngine;
+
+ ///
+ /// Patches the delegate.
+ /// Sync .
+ /// Changes to .
+ ///
+ [HarmonyPatch(typeof(InventoryLimits), nameof(InventoryLimits.GetAmmoLimit), new Type[] { typeof(ItemType), typeof(ReferenceHub) })]
+ internal static class GetCustomAmmoLimit
+ {
+#pragma warning disable SA1313
+ private static void Postfix(ItemType ammoType, ReferenceHub player, ref ushort __result)
+ {
+ if (!Player.TryGet(player, out Player ply) || !ply.CustomAmmoLimits.TryGetValue(ammoType.GetAmmoType(), out ushort limit))
+ return;
+
+ __result = (ushort)Mathf.Clamp(limit + __result - InventoryLimits.GetAmmoLimit(null, ammoType), ushort.MinValue, ushort.MaxValue);
+ }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Generic/GetCustomCategoryLimit.cs b/EXILED/Exiled.Events/Patches/Generic/GetCustomCategoryLimit.cs
new file mode 100644
index 000000000..8ceff7538
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Generic/GetCustomCategoryLimit.cs
@@ -0,0 +1,34 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Generic
+{
+ using System;
+
+ using Exiled.API.Features;
+ using HarmonyLib;
+ using InventorySystem.Configs;
+ using UnityEngine;
+
+ ///
+ /// Patches the delegate.
+ /// Sync , .
+ /// Changes to .
+ ///
+ [HarmonyPatch(typeof(InventoryLimits), nameof(InventoryLimits.GetCategoryLimit), new Type[] { typeof(ItemCategory), typeof(ReferenceHub), })]
+ internal static class GetCustomCategoryLimit
+ {
+#pragma warning disable SA1313
+ private static void Postfix(ItemCategory category, ReferenceHub player, ref sbyte __result)
+ {
+ if (!Player.TryGet(player, out Player ply) || !ply.CustomCategoryLimits.TryGetValue(category, out sbyte limit))
+ return;
+
+ __result = (sbyte)Mathf.Clamp(limit + __result - InventoryLimits.GetCategoryLimit(null, category), sbyte.MinValue, sbyte.MaxValue);
+ }
+ }
+}
diff --git a/EXILED/Exiled.Events/Patches/Generic/OfflineModeIds.cs b/EXILED/Exiled.Events/Patches/Generic/OfflineModeIds.cs
new file mode 100644
index 000000000..46c412dc3
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Generic/OfflineModeIds.cs
@@ -0,0 +1,164 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Generic
+{
+#pragma warning disable SA1402 // File may only contain a single type
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using API.Features.Pools;
+ using CentralAuth;
+ using HarmonyLib;
+ using PluginAPI.Core.Interfaces;
+ using PluginAPI.Events;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches to add an @offline suffix to UserIds in Offline Mode.
+ ///
+ [HarmonyPatch(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.Start))]
+ internal static class OfflineModeIds
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ const int offset = -1;
+ int index = newInstructions.FindLastIndex(instruction => instruction.opcode == OpCodes.Call && instruction.OperandIs(PropertySetter(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.UserId)))) + offset;
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ new CodeInstruction(OpCodes.Call, Method(typeof(OfflineModeIds), nameof(BuildUserId))),
+ });
+
+ for (int i = 0; i < newInstructions.Count; i++)
+ yield return newInstructions[i];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+
+ private static string BuildUserId(string userId) => $"{userId}@offline";
+ }
+
+ ///
+ /// Patches to add the player's UserId to the dictionary.
+ ///
+ [HarmonyPatch(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.Start))]
+ internal static class OfflineModePlayerIds
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ Label skipLabel = generator.DefineLabel();
+
+ const int offset = 1;
+ int index = newInstructions.FindLastIndex(instruction => instruction.opcode == OpCodes.Call && instruction.OperandIs(PropertySetter(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.UserId)))) + offset;
+
+ // if (!Player.PlayersUserIds.ContainsKey(this.UserId))
+ // Player.PlayersUserIds.Add(this.UserId, this._hub);
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // if (Player.PlayersUserIds.ContainsKey(this.UserId)) goto skip;
+ new(OpCodes.Ldsfld, Field(typeof(PluginAPI.Core.Player), nameof(PluginAPI.Core.Player.PlayersUserIds))),
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Call, PropertyGetter(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.UserId))),
+ new(OpCodes.Callvirt, Method(typeof(Dictionary), nameof(Dictionary.ContainsKey))),
+ new(OpCodes.Brtrue_S, skipLabel),
+
+ // Player.PlayersUserIds.Add(this.UserId, this._hub);
+ new(OpCodes.Ldsfld, Field(typeof(PluginAPI.Core.Player), nameof(PluginAPI.Core.Player.PlayersUserIds))),
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Call, PropertyGetter(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager.UserId))),
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Ldfld, Field(typeof(PlayerAuthenticationManager), nameof(PlayerAuthenticationManager._hub))),
+ new(OpCodes.Callvirt, Method(typeof(Dictionary), nameof(Dictionary.Add))),
+
+ // skip:
+ new CodeInstruction(OpCodes.Nop).WithLabels(skipLabel),
+ });
+
+ for (int i = 0; i < newInstructions.Count; i++)
+ yield return newInstructions[i];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+
+ ///
+ /// Patches to prevent it from executing the event when the server is in offline mode.
+ ///
+ [HarmonyPatch(typeof(ReferenceHub), nameof(ReferenceHub.Start))]
+ internal static class OfflineModeReferenceHub
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ const int offset = 1;
+ int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Callvirt) + offset;
+
+ Label returnLabel = generator.DefineLabel();
+
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ new CodeInstruction(OpCodes.Br_S, returnLabel),
+ });
+
+ newInstructions[newInstructions.Count - 1].WithLabels(returnLabel);
+
+ for (int i = 0; i < newInstructions.Count; i++)
+ yield return newInstructions[i];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+
+ ///
+ /// Patches to execute the event when the server is in offline mode.
+ ///
+ [HarmonyPatch(typeof(NicknameSync), nameof(NicknameSync.UserCode_CmdSetNick__String))]
+ internal static class OfflineModeJoin
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ const int offset = 1;
+ int index = newInstructions.FindIndex(x => x.opcode == OpCodes.Callvirt && x.OperandIs(Method(typeof(CharacterClassManager), nameof(CharacterClassManager.SyncServerCmdBinding)))) + offset;
+
+ // EventManager.ExecuteEvent(new PlayerJoinedEvent(this._hub));
+ newInstructions.InsertRange(
+ index,
+ new[]
+ {
+ // EventManager.ExecuteEvent(new PlayerJoinedEvent(this._hub));
+ new CodeInstruction(OpCodes.Ldarg_0),
+ new CodeInstruction(OpCodes.Ldfld, Field(typeof(NicknameSync), nameof(NicknameSync._hub))),
+ new CodeInstruction(OpCodes.Call, Method(typeof(OfflineModeJoin), nameof(ExecuteNwEvent))),
+ });
+
+ for (int i = 0; i < newInstructions.Count; i++)
+ yield return newInstructions[i];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+
+ private static void ExecuteNwEvent(ReferenceHub hub)
+ {
+ EventManager.ExecuteEvent(new PlayerJoinedEvent(hub));
+ }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Generic/PickupControlPatch.cs b/EXILED/Exiled.Events/Patches/Generic/PickupControlPatch.cs
index c76a8f247..fc319c474 100644
--- a/EXILED/Exiled.Events/Patches/Generic/PickupControlPatch.cs
+++ b/EXILED/Exiled.Events/Patches/Generic/PickupControlPatch.cs
@@ -9,6 +9,7 @@ namespace Exiled.Events.Patches.Generic
{
using System;
using System.Collections.Generic;
+ using System.Linq;
using System.Reflection.Emit;
using API.Features.Pickups;
@@ -48,11 +49,11 @@ private static IEnumerable Transpiler(
{
// pickup = Pickup.Get(pickupBase);
new(OpCodes.Ldloc_0),
- new(OpCodes.Call, Method(typeof(Pickup), nameof(Pickup.Get), new[] { typeof(ItemPickupBase) })),
+ new(OpCodes.Call, GetDeclaredMethods(typeof(Pickup)).First(x => !x.IsGenericMethod && x.Name is nameof(Pickup.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemPickupBase))),
// Item.Get(itemBase);
new(OpCodes.Ldarg_0),
- new(OpCodes.Call, Method(typeof(Item), nameof(Item.Get), new[] { typeof(ItemBase) })),
+ new(OpCodes.Call, GetDeclaredMethods(typeof(Item)).First(x => !x.IsGenericMethod && x.Name is nameof(Item.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemBase))),
// pickup.ReadItemInfo(item);
new(OpCodes.Callvirt, Method(typeof(Pickup), nameof(Pickup.ReadItemInfo))),
@@ -79,7 +80,7 @@ private static IEnumerable Transpiler(IEnumerable !x.IsGenericMethod && x.Name is nameof(Pickup.Get) && x.GetParameters().Length is 1 && x.GetParameters()[0].ParameterType == typeof(ItemPickupBase))),
new(OpCodes.Pop),
});
From 724131bbf45d611bdc2a8b24532b648e5e2eb4ab Mon Sep 17 00:00:00 2001
From: Misaka-ZeroTwo <45165615+Misaka-ZeroTwo@users.noreply.github.com>
Date: Mon, 12 Aug 2024 02:56:36 +0800
Subject: [PATCH 16/39] Version bump
---
EXILED/EXILED.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/EXILED/EXILED.props b/EXILED/EXILED.props
index 953b133c6..8085b8f20 100644
--- a/EXILED/EXILED.props
+++ b/EXILED/EXILED.props
@@ -15,7 +15,7 @@
- 8.11.0
+ 8.12.0-rc.1
false
From ee317e7cc868e990c9e599ec08e2326b8de53dd5 Mon Sep 17 00:00:00 2001
From: VALERA771 <72030575+VALERA771@users.noreply.github.com>
Date: Mon, 12 Aug 2024 19:25:26 +0300
Subject: [PATCH 17/39] `[Exiled::API]` Removing breaking changes (#54)
Co-authored-by: Nameless <85962933+Misfiy@users.noreply.github.com>
---
EXILED/Exiled.API/Features/Player.cs | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs
index 0885000f7..314817264 100644
--- a/EXILED/Exiled.API/Features/Player.cs
+++ b/EXILED/Exiled.API/Features/Player.cs
@@ -2411,6 +2411,17 @@ public ushort GetAmmoLimit(AmmoType type, bool ignoreArmor = false)
return InventorySystem.Configs.InventoryLimits.GetAmmoLimit(type.GetItemType(), referenceHub);
}
+ ///
+ /// Gets the maximum amount of ammo the player can hold, given the ammo .
+ ///
+ /// The of the ammo to check.
+ /// The maximum amount of ammo this player can carry.
+ [Obsolete("Use Player::GetAmmoLimit(AmmoType, bool) instead.")]
+ public int GetAmmoLimit(AmmoType type)
+ {
+ return (int)InventorySystem.Configs.InventoryLimits.GetAmmoLimit(type.GetItemType(), referenceHub);
+ }
+
///
/// Gets the maximum amount of ammo the player can hold, given the ammo .
/// This limit will scale with the armor the player is wearing.
From b66b2fd95e3811a0dea662b584ba925042a6c75e Mon Sep 17 00:00:00 2001
From: IRacle <79921583+IRacle1@users.noreply.github.com>
Date: Mon, 12 Aug 2024 20:15:02 +0300
Subject: [PATCH 18/39] `Scp330` sound fix (#55)
Co-authored-by: Misaka-ZeroTwo <45165615+Misaka-ZeroTwo@users.noreply.github.com>
---
.../Scp330/InteractingScp330EventArgs.cs | 9 ++-
.../Events/Scp330/InteractingScp330.cs | 67 +++++++++++++------
2 files changed, 54 insertions(+), 22 deletions(-)
diff --git a/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs
index b87238842..45a587755 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs
@@ -35,6 +35,7 @@ public InteractingScp330EventArgs(Player player, int usage)
Candy = Scp330Candies.GetRandom();
UsageCount = usage;
ShouldSever = usage >= 2;
+ ShouldPlaySound = true;
IsAllowed = Player.IsHuman;
}
@@ -53,10 +54,16 @@ public InteractingScp330EventArgs(Player player, int usage)
///
public bool ShouldSever { get; set; }
+ ///
+ /// Gets or sets a value indicating whether the sound should be played.
+ ///
+ /// It won't work if = .
+ public bool ShouldPlaySound { get; set; }
+
///
/// Gets or sets a value indicating whether the player is allowed to interact with SCP-330.
///
- public bool IsAllowed { get; set; } = true;
+ public bool IsAllowed { get; set; }
///
/// Gets the triggering the event.
diff --git a/EXILED/Exiled.Events/Patches/Events/Scp330/InteractingScp330.cs b/EXILED/Exiled.Events/Patches/Events/Scp330/InteractingScp330.cs
index c8ff97d32..bcb4f5308 100644
--- a/EXILED/Exiled.Events/Patches/Events/Scp330/InteractingScp330.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Scp330/InteractingScp330.cs
@@ -10,6 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp330
using System.Collections.Generic;
using System.Reflection.Emit;
+ using Exiled.API.Features;
using Exiled.API.Features.Pools;
using Exiled.Events.Attributes;
using Exiled.Events.EventArgs.Scp330;
@@ -17,6 +18,7 @@ namespace Exiled.Events.Patches.Events.Scp330
using HarmonyLib;
using Interactables.Interobjects;
using InventorySystem.Items.Usables.Scp330;
+ using PluginAPI.Events;
using static HarmonyLib.AccessTools;
@@ -34,9 +36,7 @@ private static IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions);
- Label shouldNotSever = generator.DefineLabel();
Label returnLabel = generator.DefineLabel();
- Label enableEffectLabel = generator.DefineLabel();
LocalBuilder ev = generator.DeclareLocal(typeof(InteractingScp330EventArgs));
@@ -74,35 +74,60 @@ private static IEnumerable Transpiler(IEnumerable instruction.Calls(Method(typeof(Scp330Interobject), nameof(Scp330Interobject.RpcMakeSound)))) + addShouldSeverOffset;
+ /* next code will used to override sound rpc check by EXILED
+ * old:
+ * if (args.PlaySound)
+ * new:
+ * if (args.PlaySound && ev.PlaySound)
+ */
- int serverEffectLocationStart = -1;
- int enableEffect = newInstructions.FindLastIndex(
- instruction => instruction.LoadsField(Field(typeof(ReferenceHub), nameof(ReferenceHub.playerEffectsController)))) + serverEffectLocationStart;
+ offset = 1;
+ index = newInstructions.FindLastIndex(
+ instruction => instruction.Calls(PropertyGetter(typeof(PlayerInteractScp330Event), nameof(PlayerInteractScp330Event.PlaySound)))) + offset;
- newInstructions[enableEffect].WithLabels(enableEffectLabel);
newInstructions.InsertRange(
- addShouldSeverIndex,
+ index,
+ new[]
+ {
+ // load ev.ShouldPlaySound and or operation with nw property.
+ new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex),
+ new(OpCodes.Callvirt, PropertyGetter(typeof(InteractingScp330EventArgs), nameof(InteractingScp330EventArgs.ShouldPlaySound))),
+ new(OpCodes.And),
+ });
+
+ /* next code will used to override Sever check by EXILED
+ * old:
+ * if (args.AllowPunishment && uses >= 2)
+ * new:
+ * if (args.AllowPunishment && ev.ShouldSever)
+ */
+
+ // set `notSeverLabel`
+ offset = -1;
+ index = newInstructions.FindLastIndex(
+ instruction => instruction.LoadsField(Field(typeof(Scp330Interobject), nameof(Scp330Interobject._takenCandies)))) + offset;
+
+ Label notSeverLabel = newInstructions[index].labels[0];
+
+ offset = 2;
+ index = newInstructions.FindLastIndex(
+ instruction => instruction.Calls(PropertyGetter(typeof(PlayerInteractScp330Event), nameof(PlayerInteractScp330Event.AllowPunishment)))) + offset;
+
+ // remove `uses >= 2` check, to override that by ev.ShouldSever
+ newInstructions.RemoveRange(index, 3);
+
+ newInstructions.InsertRange(
+ index,
new[]
{
// if (!ev.ShouldSever)
// goto shouldNotSever;
- new CodeInstruction(OpCodes.Ldloc, ev.LocalIndex),
+ new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex),
new(OpCodes.Callvirt, PropertyGetter(typeof(InteractingScp330EventArgs), nameof(InteractingScp330EventArgs.ShouldSever))),
- new(OpCodes.Brfalse, shouldNotSever),
- new(OpCodes.Br, enableEffectLabel),
+ new(OpCodes.Brfalse_S, notSeverLabel),
});
- // This will let us jump to the taken candies code and lock until ldarg_0, meaning we allow base game logic handle candy adding.
- int addTakenCandiesOffset = -1;
- int addTakenCandiesIndex = newInstructions.FindLastIndex(
- instruction => instruction.LoadsField(Field(typeof(Scp330Interobject), nameof(Scp330Interobject._takenCandies)))) + addTakenCandiesOffset;
-
- newInstructions[addTakenCandiesIndex].WithLabels(shouldNotSever);
- newInstructions[newInstructions.Count - 1].WithLabels(returnLabel);
+ newInstructions[newInstructions.Count - 1].labels.Add(returnLabel);
for (int z = 0; z < newInstructions.Count; z++)
yield return newInstructions[z];
From f4abc04b64cac69b4c57680553859e776a3a3a7d Mon Sep 17 00:00:00 2001
From: Misaka-ZeroTwo <45165615+Misaka-ZeroTwo@users.noreply.github.com>
Date: Tue, 13 Aug 2024 01:17:11 +0800
Subject: [PATCH 19/39] v8.12.0-rc.2 (#57)
* `[Exiled::API]` Removing breaking changes (#54)
Co-authored-by: VALERA771 <72030575+VALERA771@users.noreply.github.com>
Co-authored-by: Nameless <85962933+Misfiy@users.noreply.github.com>
* `Scp330` sound fix (#55)
Co-authored-by: IRacle <79921583+IRacle1@users.noreply.github.com>
Co-authored-by: Misaka-ZeroTwo <45165615+Misaka-ZeroTwo@users.noreply.github.com>
---
EXILED/Exiled.API/Features/Player.cs | 11 +++
.../Scp330/InteractingScp330EventArgs.cs | 9 ++-
.../Events/Scp330/InteractingScp330.cs | 67 +++++++++++++------
3 files changed, 65 insertions(+), 22 deletions(-)
diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs
index 0885000f7..314817264 100644
--- a/EXILED/Exiled.API/Features/Player.cs
+++ b/EXILED/Exiled.API/Features/Player.cs
@@ -2411,6 +2411,17 @@ public ushort GetAmmoLimit(AmmoType type, bool ignoreArmor = false)
return InventorySystem.Configs.InventoryLimits.GetAmmoLimit(type.GetItemType(), referenceHub);
}
+ ///
+ /// Gets the maximum amount of ammo the player can hold, given the ammo .
+ ///
+ /// The of the ammo to check.
+ /// The maximum amount of ammo this player can carry.
+ [Obsolete("Use Player::GetAmmoLimit(AmmoType, bool) instead.")]
+ public int GetAmmoLimit(AmmoType type)
+ {
+ return (int)InventorySystem.Configs.InventoryLimits.GetAmmoLimit(type.GetItemType(), referenceHub);
+ }
+
///
/// Gets the maximum amount of ammo the player can hold, given the ammo .
/// This limit will scale with the armor the player is wearing.
diff --git a/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs
index b87238842..45a587755 100644
--- a/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Scp330/InteractingScp330EventArgs.cs
@@ -35,6 +35,7 @@ public InteractingScp330EventArgs(Player player, int usage)
Candy = Scp330Candies.GetRandom();
UsageCount = usage;
ShouldSever = usage >= 2;
+ ShouldPlaySound = true;
IsAllowed = Player.IsHuman;
}
@@ -53,10 +54,16 @@ public InteractingScp330EventArgs(Player player, int usage)
///
public bool ShouldSever { get; set; }
+ ///
+ /// Gets or sets a value indicating whether the sound should be played.
+ ///
+ /// It won't work if = .
+ public bool ShouldPlaySound { get; set; }
+
///
/// Gets or sets a value indicating whether the player is allowed to interact with SCP-330.
///
- public bool IsAllowed { get; set; } = true;
+ public bool IsAllowed { get; set; }
///
/// Gets the triggering the event.
diff --git a/EXILED/Exiled.Events/Patches/Events/Scp330/InteractingScp330.cs b/EXILED/Exiled.Events/Patches/Events/Scp330/InteractingScp330.cs
index c8ff97d32..bcb4f5308 100644
--- a/EXILED/Exiled.Events/Patches/Events/Scp330/InteractingScp330.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Scp330/InteractingScp330.cs
@@ -10,6 +10,7 @@ namespace Exiled.Events.Patches.Events.Scp330
using System.Collections.Generic;
using System.Reflection.Emit;
+ using Exiled.API.Features;
using Exiled.API.Features.Pools;
using Exiled.Events.Attributes;
using Exiled.Events.EventArgs.Scp330;
@@ -17,6 +18,7 @@ namespace Exiled.Events.Patches.Events.Scp330
using HarmonyLib;
using Interactables.Interobjects;
using InventorySystem.Items.Usables.Scp330;
+ using PluginAPI.Events;
using static HarmonyLib.AccessTools;
@@ -34,9 +36,7 @@ private static IEnumerable Transpiler(IEnumerable newInstructions = ListPool.Pool.Get(instructions);
- Label shouldNotSever = generator.DefineLabel();
Label returnLabel = generator.DefineLabel();
- Label enableEffectLabel = generator.DefineLabel();
LocalBuilder ev = generator.DeclareLocal(typeof(InteractingScp330EventArgs));
@@ -74,35 +74,60 @@ private static IEnumerable Transpiler(IEnumerable instruction.Calls(Method(typeof(Scp330Interobject), nameof(Scp330Interobject.RpcMakeSound)))) + addShouldSeverOffset;
+ /* next code will used to override sound rpc check by EXILED
+ * old:
+ * if (args.PlaySound)
+ * new:
+ * if (args.PlaySound && ev.PlaySound)
+ */
- int serverEffectLocationStart = -1;
- int enableEffect = newInstructions.FindLastIndex(
- instruction => instruction.LoadsField(Field(typeof(ReferenceHub), nameof(ReferenceHub.playerEffectsController)))) + serverEffectLocationStart;
+ offset = 1;
+ index = newInstructions.FindLastIndex(
+ instruction => instruction.Calls(PropertyGetter(typeof(PlayerInteractScp330Event), nameof(PlayerInteractScp330Event.PlaySound)))) + offset;
- newInstructions[enableEffect].WithLabels(enableEffectLabel);
newInstructions.InsertRange(
- addShouldSeverIndex,
+ index,
+ new[]
+ {
+ // load ev.ShouldPlaySound and or operation with nw property.
+ new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex),
+ new(OpCodes.Callvirt, PropertyGetter(typeof(InteractingScp330EventArgs), nameof(InteractingScp330EventArgs.ShouldPlaySound))),
+ new(OpCodes.And),
+ });
+
+ /* next code will used to override Sever check by EXILED
+ * old:
+ * if (args.AllowPunishment && uses >= 2)
+ * new:
+ * if (args.AllowPunishment && ev.ShouldSever)
+ */
+
+ // set `notSeverLabel`
+ offset = -1;
+ index = newInstructions.FindLastIndex(
+ instruction => instruction.LoadsField(Field(typeof(Scp330Interobject), nameof(Scp330Interobject._takenCandies)))) + offset;
+
+ Label notSeverLabel = newInstructions[index].labels[0];
+
+ offset = 2;
+ index = newInstructions.FindLastIndex(
+ instruction => instruction.Calls(PropertyGetter(typeof(PlayerInteractScp330Event), nameof(PlayerInteractScp330Event.AllowPunishment)))) + offset;
+
+ // remove `uses >= 2` check, to override that by ev.ShouldSever
+ newInstructions.RemoveRange(index, 3);
+
+ newInstructions.InsertRange(
+ index,
new[]
{
// if (!ev.ShouldSever)
// goto shouldNotSever;
- new CodeInstruction(OpCodes.Ldloc, ev.LocalIndex),
+ new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex),
new(OpCodes.Callvirt, PropertyGetter(typeof(InteractingScp330EventArgs), nameof(InteractingScp330EventArgs.ShouldSever))),
- new(OpCodes.Brfalse, shouldNotSever),
- new(OpCodes.Br, enableEffectLabel),
+ new(OpCodes.Brfalse_S, notSeverLabel),
});
- // This will let us jump to the taken candies code and lock until ldarg_0, meaning we allow base game logic handle candy adding.
- int addTakenCandiesOffset = -1;
- int addTakenCandiesIndex = newInstructions.FindLastIndex(
- instruction => instruction.LoadsField(Field(typeof(Scp330Interobject), nameof(Scp330Interobject._takenCandies)))) + addTakenCandiesOffset;
-
- newInstructions[addTakenCandiesIndex].WithLabels(shouldNotSever);
- newInstructions[newInstructions.Count - 1].WithLabels(returnLabel);
+ newInstructions[newInstructions.Count - 1].labels.Add(returnLabel);
for (int z = 0; z < newInstructions.Count; z++)
yield return newInstructions[z];
From c82bdc292282f61d40b3cbbf1e2f3b285e364f1b Mon Sep 17 00:00:00 2001
From: Misaka-ZeroTwo <45165615+Misaka-ZeroTwo@users.noreply.github.com>
Date: Tue, 13 Aug 2024 01:18:07 +0800
Subject: [PATCH 20/39] Version bump
---
EXILED/EXILED.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/EXILED/EXILED.props b/EXILED/EXILED.props
index 8085b8f20..52056665f 100644
--- a/EXILED/EXILED.props
+++ b/EXILED/EXILED.props
@@ -15,7 +15,7 @@
- 8.12.0-rc.1
+ 8.12.0-rc.2
false
From 0ab903797dfa7cc5fbdf5199a97df9eb18de323c Mon Sep 17 00:00:00 2001
From: Nameless <85962933+Misfiy@users.noreply.github.com>
Date: Mon, 12 Aug 2024 20:21:52 +0200
Subject: [PATCH 21/39] Fix Npc (#58)
---
EXILED/Exiled.API/Features/Npc.cs | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/EXILED/Exiled.API/Features/Npc.cs b/EXILED/Exiled.API/Features/Npc.cs
index 6824cbab9..52f339dea 100644
--- a/EXILED/Exiled.API/Features/Npc.cs
+++ b/EXILED/Exiled.API/Features/Npc.cs
@@ -5,26 +5,22 @@
//
// -----------------------------------------------------------------------
-#nullable enable
namespace Exiled.API.Features
{
+#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using CommandSystem;
-
using Exiled.API.Enums;
using Exiled.API.Extensions;
using Exiled.API.Features.Components;
+ using Exiled.API.Features.Roles;
using Footprinting;
-
using MEC;
-
using Mirror;
-
using PlayerRoles;
-
using UnityEngine;
using Object = UnityEngine.Object;
@@ -60,8 +56,8 @@ public override Vector3 Position
set
{
base.Position = value;
- if (Role is Roles.FpcRole fpcRole)
- fpcRole.RelativePosition = new(value);
+ if (Role is FpcRole fpcRole)
+ fpcRole.ClientRelativePosition = new(value);
}
}
From 119f930f5a910e3c645fe01c0915681984b6eab8 Mon Sep 17 00:00:00 2001
From: Misaka-ZeroTwo <45165615+Misaka-ZeroTwo@users.noreply.github.com>
Date: Wed, 14 Aug 2024 00:08:25 +0800
Subject: [PATCH 22/39] Fix breaking change (#61)
Fix a breaking change in #9
---
EXILED/Exiled.API/Features/Player.cs | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs
index 314817264..0a2b20b4f 100644
--- a/EXILED/Exiled.API/Features/Player.cs
+++ b/EXILED/Exiled.API/Features/Player.cs
@@ -2478,6 +2478,15 @@ public void ResetAmmoLimit(AmmoType ammoType)
/// If the player has a custom limit for the specific .
public bool HasCustomAmmoLimit(AmmoType ammoType) => CustomAmmoLimits.ContainsKey(ammoType);
+ ///
+ /// Gets the maximum amount of an the player can hold, based on the armor the player is wearing, as well as server configuration.
+ ///
+ /// The to check.
+ /// The maximum amount of items in the category that the player can hold.
+ [Obsolete("Use Player::GetCategoryLimit(ItemCategory, bool) instead.")]
+ public int GetCategoryLimit(ItemCategory category) =>
+ InventorySystem.Configs.InventoryLimits.GetCategoryLimit(category, referenceHub);
+
///
/// Gets the maximum amount of an the player can hold, based on the armor the player is wearing, as well as server configuration.
///
From 924eef52bc2100823775586af449b09ee882e423 Mon Sep 17 00:00:00 2001
From: Misaka-ZeroTwo <45165615+Misaka-ZeroTwo@users.noreply.github.com>
Date: Wed, 14 Aug 2024 00:10:20 +0800
Subject: [PATCH 23/39] v8.12.0-rc.3 (#62)
* Fix Npc (#58)
* Fix breaking change (#61)
---
EXILED/Exiled.API/Features/Npc.cs | 12 ++++--------
EXILED/Exiled.API/Features/Player.cs | 9 +++++++++
2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/EXILED/Exiled.API/Features/Npc.cs b/EXILED/Exiled.API/Features/Npc.cs
index 6824cbab9..52f339dea 100644
--- a/EXILED/Exiled.API/Features/Npc.cs
+++ b/EXILED/Exiled.API/Features/Npc.cs
@@ -5,26 +5,22 @@
//
// -----------------------------------------------------------------------
-#nullable enable
namespace Exiled.API.Features
{
+#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using CommandSystem;
-
using Exiled.API.Enums;
using Exiled.API.Extensions;
using Exiled.API.Features.Components;
+ using Exiled.API.Features.Roles;
using Footprinting;
-
using MEC;
-
using Mirror;
-
using PlayerRoles;
-
using UnityEngine;
using Object = UnityEngine.Object;
@@ -60,8 +56,8 @@ public override Vector3 Position
set
{
base.Position = value;
- if (Role is Roles.FpcRole fpcRole)
- fpcRole.RelativePosition = new(value);
+ if (Role is FpcRole fpcRole)
+ fpcRole.ClientRelativePosition = new(value);
}
}
diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs
index 314817264..0a2b20b4f 100644
--- a/EXILED/Exiled.API/Features/Player.cs
+++ b/EXILED/Exiled.API/Features/Player.cs
@@ -2478,6 +2478,15 @@ public void ResetAmmoLimit(AmmoType ammoType)
/// If the player has a custom limit for the specific .
public bool HasCustomAmmoLimit(AmmoType ammoType) => CustomAmmoLimits.ContainsKey(ammoType);
+ ///
+ /// Gets the maximum amount of an the player can hold, based on the armor the player is wearing, as well as server configuration.
+ ///
+ /// The to check.
+ /// The maximum amount of items in the category that the player can hold.
+ [Obsolete("Use Player::GetCategoryLimit(ItemCategory, bool) instead.")]
+ public int GetCategoryLimit(ItemCategory category) =>
+ InventorySystem.Configs.InventoryLimits.GetCategoryLimit(category, referenceHub);
+
///
/// Gets the maximum amount of an the player can hold, based on the armor the player is wearing, as well as server configuration.
///
From bbae123eeacf217bc818fa5de4c57b3c80a16a12 Mon Sep 17 00:00:00 2001
From: Misaka-ZeroTwo <45165615+Misaka-ZeroTwo@users.noreply.github.com>
Date: Wed, 14 Aug 2024 00:11:19 +0800
Subject: [PATCH 24/39] Version bump
---
EXILED/EXILED.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/EXILED/EXILED.props b/EXILED/EXILED.props
index 52056665f..e1ff99890 100644
--- a/EXILED/EXILED.props
+++ b/EXILED/EXILED.props
@@ -15,7 +15,7 @@
- 8.12.0-rc.2
+ 8.12.0-rc.3
false
From afdb0b216f43d6910453aaa34f483035e81614fd Mon Sep 17 00:00:00 2001
From: Misaka-ZeroTwo <45165615+Misaka-ZeroTwo@users.noreply.github.com>
Date: Wed, 14 Aug 2024 00:11:19 +0800
Subject: [PATCH 25/39] Version bump
---
EXILED/EXILED.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/EXILED/EXILED.props b/EXILED/EXILED.props
index 52056665f..e1ff99890 100644
--- a/EXILED/EXILED.props
+++ b/EXILED/EXILED.props
@@ -15,7 +15,7 @@
- 8.12.0-rc.2
+ 8.12.0-rc.3
false
From 904865e04f86424b77b27023aa7d138ed5dfe220 Mon Sep 17 00:00:00 2001
From: IRacle
Date: Wed, 14 Aug 2024 15:36:50 +0300
Subject: [PATCH 26/39] =?UTF-8?q?=F0=9F=92=80=F0=9F=92=80=F0=9F=92=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
EXILED/Exiled.API/Features/Player.cs | 9 ++-------
1 file changed, 2 insertions(+), 7 deletions(-)
diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs
index 0a2b20b4f..b5890c2a7 100644
--- a/EXILED/Exiled.API/Features/Player.cs
+++ b/EXILED/Exiled.API/Features/Player.cs
@@ -1277,13 +1277,8 @@ public static Player Get(GameObject gameObject)
if (Dictionary.TryGetValue(gameObject, out Player player))
return player;
- if (UnverifiedPlayers.TryGetValue(gameObject, out player))
- return player;
-
- if (ReferenceHub.TryGetHub(gameObject, out ReferenceHub hub))
- return new(hub);
-
- return null;
+ UnverifiedPlayers.TryGetValue(gameObject, out player);
+ return player;
}
///
From bcb78e70ede8b6bf7a022fe55bcd06b06f442430 Mon Sep 17 00:00:00 2001
From: IRacle
Date: Thu, 15 Aug 2024 14:38:35 +0300
Subject: [PATCH 27/39] `SendStaffMessage` fix
---
EXILED/Exiled.API/Features/Player.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs
index b5890c2a7..175d7731a 100644
--- a/EXILED/Exiled.API/Features/Player.cs
+++ b/EXILED/Exiled.API/Features/Player.cs
@@ -2288,7 +2288,7 @@ public void RemoteAdminMessage(string message, bool success = true, string plugi
/// if message was send; otherwise, .
public bool SendStaffMessage(string message, EncryptedChannelManager.EncryptedChannel channel = EncryptedChannelManager.EncryptedChannel.AdminChat)
{
- return ReferenceHub.encryptedChannelManager.TrySendMessageToClient("!" + NetId + message, channel);
+ return ReferenceHub.encryptedChannelManager.TrySendMessageToClient(NetId + "!" + message, channel);
}
///
@@ -2299,7 +2299,7 @@ public bool SendStaffMessage(string message, EncryptedChannelManager.EncryptedCh
/// if message was send; otherwise, .
public bool SendStaffPing(string message, EncryptedChannelManager.EncryptedChannel channel = EncryptedChannelManager.EncryptedChannel.AdminChat)
{
- return ReferenceHub.encryptedChannelManager.TrySendMessageToClient("!0" + message, channel);
+ return ReferenceHub.encryptedChannelManager.TrySendMessageToClient("0!" + message, channel);
}
///
From 53ce718f2522a0c967ebb72f010b699fe6cbff11 Mon Sep 17 00:00:00 2001
From: FoxWorn3365 <61429263+FoxWorn3365@users.noreply.github.com>
Date: Fri, 16 Aug 2024 01:25:00 +0200
Subject: [PATCH 28/39] Added the SCP-079 Recontaining event (#21)
* added the RecontainingEvent with the patch
* Fixed constructors
* consistency
* fixed logic skill issue
* Removed "useless" constructor
* corrected the typo
* fixed some typo
* final edit for the final request
* Apply suggestions from code review
* Apply suggestions from code review
Co-authored-by: VALERA771 <72030575+VALERA771@users.noreply.github.com>
---------
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
Co-authored-by: VALERA771 <72030575+VALERA771@users.noreply.github.com>
---
.../EventArgs/Scp079/RecontainingEventArgs.cs | 42 ++++++++++++
EXILED/Exiled.Events/Handlers/Scp079.cs | 11 +++
.../Patches/Events/Scp079/Recontaining.cs | 67 +++++++++++++++++++
3 files changed, 120 insertions(+)
create mode 100644 EXILED/Exiled.Events/EventArgs/Scp079/RecontainingEventArgs.cs
create mode 100644 EXILED/Exiled.Events/Patches/Events/Scp079/Recontaining.cs
diff --git a/EXILED/Exiled.Events/EventArgs/Scp079/RecontainingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Scp079/RecontainingEventArgs.cs
new file mode 100644
index 000000000..11d88c3f1
--- /dev/null
+++ b/EXILED/Exiled.Events/EventArgs/Scp079/RecontainingEventArgs.cs
@@ -0,0 +1,42 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.EventArgs.Scp079
+{
+ using Exiled.API.Features;
+ using Exiled.Events.EventArgs.Interfaces;
+
+ ///
+ /// Contains information before SCP-079 gets recontained.
+ ///
+ public class RecontainingEventArgs : IDeniableEvent, IPlayerEvent
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The instance.
+ public RecontainingEventArgs(BreakableWindow recontainer)
+ {
+ Player = Player.Get(recontainer.LastAttacker.Hub);
+ IsAutomatic = recontainer.LastAttacker.IsSet;
+ }
+
+ ///
+ /// Gets the Player that started the recontainment process.
+ /// Can be null if is true.
+ ///
+ public Player Player { get; }
+
+ ///
+ public bool IsAllowed { get; set; } = true;
+
+ ///
+ /// Gets a value indicating whether or not the recontained has been made automatically or by triggering the proccess.
+ ///
+ public bool IsAutomatic { get; }
+ }
+}
diff --git a/EXILED/Exiled.Events/Handlers/Scp079.cs b/EXILED/Exiled.Events/Handlers/Scp079.cs
index 85da2da7e..b272defea 100644
--- a/EXILED/Exiled.Events/Handlers/Scp079.cs
+++ b/EXILED/Exiled.Events/Handlers/Scp079.cs
@@ -57,6 +57,11 @@ public static class Scp079
///
public static Event ChangingSpeakerStatus { get; set; } = new();
+ ///
+ /// Invoked before SCP-079 recontainment.
+ ///
+ public static Event Recontaining { get; set; } = new();
+
///
/// Invoked after SCP-079 recontainment.
///
@@ -125,6 +130,12 @@ public static class Scp079
/// The instance.
public static void OnChangingSpeakerStatus(ChangingSpeakerStatusEventArgs ev) => ChangingSpeakerStatus.InvokeSafely(ev);
+ ///
+ /// Called before SCP-079 is recontained.
+ ///
+ /// The instance.
+ public static void OnRecontaining(RecontainingEventArgs ev) => Recontaining.InvokeSafely(ev);
+
///
/// Called after SCP-079 is recontained.
///
diff --git a/EXILED/Exiled.Events/Patches/Events/Scp079/Recontaining.cs b/EXILED/Exiled.Events/Patches/Events/Scp079/Recontaining.cs
new file mode 100644
index 000000000..82e924fbc
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Events/Scp079/Recontaining.cs
@@ -0,0 +1,67 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Events.Scp079
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using Exiled.API.Features.Pools;
+ using Exiled.Events.Attributes;
+ using Exiled.Events.EventArgs.Scp079;
+ using Exiled.Events.Handlers;
+
+ using HarmonyLib;
+
+ using PlayerRoles.PlayableScps.Scp079;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches .
+ /// Adds the event.
+ ///
+ [EventPatch(typeof(Scp079), nameof(Scp079.Recontaining))]
+ [HarmonyPatch(typeof(Scp079Recontainer), nameof(Scp079Recontainer.Recontain))]
+ internal class Recontaining
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ int index = 0;
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ LocalBuilder ev = generator.DeclareLocal(typeof(RecontainingEventArgs));
+
+ Label returnLabel = generator.DefineLabel();
+
+ newInstructions.InsertRange(index, new CodeInstruction[]
+ {
+ // RecontainingEventArgs ev = new(this._activatorGlass)
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Ldfld, PropertyGetter(typeof(Scp079Recontainer), nameof(Scp079Recontainer._activatorGlass))),
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(RecontainingEventArgs))[0]),
+ new(OpCodes.Stloc_S, ev.LocalIndex),
+
+ // Scp079.OnRecontaining(ev)
+ new(OpCodes.Ldloc_S, ev.LocalIndex),
+ new(OpCodes.Call, Method(typeof(Scp079), nameof(Scp079.OnRecontaining))),
+
+ // if (!ev.IsAllowed) return;
+ new(OpCodes.Ldloc_S, ev.LocalIndex),
+ new(OpCodes.Callvirt, Method(typeof(RecontainingEventArgs), nameof(RecontainingEventArgs.IsAllowed))),
+ new(OpCodes.Brfalse_S, returnLabel),
+ });
+
+ newInstructions[newInstructions.Count - 1].WithLabels(returnLabel);
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
From bc0b1798cd4a764e625b6c77f8c1b90009a32852 Mon Sep 17 00:00:00 2001
From: ZeroTwo <63092138+NotZer0Two@users.noreply.github.com>
Date: Fri, 16 Aug 2024 01:28:35 +0200
Subject: [PATCH 29/39] `[EXILED::API]` Adding SpawnMice (#47)
* Mouse Spawner
* Changes to make it public
* Optimization
* Finally can i spawn rats?
* Fix adding throw and changing the error that throws
---------
Co-authored-by: VALERA771 <72030575+VALERA771@users.noreply.github.com>
---
EXILED/Exiled.API/Features/Map.cs | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/EXILED/Exiled.API/Features/Map.cs b/EXILED/Exiled.API/Features/Map.cs
index 3f1f6fc1a..2b3c4cdb0 100644
--- a/EXILED/Exiled.API/Features/Map.cs
+++ b/EXILED/Exiled.API/Features/Map.cs
@@ -54,6 +54,8 @@ public static class Map
private static AmbientSoundPlayer ambientSoundPlayer;
+ private static SqueakSpawner squeakSpawner;
+
///
/// Gets the tantrum prefab.
///
@@ -109,6 +111,11 @@ public static int Seed
///
public static AmbientSoundPlayer AmbientSoundPlayer => ambientSoundPlayer ??= ReferenceHub.HostHub.GetComponent();
+ ///
+ /// Gets the .
+ ///
+ public static SqueakSpawner SqueakSpawner => squeakSpawner ??= Object.FindObjectOfType();
+
///
/// Broadcasts a message to all players.
///
@@ -362,6 +369,19 @@ public static void PlayGunSound(Vector3 position, ItemType firearmType, byte max
msg.SendToAuthenticated();
}
+ ///
+ /// Spawns mice inside the .
+ ///
+ /// The type of mice you want to spawn..
+ public static void SpawnMice(byte mice = 1)
+ {
+ if (mice > SqueakSpawner.mice.Length)
+ throw new ArgumentOutOfRangeException($"Mouse type must be between 1 and {SqueakSpawner.mice.Length}.");
+
+ SqueakSpawner.NetworksyncSpawn = mice;
+ SqueakSpawner.SyncMouseSpawn(0, SqueakSpawner.NetworksyncSpawn);
+ }
+
///
/// Clears the lazy loading game object cache.
///
From 7a6df1691c72691efc2e7b4139a0708215c657d0 Mon Sep 17 00:00:00 2001
From: ZeroTwo <63092138+NotZer0Two@users.noreply.github.com>
Date: Fri, 16 Aug 2024 01:29:05 +0200
Subject: [PATCH 30/39] `[EXILED::API]` Adding ScaleNetworkIdentityObject (#49)
---
.../Exiled.API/Extensions/MirrorExtensions.cs | 38 +++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
index e85b54ed6..21f2d111e 100644
--- a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
+++ b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
@@ -355,6 +355,44 @@ public static void MessageTranslated(this Player player, string words, string tr
}
}
+ ///
+ /// Scales an object for the specified player.
+ ///
+ /// Target to send.
+ /// The to scale.
+ /// The scale the object needs to be set to.
+ public static void ScaleNetworkIdentityObject(this Player player, NetworkIdentity identity, Vector3 scale)
+ {
+ identity.gameObject.transform.localScale = scale;
+ ObjectDestroyMessage objectDestroyMessage = new()
+ {
+ netId = identity.netId,
+ };
+
+ player.Connection.Send(objectDestroyMessage, 0);
+ SendSpawnMessageMethodInfo?.Invoke(null, new object[] { identity, player.Connection });
+ }
+
+ ///
+ /// Scales an object for all players.
+ ///
+ /// The to scale.
+ /// The scale the object needs to be set to.
+ public static void ScaleNetworkIdentityObject(this NetworkIdentity identity, Vector3 scale)
+ {
+ identity.gameObject.transform.localScale = scale;
+ ObjectDestroyMessage objectDestroyMessage = new()
+ {
+ netId = identity.netId,
+ };
+
+ foreach (Player ply in Player.List)
+ {
+ ply.Connection.Send(objectDestroyMessage, 0);
+ SendSpawnMessageMethodInfo?.Invoke(null, new object[] { identity, ply.Connection });
+ }
+ }
+
///
/// Send fake values to client's .
///
From 7befb7a4c2a82c6336c1b564f1c346486932c440 Mon Sep 17 00:00:00 2001
From: IRacle
Date: Fri, 16 Aug 2024 16:42:17 +0300
Subject: [PATCH 31/39] =?UTF-8?q?Revert=20"=F0=9F=92=80=F0=9F=92=80?=
=?UTF-8?q?=F0=9F=92=80"?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This reverts commit 904865e04f86424b77b27023aa7d138ed5dfe220.
---
EXILED/Exiled.API/Features/Player.cs | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/EXILED/Exiled.API/Features/Player.cs b/EXILED/Exiled.API/Features/Player.cs
index 175d7731a..b7a4afee5 100644
--- a/EXILED/Exiled.API/Features/Player.cs
+++ b/EXILED/Exiled.API/Features/Player.cs
@@ -1277,8 +1277,13 @@ public static Player Get(GameObject gameObject)
if (Dictionary.TryGetValue(gameObject, out Player player))
return player;
- UnverifiedPlayers.TryGetValue(gameObject, out player);
- return player;
+ if (UnverifiedPlayers.TryGetValue(gameObject, out player))
+ return player;
+
+ if (ReferenceHub.TryGetHub(gameObject, out ReferenceHub hub))
+ return new(hub);
+
+ return null;
}
///
From 0c9cfff3bb7fc9fa45d850f87403429e76b4a6b8 Mon Sep 17 00:00:00 2001
From: ZeroTwo <63092138+NotZer0Two@users.noreply.github.com>
Date: Tue, 20 Aug 2024 21:02:26 +0200
Subject: [PATCH 32/39] `[EXILED::API]` Adding SendFakeSceneLoading (#45)
* Added ScenesType and Corrisponding Methods for the Server and Player
* Fixing Building Error
* Applied the Change request
* Changes requested by Yamato made
* Fixed Building Errors
* Fix build
---------
Co-authored-by: VALERA771 <72030575+VALERA771@users.noreply.github.com>
---
EXILED/Exiled.API/Enums/ScenesType.cs | 47 +++++++++++++++++++
.../Exiled.API/Extensions/MirrorExtensions.cs | 30 ++++++++++++
EXILED/Exiled.API/Features/Server.cs | 2 +
3 files changed, 79 insertions(+)
create mode 100644 EXILED/Exiled.API/Enums/ScenesType.cs
diff --git a/EXILED/Exiled.API/Enums/ScenesType.cs b/EXILED/Exiled.API/Enums/ScenesType.cs
new file mode 100644
index 000000000..2a7898ca6
--- /dev/null
+++ b/EXILED/Exiled.API/Enums/ScenesType.cs
@@ -0,0 +1,47 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.API.Enums
+{
+ ///
+ /// Unique identifier for the different types of Scenes the client and server can load.
+ ///
+ public enum ScenesType
+ {
+ ///
+ /// The facility itself.
+ ///
+ Facility,
+
+ ///
+ /// The current main menu.
+ /// ! Will cause crash when trying joining servers !
+ ///
+ NewMainMenu,
+
+ ///
+ /// The old main menu.
+ ///
+ MainMenuRemastered,
+
+ ///
+ /// The old server list.
+ ///
+ FastMenu,
+
+ ///
+ /// The loading Screen.
+ /// ! Will cause crash when trying joining servers !
+ ///
+ PreLoader,
+
+ ///
+ /// A black menu before loading the .
+ ///
+ Loader,
+ }
+}
diff --git a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
index 21f2d111e..8b4350b5b 100644
--- a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
+++ b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
@@ -15,6 +15,7 @@ namespace Exiled.API.Extensions
using System.Reflection.Emit;
using System.Text;
+ using Exiled.API.Enums;
using Features;
using Features.Pools;
@@ -355,6 +356,35 @@ public static void MessageTranslated(this Player player, string words, string tr
}
}
+ ///
+ /// Sends to the player a Fake Change Scene.
+ ///
+ /// The player to send the Scene.
+ /// The new Scene the client will load.
+ public static void SendFakeSceneLoading(this Player player, ScenesType newSceneName)
+ {
+ SceneMessage message = new()
+ {
+ sceneName = newSceneName.ToString(),
+ };
+
+ player.Connection.Send(message);
+ }
+
+ ///
+ /// Emulation of the method SCP:SL uses to change scene.
+ ///
+ /// The new Scene the client will load.
+ public static void ChangeSceneToAllClients(ScenesType scene)
+ {
+ SceneMessage message = new()
+ {
+ sceneName = scene.ToString(),
+ };
+
+ NetworkServer.SendToAll(message);
+ }
+
///
/// Scales an object for the specified player.
///
diff --git a/EXILED/Exiled.API/Features/Server.cs b/EXILED/Exiled.API/Features/Server.cs
index b1b05e8c1..5dea5ee84 100644
--- a/EXILED/Exiled.API/Features/Server.cs
+++ b/EXILED/Exiled.API/Features/Server.cs
@@ -11,6 +11,8 @@ namespace Exiled.API.Features
using System.Collections.Generic;
using System.Reflection;
+ using Exiled.API.Enums;
+
using GameCore;
using Interfaces;
From 5fc1b143806940d08b6dccf02187cac57464a926 Mon Sep 17 00:00:00 2001
From: IRacle <79921583+IRacle1@users.noreply.github.com>
Date: Sat, 24 Aug 2024 13:16:45 +0300
Subject: [PATCH 33/39] scp018projectile (#73)
---
EXILED/Exiled.API/Features/Pickups/Pickup.cs | 1 +
.../Pickups/Projectiles/Scp018Projectile.cs | 20 +++++++++++++++++--
2 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/EXILED/Exiled.API/Features/Pickups/Pickup.cs b/EXILED/Exiled.API/Features/Pickups/Pickup.cs
index 6c600dd4c..010b1e52c 100644
--- a/EXILED/Exiled.API/Features/Pickups/Pickup.cs
+++ b/EXILED/Exiled.API/Features/Pickups/Pickup.cs
@@ -133,6 +133,7 @@ internal Pickup(ItemType type)
public PickupStandardPhysics PhysicsModule
{
get => Base.PhysicsModule as PickupStandardPhysics;
+ [Obsolete("Unsafe.")]
set
{
Base.PhysicsModule.DestroyModule();
diff --git a/EXILED/Exiled.API/Features/Pickups/Projectiles/Scp018Projectile.cs b/EXILED/Exiled.API/Features/Pickups/Projectiles/Scp018Projectile.cs
index c5549c87f..798cd04fc 100644
--- a/EXILED/Exiled.API/Features/Pickups/Projectiles/Scp018Projectile.cs
+++ b/EXILED/Exiled.API/Features/Pickups/Projectiles/Scp018Projectile.cs
@@ -7,7 +7,11 @@
namespace Exiled.API.Features.Pickups.Projectiles
{
+ using System;
+ using System.Reflection;
+
using Exiled.API.Interfaces;
+ using HarmonyLib;
using InventorySystem.Items.ThrowableProjectiles;
@@ -18,6 +22,9 @@ namespace Exiled.API.Features.Pickups.Projectiles
///
public class Scp018Projectile : TimeGrenadeProjectile, IWrapper
{
+ private static FieldInfo maxVelocityField;
+ private static FieldInfo velocityPerBounceField;
+
///
/// Initializes a new instance of the class.
///
@@ -48,6 +55,7 @@ internal Scp018Projectile()
public new Scp018Physics PhysicsModule
{
get => Base.PhysicsModule as Scp018Physics;
+ [Obsolete("Unsafe.", true)]
set
{
Base.PhysicsModule.DestroyModule();
@@ -61,7 +69,11 @@ internal Scp018Projectile()
public float MaxVelocity
{
get => PhysicsModule._maxVel;
- set => PhysicsModule = new Scp018Physics(Base, PhysicsModule._trail, PhysicsModule._radius, value, PhysicsModule._velPerBounce);
+ set
+ {
+ maxVelocityField ??= AccessTools.Field(typeof(Scp018Physics), nameof(Scp018Physics._maxVel));
+ maxVelocityField.SetValue(PhysicsModule, value);
+ }
}
///
@@ -70,7 +82,11 @@ public float MaxVelocity
public float VelocityPerBounce
{
get => PhysicsModule._maxVel;
- set => PhysicsModule = new Scp018Physics(Base, PhysicsModule._trail, PhysicsModule._radius, MaxVelocity, value);
+ set
+ {
+ velocityPerBounceField ??= AccessTools.Field(typeof(Scp018Physics), nameof(Scp018Physics._velPerBounce));
+ velocityPerBounceField.SetValue(PhysicsModule, value);
+ }
}
///
From 327801e77e8ab5c8a59122db71e56853eda0995e Mon Sep 17 00:00:00 2001
From: IRacle <79921583+IRacle1@users.noreply.github.com>
Date: Tue, 27 Aug 2024 17:21:36 +0300
Subject: [PATCH 34/39] =?UTF-8?q?=F0=9F=98=B1=F0=9F=98=8E=F0=9F=99=82?=
=?UTF-8?q?=F0=9F=92=80=F0=9F=98=88=20(#51)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Разрабы дауны😱😎🙂💀😈
* new better `SendFakeSyncVar` method
* ililililililil
* c
---
.../Exiled.API/Extensions/MirrorExtensions.cs | 33 +++++++++++++++
.../Exiled.Events/Patches/Generic/DoorList.cs | 42 +++++++++++++++++--
2 files changed, 71 insertions(+), 4 deletions(-)
diff --git a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
index 8b4350b5b..22d799677 100644
--- a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
+++ b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
@@ -431,6 +431,7 @@ public static void ScaleNetworkIdentityObject(this NetworkIdentity identity, Vec
/// 's type.
/// Property name starting with Network.
/// Value of send to target.
+ [Obsolete("Use overload with type-template instead.")]
public static void SendFakeSyncVar(this Player target, NetworkIdentity behaviorOwner, Type targetType, string propertyName, object value)
{
if (!target.IsConnected)
@@ -454,6 +455,38 @@ void CustomSyncVarGenerator(NetworkWriter targetWriter)
}
}
+ ///
+ /// Send fake values to client's .
+ ///
+ /// Target SyncVar property type.
+ /// Target to send.
+ /// of object that owns .
+ /// 's type.
+ /// Property name starting with Network.
+ /// Value of send to target.
+ public static void SendFakeSyncVar(this Player target, NetworkIdentity behaviorOwner, Type targetType, string propertyName, T value)
+ {
+ if (!target.IsConnected)
+ return;
+
+ NetworkWriterPooled writer = NetworkWriterPool.Get();
+ NetworkWriterPooled writer2 = NetworkWriterPool.Get();
+ MakeCustomSyncWriter(behaviorOwner, targetType, null, CustomSyncVarGenerator, writer, writer2);
+ target.Connection.Send(new EntityStateMessage
+ {
+ netId = behaviorOwner.netId,
+ payload = writer.ToArraySegment(),
+ });
+
+ NetworkWriterPool.Return(writer);
+ NetworkWriterPool.Return(writer2);
+ void CustomSyncVarGenerator(NetworkWriter targetWriter)
+ {
+ targetWriter.WriteULong(SyncVarDirtyBits[$"{targetType.Name}.{propertyName}"]);
+ WriterExtensions[typeof(T)]?.Invoke(null, new object[2] { targetWriter, value });
+ }
+ }
+
///
/// Force resync to client's .
///
diff --git a/EXILED/Exiled.Events/Patches/Generic/DoorList.cs b/EXILED/Exiled.Events/Patches/Generic/DoorList.cs
index 4f1ce6e33..b8b653455 100644
--- a/EXILED/Exiled.Events/Patches/Generic/DoorList.cs
+++ b/EXILED/Exiled.Events/Patches/Generic/DoorList.cs
@@ -30,14 +30,48 @@ namespace Exiled.Events.Patches.Generic
[HarmonyPatch(typeof(DoorVariant), nameof(DoorVariant.RegisterRooms))]
internal class DoorList
{
- private static void Postfix(DoorVariant __instance)
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
{
- if (Door.DoorVariantToDoor.ContainsKey(__instance))
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ Label ret = generator.DefineLabel();
+
+ // if (Rooms == null)
+ // return;
+ newInstructions.InsertRange(
+ 0,
+ new CodeInstruction[]
+ {
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Callvirt, PropertyGetter(typeof(DoorVariant), nameof(DoorVariant.Rooms))),
+ new(OpCodes.Brfalse_S, ret),
+ });
+
+ // DoorList.InitDoor(this);
+ newInstructions.InsertRange(
+ newInstructions.Count - 1,
+ new CodeInstruction[]
+ {
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Call, Method(typeof(DoorList), nameof(DoorList.InitDoor))),
+ });
+
+ newInstructions[newInstructions.Count - 1].labels.Add(ret);
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+
+ private static void InitDoor(DoorVariant doorVariant)
+ {
+ if (Door.DoorVariantToDoor.ContainsKey(doorVariant))
return;
- List rooms = __instance.Rooms.Select(identifier => Room.RoomIdentifierToRoom[identifier]).ToList();
+ List rooms = doorVariant.Rooms.Select(identifier => Room.RoomIdentifierToRoom[identifier]).ToList();
- Door door = Door.Create(__instance, rooms);
+ Door door = Door.Create(doorVariant, rooms);
foreach (Room room in rooms)
{
From 880fde01370a4c77fdd19cf94560d8341ddf5ea2 Mon Sep 17 00:00:00 2001
From: IRacle <79921583+IRacle1@users.noreply.github.com>
Date: Tue, 27 Aug 2024 17:25:40 +0300
Subject: [PATCH 35/39] `KeycardPickup.Permissions` now is not joke (#70)
---
.../Item/KeycardInteractingEventArgs.cs | 7 ++-
.../Patches/Events/Item/KeycardInteracting.cs | 62 +++++++++++++------
2 files changed, 48 insertions(+), 21 deletions(-)
diff --git a/EXILED/Exiled.Events/EventArgs/Item/KeycardInteractingEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Item/KeycardInteractingEventArgs.cs
index 65f7a95c7..181f7c7eb 100644
--- a/EXILED/Exiled.Events/EventArgs/Item/KeycardInteractingEventArgs.cs
+++ b/EXILED/Exiled.Events/EventArgs/Item/KeycardInteractingEventArgs.cs
@@ -29,7 +29,7 @@ public class KeycardInteractingEventArgs : IPlayerEvent, IDeniableEvent, IDoorEv
///
public KeycardInteractingEventArgs(BaseKeycardPickup pickup, Player player, DoorVariant door, bool isAllowed = true)
{
- Pickup = Pickup.Get(pickup);
+ KeycardPickup = Pickup.Get(pickup);
Player = player;
Door = Door.Get(door);
IsAllowed = isAllowed;
@@ -38,7 +38,10 @@ public KeycardInteractingEventArgs(BaseKeycardPickup pickup, Player player, Door
///
/// Gets the item that's interacting with the door.
///
- public Pickup Pickup { get; }
+ public Pickup Pickup => KeycardPickup;
+
+ ///
+ public KeycardPickup KeycardPickup { get; }
///
/// Gets the player who's threw the keycard.
diff --git a/EXILED/Exiled.Events/Patches/Events/Item/KeycardInteracting.cs b/EXILED/Exiled.Events/Patches/Events/Item/KeycardInteracting.cs
index 288e5d212..e26ef525d 100644
--- a/EXILED/Exiled.Events/Patches/Events/Item/KeycardInteracting.cs
+++ b/EXILED/Exiled.Events/Patches/Events/Item/KeycardInteracting.cs
@@ -11,10 +11,10 @@ namespace Exiled.Events.Patches.Events.Item
using System.Reflection.Emit;
using API.Features;
+ using API.Features.Pickups;
using API.Features.Pools;
using Exiled.Events;
- using Exiled.Events.Attributes;
using Exiled.Events.EventArgs.Item;
using Footprinting;
@@ -23,18 +23,19 @@ namespace Exiled.Events.Patches.Events.Item
using Interactables.Interobjects.DoorUtils;
- using InventorySystem.Items.Keycards;
+ using InventorySystem.Items;
using UnityEngine;
using static HarmonyLib.AccessTools;
+ using BaseKeycardPickup = InventorySystem.Items.Keycards.KeycardPickup;
+
///
- /// Patches .
+ /// Patches and adds implementation.
/// Adds the event.
///
- [EventPatch(typeof(Handlers.Item), nameof(Handlers.Item.KeycardInteracting))]
- [HarmonyPatch(typeof(KeycardPickup), nameof(KeycardPickup.ProcessCollision))]
+ [HarmonyPatch(typeof(BaseKeycardPickup), nameof(BaseKeycardPickup.ProcessCollision))]
internal static class KeycardInteracting
{
private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
@@ -46,7 +47,6 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable i.Calls(Method(typeof(DoorPermissions), nameof(DoorPermissions.CheckPermissions)))) + offset;
+ newInstructions.RemoveRange(index, 14);
newInstructions.InsertRange(
index,
new[]
{
- new(OpCodes.Stloc_S, havePermissions.LocalIndex),
- new(OpCodes.Br_S, skip2),
-
- // save original return
- new CodeInstruction(OpCodes.Ret).MoveLabelsFrom(newInstructions[index + 1]),
- new CodeInstruction(OpCodes.Nop).WithLabels(skip2),
+ // override permissions check, to implement KeycardPickup::Permissions
+ new(OpCodes.Ldarg_0),
+ new(OpCodes.Ldloc_1),
+ new CodeInstruction(OpCodes.Call, Method(typeof(KeycardInteracting), nameof(KeycardInteracting.CheckPermissions))),
+ new CodeInstruction(OpCodes.Stloc_S, havePermissions.LocalIndex),
});
- newInstructions.RemoveRange(index + 4, 2);
+ // 4 new instructions
+ offset = 4;
+ index += offset;
+
+ newInstructions.RemoveRange(index, 2);
offset = -5;
index = newInstructions.FindIndex(i => i.Calls(PropertySetter(typeof(DoorVariant), nameof(DoorVariant.NetworkTargetState)))) + offset;
@@ -111,7 +117,7 @@ private static IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable Transpiler(IEnumerable.Pool.Return(newInstructions);
}
+
+ private static bool CheckPermissions(BaseKeycardPickup keycard, DoorPermissions permissions)
+ {
+ if (permissions.RequiredPermissions == KeycardPermissions.None)
+ {
+ return true;
+ }
+
+ if (Pickup.Get(keycard) is KeycardPickup keycardPickup)
+ {
+ if (!permissions.RequireAll)
+ {
+ return ((KeycardPermissions)keycardPickup.Permissions & permissions.RequiredPermissions) != 0;
+ }
+
+ return ((KeycardPermissions)keycardPickup.Permissions & permissions.RequiredPermissions) == permissions.RequiredPermissions;
+ }
+
+ return InventorySystem.InventoryItemLoader.AvailableItems.TryGetValue(keycard.Info.ItemId, out ItemBase itemBase) && permissions.CheckPermissions(itemBase, null);
+ }
}
}
\ No newline at end of file
From acd1b2984db65090576c11e2a196da7432bcc223 Mon Sep 17 00:00:00 2001
From: VALERA771 <72030575+VALERA771@users.noreply.github.com>
Date: Tue, 27 Aug 2024 17:26:34 +0300
Subject: [PATCH 36/39] `[Exiled::Events]` `Unbanning` and `Unbanned` events
(#68)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* 🐱👤🐱👤🐱👤
* some fixes
* fix
* ☠️💀💀💀☠️💀
Co-authored-by: IRacle <79921583+IRacle1@users.noreply.github.com>
---------
Co-authored-by: IRacle <79921583+IRacle1@users.noreply.github.com>
---
.../EventArgs/Server/UnbannedEventArgs.cs | 36 ++++++++
.../EventArgs/Server/UnbanningEventArgs.cs | 43 +++++++++
EXILED/Exiled.Events/Handlers/Server.cs | 22 +++++
.../Patches/Events/Server/Unban.cs | 92 +++++++++++++++++++
4 files changed, 193 insertions(+)
create mode 100644 EXILED/Exiled.Events/EventArgs/Server/UnbannedEventArgs.cs
create mode 100644 EXILED/Exiled.Events/EventArgs/Server/UnbanningEventArgs.cs
create mode 100644 EXILED/Exiled.Events/Patches/Events/Server/Unban.cs
diff --git a/EXILED/Exiled.Events/EventArgs/Server/UnbannedEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Server/UnbannedEventArgs.cs
new file mode 100644
index 000000000..f1181a3b9
--- /dev/null
+++ b/EXILED/Exiled.Events/EventArgs/Server/UnbannedEventArgs.cs
@@ -0,0 +1,36 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.EventArgs.Server
+{
+ ///
+ /// Contains all information after a player gets unbanned.
+ ///
+ public class UnbannedEventArgs
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ public UnbannedEventArgs(string details, BanHandler.BanType banType)
+ {
+ BanDetails = BanHandler.ProcessBanItem(details, banType);
+ BanType = banType;
+ }
+
+ ///
+ /// Gets the ban details.
+ ///
+ public BanDetails BanDetails { get; }
+
+ ///
+ /// Gets the ban type.
+ ///
+ public BanHandler.BanType BanType { get; }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/EventArgs/Server/UnbanningEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Server/UnbanningEventArgs.cs
new file mode 100644
index 000000000..aa23690e6
--- /dev/null
+++ b/EXILED/Exiled.Events/EventArgs/Server/UnbanningEventArgs.cs
@@ -0,0 +1,43 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.EventArgs.Server
+{
+ using Exiled.Events.EventArgs.Interfaces;
+
+ ///
+ /// Contains all information before player is unbanned.
+ ///
+ public class UnbanningEventArgs : IDeniableEvent
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ ///
+ public UnbanningEventArgs(string banDetails, BanHandler.BanType banType, bool isAllowed = true)
+ {
+ BanDetails = BanHandler.ProcessBanItem(banDetails, banType);
+ BanType = banType;
+ IsAllowed = isAllowed;
+ }
+
+ ///
+ /// Gets or sets the ban details.
+ ///
+ public BanDetails BanDetails { get; set; }
+
+ ///
+ /// Gets the ban type.
+ ///
+ public BanHandler.BanType BanType { get; }
+
+ ///
+ public bool IsAllowed { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Handlers/Server.cs b/EXILED/Exiled.Events/Handlers/Server.cs
index 75be4d81c..3f5b71e63 100644
--- a/EXILED/Exiled.Events/Handlers/Server.cs
+++ b/EXILED/Exiled.Events/Handlers/Server.cs
@@ -111,6 +111,16 @@ public static class Server
///
public static Event ReloadedPermissions { get; set; } = new();
+ ///
+ /// Invoked before player is being unbanned.
+ ///
+ public static Event Unbanning { get; set; } = new();
+
+ ///
+ /// Invoked after player is being unbanned.
+ ///
+ public static Event Unbanned { get; set; } = new();
+
///
/// Called before waiting for players.
///
@@ -210,5 +220,17 @@ public static class Server
///
/// The instance.
public static void OnSelectingRespawnTeam(SelectingRespawnTeamEventArgs ev) => SelectingRespawnTeam.InvokeSafely(ev);
+
+ ///
+ /// Called before player is being unbanned.
+ ///
+ /// The instance.
+ public static void OnUnbanning(UnbanningEventArgs ev) => Unbanning.InvokeSafely(ev);
+
+ ///
+ /// Called after player is being unbanned.
+ ///
+ /// The instance.
+ public static void OnUnbanned(UnbannedEventArgs ev) => Unbanned.InvokeSafely(ev);
}
}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Patches/Events/Server/Unban.cs b/EXILED/Exiled.Events/Patches/Events/Server/Unban.cs
new file mode 100644
index 000000000..5eb89d224
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Events/Server/Unban.cs
@@ -0,0 +1,92 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Events.Server
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using Exiled.API.Features.Pools;
+ using Exiled.Events.Attributes;
+ using Exiled.Events.EventArgs.Server;
+ using HarmonyLib;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patches
+ /// to add and events.
+ ///
+ [HarmonyPatch(typeof(BanHandler), nameof(BanHandler.RemoveBan))]
+ [EventPatch(typeof(Handlers.Server), nameof(Handlers.Server.Unbanning))]
+ [EventPatch(typeof(Handlers.Server), nameof(Handlers.Server.Unbanned))]
+ internal class Unban
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ LocalBuilder ev = generator.DeclareLocal(typeof(UnbanningEventArgs));
+
+ Label continueLabel = generator.DefineLabel();
+
+ newInstructions.InsertRange(0, new CodeInstruction[]
+ {
+ // id
+ new(OpCodes.Ldarg_0),
+
+ // type
+ new(OpCodes.Ldarg_1),
+
+ // true
+ new(OpCodes.Ldc_I4_1),
+
+ // UnbanningEventArgs ev = new(string, BanHandler.BanType, true);
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(UnbanningEventArgs))[0]),
+ new(OpCodes.Dup),
+ new(OpCodes.Dup),
+ new(OpCodes.Stloc_S, ev.LocalIndex),
+
+ // Handlers.Server.OnUnbanning(ev);
+ new(OpCodes.Call, Method(typeof(Handlers.Server), nameof(Handlers.Server.OnUnbanning))),
+
+ // if (!ev.IsAllowed)
+ // return;
+ new(OpCodes.Callvirt, PropertyGetter(typeof(UnbanningEventArgs), nameof(UnbanningEventArgs.IsAllowed))),
+ new(OpCodes.Brtrue_S, continueLabel),
+
+ new(OpCodes.Ret),
+
+ // id = ev.BanDetails.ToString();
+ new CodeInstruction(OpCodes.Ldloc_S, ev.LocalIndex).WithLabels(continueLabel),
+ new(OpCodes.Callvirt, PropertyGetter(typeof(UnbanningEventArgs), nameof(UnbanningEventArgs.BanDetails))),
+ new(OpCodes.Call, Method(typeof(BanDetails), nameof(BanDetails.ToString))),
+ new(OpCodes.Starg_S, 1),
+ });
+
+ newInstructions.InsertRange(newInstructions.Count - 1, new CodeInstruction[]
+ {
+ // id
+ new(OpCodes.Ldarg_0),
+
+ // type
+ new(OpCodes.Ldarg_1),
+
+ // UnbannedEventArgs ev2 = new(string, BanHandler.BanType);
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(UnbannedEventArgs))[0]),
+
+ // Handlers.Server.OnUnbanned(ev2);
+ new(OpCodes.Call, Method(typeof(Handlers.Server), nameof(Handlers.Server.OnUnbanned))),
+ });
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
\ No newline at end of file
From 5aa87548bb89c3b7a34f0a047947d64fba579154 Mon Sep 17 00:00:00 2001
From: ZeroTwo <63092138+NotZer0Two@users.noreply.github.com>
Date: Tue, 27 Aug 2024 16:39:24 +0200
Subject: [PATCH 37/39] `[EXILED::API]` Adding StaminaRegenMultiplier
(BodyArmor) (#71)
* First push
* using removed
* Added to the pickup
* Forgot to add to Armor (not pickup)
* Fix Build error
* Simplification
---
EXILED/Exiled.API/Features/Items/Armor.cs | 8 ++++-
.../Features/Pickups/BodyArmorPickup.cs | 7 +++++
.../Patches/Generic/StaminaRegenArmor.cs | 30 +++++++++++++++++++
3 files changed, 44 insertions(+), 1 deletion(-)
create mode 100644 EXILED/Exiled.Events/Patches/Generic/StaminaRegenArmor.cs
diff --git a/EXILED/Exiled.API/Features/Items/Armor.cs b/EXILED/Exiled.API/Features/Items/Armor.cs
index 8f176e676..1a46bc360 100644
--- a/EXILED/Exiled.API/Features/Items/Armor.cs
+++ b/EXILED/Exiled.API/Features/Items/Armor.cs
@@ -15,7 +15,6 @@ namespace Exiled.API.Features.Items
using Exiled.API.Interfaces;
using InventorySystem.Items.Armor;
-
using PlayerRoles;
using Structs;
@@ -110,6 +109,11 @@ public float StaminaUseMultiplier
set => Base._staminaUseMultiplier = value;
}
+ ///
+ /// Gets or sets the stamina regen multiplier.
+ ///
+ public float StaminaRegenMultiplier { get; set; } = 1f;
+
///
/// Gets or sets how much the users movement speed should be affected when wearing this armor. (higher values = slower movement).
///
@@ -153,6 +157,7 @@ public IEnumerable CategoryLimits
StaminaUseMultiplier = StaminaUseMultiplier,
RemoveExcessOnDrop = RemoveExcessOnDrop,
CategoryLimits = CategoryLimits,
+ StaminaRegenMultiplier = StaminaRegenMultiplier,
AmmoLimits = AmmoLimits,
VestEfficacy = VestEfficacy,
HelmetEfficacy = HelmetEfficacy,
@@ -168,6 +173,7 @@ internal override void ReadPickupInfo(Pickup pickup)
VestEfficacy = armorPickup.VestEfficacy;
RemoveExcessOnDrop = armorPickup.RemoveExcessOnDrop;
StaminaUseMultiplier = armorPickup.StaminaUseMultiplier;
+ StaminaRegenMultiplier = armorPickup.StaminaRegenMultiplier;
AmmoLimits = armorPickup.AmmoLimits;
CategoryLimits = armorPickup.CategoryLimits;
}
diff --git a/EXILED/Exiled.API/Features/Pickups/BodyArmorPickup.cs b/EXILED/Exiled.API/Features/Pickups/BodyArmorPickup.cs
index 1d98dd7ec..e387cf182 100644
--- a/EXILED/Exiled.API/Features/Pickups/BodyArmorPickup.cs
+++ b/EXILED/Exiled.API/Features/Pickups/BodyArmorPickup.cs
@@ -98,6 +98,11 @@ public int VestEfficacy
///
public float StaminaUseMultiplier { get; set; }
+ ///
+ /// Gets or sets the stamina regen multiplier.
+ ///
+ public float StaminaRegenMultiplier { get; set; }
+
///
/// Gets how much the users movement speed should be affected when wearing this armor. (higher values = slower movement).
///
@@ -129,6 +134,7 @@ internal override void ReadItemInfo(Item item)
vestEfficacy = armoritem.VestEfficacy;
RemoveExcessOnDrop = armoritem.RemoveExcessOnDrop;
StaminaUseMultiplier = armoritem.StaminaUseMultiplier;
+ StaminaRegenMultiplier = armoritem.StaminaRegenMultiplier;
AmmoLimits = armoritem.AmmoLimits;
CategoryLimits = armoritem.CategoryLimits;
}
@@ -144,6 +150,7 @@ protected override void InitializeProperties(ItemBase itemBase)
vestEfficacy = armoritem.VestEfficacy;
RemoveExcessOnDrop = !armoritem.DontRemoveExcessOnDrop;
StaminaUseMultiplier = armoritem._staminaUseMultiplier;
+ StaminaRegenMultiplier = armoritem.StaminaRegenMultiplier;
AmmoLimits = armoritem.AmmoLimits.Select(limit => (ArmorAmmoLimit)limit);
CategoryLimits = armoritem.CategoryLimits;
}
diff --git a/EXILED/Exiled.Events/Patches/Generic/StaminaRegenArmor.cs b/EXILED/Exiled.Events/Patches/Generic/StaminaRegenArmor.cs
new file mode 100644
index 000000000..e4b83f10e
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Generic/StaminaRegenArmor.cs
@@ -0,0 +1,30 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Generic
+{
+ using Exiled.API.Features;
+ using Exiled.API.Features.Items;
+#pragma warning disable SA1313
+
+ using HarmonyLib;
+ using InventorySystem.Items.Armor;
+
+ ///
+ /// Patches .
+ /// Implements .
+ ///
+ [HarmonyPatch(typeof(BodyArmor), nameof(BodyArmor.StaminaRegenMultiplier), MethodType.Getter)]
+ internal class StaminaRegenArmor
+ {
+ private static void Postfix(BodyArmor __instance, ref float __result)
+ {
+ if(Item.Get(__instance) is Armor armor)
+ __result *= armor.StaminaRegenMultiplier;
+ }
+ }
+}
\ No newline at end of file
From af3da2d92e71d38321f5c29b0df49e63c130431f Mon Sep 17 00:00:00 2001
From: ZeroTwo <63092138+NotZer0Two@users.noreply.github.com>
Date: Tue, 27 Aug 2024 20:59:04 +0200
Subject: [PATCH 38/39] `[EXILED::API]` Adding MoveNetworkIdentityObject (#48)
* MoveNetworkObject Added
* fix builds
* Fix Builds
---------
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
---
.../Exiled.API/Extensions/MirrorExtensions.cs | 38 +++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
index 22d799677..114435a40 100644
--- a/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
+++ b/EXILED/Exiled.API/Extensions/MirrorExtensions.cs
@@ -356,6 +356,24 @@ public static void MessageTranslated(this Player player, string words, string tr
}
}
+ ///
+ /// Moves object for the player.
+ ///
+ /// Target to send.
+ /// The to move.
+ /// The position to change.
+ public static void MoveNetworkIdentityObject(this Player player, NetworkIdentity identity, Vector3 pos)
+ {
+ identity.gameObject.transform.position = pos;
+ ObjectDestroyMessage objectDestroyMessage = new()
+ {
+ netId = identity.netId,
+ };
+
+ player.Connection.Send(objectDestroyMessage, 0);
+ SendSpawnMessageMethodInfo?.Invoke(null, new object[] { identity, player.Connection });
+ }
+
///
/// Sends to the player a Fake Change Scene.
///
@@ -403,6 +421,26 @@ public static void ScaleNetworkIdentityObject(this Player player, NetworkIdentit
SendSpawnMessageMethodInfo?.Invoke(null, new object[] { identity, player.Connection });
}
+ ///
+ /// Moves object for all the players.
+ ///
+ /// The to move.
+ /// The position to change.
+ public static void MoveNetworkIdentityObject(this NetworkIdentity identity, Vector3 pos)
+ {
+ identity.gameObject.transform.position = pos;
+ ObjectDestroyMessage objectDestroyMessage = new()
+ {
+ netId = identity.netId,
+ };
+
+ foreach (Player ply in Player.List)
+ {
+ ply.Connection.Send(objectDestroyMessage, 0);
+ SendSpawnMessageMethodInfo?.Invoke(null, new object[] { identity, ply.Connection });
+ }
+ }
+
///
/// Scales an object for all players.
///
From 532fdfb1eb8046fb1c0c7ea48a9f56b8c488f509 Mon Sep 17 00:00:00 2001
From: ZeroTwo <63092138+NotZer0Two@users.noreply.github.com>
Date: Tue, 27 Aug 2024 20:59:57 +0200
Subject: [PATCH 39/39] `[EXILED::EVENTS]` Adding PlayingAudioLog 69 PR (#69)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* Audio log event
* Mistakes
* Fix build errors
* Update EXILED/Exiled.Events/Patches/Events/Player/PlayingAudioLog.cs
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Update EXILED/Exiled.Events/EventArgs/Player/PlayingAudioLogEventArgs.cs
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Update EXILED/Exiled.Events/EventArgs/Player/PlayingAudioLogEventArgs.cs
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Update EXILED/Exiled.Events/EventArgs/Player/PlayingAudioLogEventArgs.cs
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* Update EXILED/Exiled.Events/EventArgs/Player/PlayingAudioLogEventArgs.cs
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
* 🧠💭😔😞📉😭🤔➡️🧑⚕️💬💊📈😌
---------
Co-authored-by: Yamato <66829532+louis1706@users.noreply.github.com>
---
.../Player/PlayingAudioLogEventArgs.cs | 44 ++++++++++++
EXILED/Exiled.Events/Handlers/Player.cs | 11 +++
.../Patches/Events/Player/PlayingAudioLog.cs | 68 +++++++++++++++++++
3 files changed, 123 insertions(+)
create mode 100644 EXILED/Exiled.Events/EventArgs/Player/PlayingAudioLogEventArgs.cs
create mode 100644 EXILED/Exiled.Events/Patches/Events/Player/PlayingAudioLog.cs
diff --git a/EXILED/Exiled.Events/EventArgs/Player/PlayingAudioLogEventArgs.cs b/EXILED/Exiled.Events/EventArgs/Player/PlayingAudioLogEventArgs.cs
new file mode 100644
index 000000000..cad328ea2
--- /dev/null
+++ b/EXILED/Exiled.Events/EventArgs/Player/PlayingAudioLogEventArgs.cs
@@ -0,0 +1,44 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.EventArgs.Player
+{
+ using API.Features;
+
+ using Interfaces;
+
+ ///
+ /// Contains all information before a player plays the AudioLog.
+ ///
+ public class PlayingAudioLogEventArgs : IPlayerEvent, IDeniableEvent
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public PlayingAudioLogEventArgs(Player player, bool isAllowed = true)
+ {
+ Player = player;
+ IsAllowed = isAllowed;
+ }
+
+ ///
+ /// Gets or sets a value indicating whether or not the audio will start.
+ ///
+ public bool IsAllowed { get; set; }
+
+ ///
+ /// Gets the player who started the AudioLog.
+ ///
+ public Player Player { get; }
+ }
+}
\ No newline at end of file
diff --git a/EXILED/Exiled.Events/Handlers/Player.cs b/EXILED/Exiled.Events/Handlers/Player.cs
index 016bdf644..5cf86d879 100644
--- a/EXILED/Exiled.Events/Handlers/Player.cs
+++ b/EXILED/Exiled.Events/Handlers/Player.cs
@@ -208,6 +208,11 @@ public class Player
///
public static Event DroppingNothing { get; set; } = new();
+ ///
+ /// Invoked before playing an AudioLog.
+ ///
+ public static Event PlayingAudioLog { get; set; } = new();
+
///
/// Invoked before picking up an .
///
@@ -691,6 +696,12 @@ public class Player
/// The instance.
public static void OnDroppingNothing(DroppingNothingEventArgs ev) => DroppingNothing.InvokeSafely(ev);
+ ///
+ /// Called before a plays an AudioLog.
+ ///
+ /// The instance.
+ public static void OnPlayingAudioLog(PlayingAudioLogEventArgs ev) => PlayingAudioLog.InvokeSafely(ev);
+
///
/// Called before a picks up an item.
///
diff --git a/EXILED/Exiled.Events/Patches/Events/Player/PlayingAudioLog.cs b/EXILED/Exiled.Events/Patches/Events/Player/PlayingAudioLog.cs
new file mode 100644
index 000000000..c2b0125e1
--- /dev/null
+++ b/EXILED/Exiled.Events/Patches/Events/Player/PlayingAudioLog.cs
@@ -0,0 +1,68 @@
+// -----------------------------------------------------------------------
+//
+// Copyright (c) Exiled Team. All rights reserved.
+// Licensed under the CC BY-SA 3.0 license.
+//
+// -----------------------------------------------------------------------
+
+namespace Exiled.Events.Patches.Events.Player
+{
+ using System.Collections.Generic;
+ using System.Reflection.Emit;
+
+ using API.Features;
+ using API.Features.Pools;
+ using Exiled.Events.Attributes;
+ using Exiled.Events.EventArgs.Player;
+ using HarmonyLib;
+ using MapGeneration.Spawnables;
+
+ using static HarmonyLib.AccessTools;
+
+ ///
+ /// Patch the .
+ /// Adds the event.
+ ///
+ [EventPatch(typeof(Handlers.Player), nameof(Handlers.Player.PlayingAudioLog))]
+ [HarmonyPatch(typeof(AudioLog), nameof(AudioLog.ServerInteract))]
+ internal static class PlayingAudioLog
+ {
+ private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator)
+ {
+ List newInstructions = ListPool.Pool.Get(instructions);
+
+ Label retLabel = generator.DefineLabel();
+
+ newInstructions.InsertRange(
+ 0,
+ new CodeInstruction[]
+ {
+ // Player player = Player.Get(ReferenceHub);
+ new(OpCodes.Ldarg_1),
+ new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })),
+
+ // true
+ new(OpCodes.Ldc_I4_1),
+
+ // PlayingAudioLogEventArgs ev = new(Player, bool)
+ new(OpCodes.Newobj, GetDeclaredConstructors(typeof(PlayingAudioLogEventArgs))[0]),
+ new(OpCodes.Dup),
+
+ // Handlers.Player.OnPlayingAudioLog(ev)
+ new(OpCodes.Call, Method(typeof(Handlers.Player), nameof(Handlers.Player.OnPlayingAudioLog))),
+
+ // if (!ev.IsAllowed)
+ // return;
+ new(OpCodes.Callvirt, PropertyGetter(typeof(PlayingAudioLogEventArgs), nameof(PlayingAudioLogEventArgs.IsAllowed))),
+ new(OpCodes.Brfalse, retLabel),
+ });
+
+ newInstructions[newInstructions.Count - 1].labels.Add(retLabel);
+
+ for (int z = 0; z < newInstructions.Count; z++)
+ yield return newInstructions[z];
+
+ ListPool.Pool.Return(newInstructions);
+ }
+ }
+}
\ No newline at end of file