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

- English + English


- Русский + Русский


- 中文 + 中文


- Español + Español


- Polski + Polski


- Português-BR + Português-BR


- Italiano + Italiano


- Čeština + Čeština


- Dansk + Dansk


- Türkçe + Türkçe


- German + German


- Français + Français


- 한국어 + 한국어


- ไทย + ไทย
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