diff --git a/EXILED/Exiled.API/Features/Roles/Scp939Role.cs b/EXILED/Exiled.API/Features/Roles/Scp939Role.cs index 4396f8f87b..65ab01864a 100644 --- a/EXILED/Exiled.API/Features/Roles/Scp939Role.cs +++ b/EXILED/Exiled.API/Features/Roles/Scp939Role.cs @@ -90,6 +90,11 @@ internal Scp939Role(Scp939GameRole baseRole) /// ~Scp939Role() => ListPool.Pool.Return(VisiblePlayers); + /// + /// Gets a list of players who are turned away from SCP-939 AmnesticCloud Ability. + /// + public static HashSet TurnedPlayers { get; } = new(20); + /// public override RoleTypeId Type { get; } = RoleTypeId.Scp939; diff --git a/EXILED/Exiled.Events/Config.cs b/EXILED/Exiled.Events/Config.cs index 56a8206e74..5559774efa 100644 --- a/EXILED/Exiled.Events/Config.cs +++ b/EXILED/Exiled.Events/Config.cs @@ -44,6 +44,12 @@ public sealed class Config : IConfig [Description("Indicates whether SCP-049 can sense tutorial players")] public bool CanScp049SenseTutorial { get; set; } = true; + /// + /// Gets or sets a value indicating whether Tutorial is affected by Scp-939 Amnestic Cloud. + /// + [Description("Indicates whether Tutorial is affected by Scp-939 Amnestic Cloud")] + public bool TutorialAffectedByScp939AmnesticCloud { get; set; } = true; + /// /// Gets or sets a value indicating whether tutorial is affected by SCP-079 scan. /// diff --git a/EXILED/Exiled.Events/Handlers/Internal/Round.cs b/EXILED/Exiled.Events/Handlers/Internal/Round.cs index 99212af062..2091870dfa 100644 --- a/EXILED/Exiled.Events/Handlers/Internal/Round.cs +++ b/EXILED/Exiled.Events/Handlers/Internal/Round.cs @@ -62,6 +62,7 @@ public static void OnRestartingRound() Scp173Role.TurnedPlayers.Clear(); Scp096Role.TurnedPlayers.Clear(); Scp079Role.TurnedPlayers.Clear(); + Scp939Role.TurnedPlayers.Clear(); MultiAdminFeatures.CallEvent(MultiAdminFeatures.EventType.ROUND_END); diff --git a/EXILED/Exiled.Events/Patches/Generic/StayAtAmnesticCloud.cs b/EXILED/Exiled.Events/Patches/Generic/StayAtAmnesticCloud.cs new file mode 100644 index 0000000000..6541c383f3 --- /dev/null +++ b/EXILED/Exiled.Events/Patches/Generic/StayAtAmnesticCloud.cs @@ -0,0 +1,85 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) ExMod Team. All rights reserved. +// Licensed under the CC BY-SA 3.0 license. +// +// ----------------------------------------------------------------------- + +namespace Exiled.Events.Patches.Generic +{ + using System.Collections.Generic; + using System.Reflection.Emit; + + using Exiled.API.Features; + using Exiled.API.Features.Pools; + using HarmonyLib; + using PlayerRoles; + using PlayerRoles.PlayableScps.Scp939; + + using static HarmonyLib.AccessTools; + + using ExiledEvents = Exiled.Events.Events; + using Scp939Role = API.Features.Roles.Scp939Role; + + /// + /// Patches . + /// . + /// + [HarmonyPatch(typeof(Scp939AmnesticCloudInstance), nameof(Scp939AmnesticCloudInstance.OnStay))] + internal static class StayAtAmnesticCloud + { + private static IEnumerable Transpiler(IEnumerable instructions, ILGenerator generator) + { + List newInstructions = ListPool.Pool.Get(instructions); + + Label continueLabel = generator.DefineLabel(); + + Label returnLabel = generator.DefineLabel(); + + // Second check pointer + // We use it to pass execution + // to the second check if the first check fails, + // otherwise the second check won't be executed + Label secondCheckPointer = generator.DefineLabel(); + + newInstructions[0].WithLabels(continueLabel); + + // if (referenceHub.roleManager.CurrentRole.RoleTypeId == RoleTypeId.Tutorial && ExiledEvents.Instance.Config.TutorialAffectedByScp939AmnesticCloud + // || Scp939Role.TurnedPlayers.Contains(Player.Get(referenceHub))) + // return; + newInstructions.InsertRange( + 0, + new[] + { + // if ((referenceHub.roleManager.CurrentRole.RoleTypeId == RoleTypeId.Tutorial && + new(OpCodes.Ldarg_1), + new(OpCodes.Ldfld, Field(typeof(ReferenceHub), nameof(ReferenceHub.roleManager))), + new(OpCodes.Callvirt, PropertyGetter(typeof(PlayerRoleManager), nameof(PlayerRoleManager.CurrentRole))), + new(OpCodes.Callvirt, PropertyGetter(typeof(PlayerRoleBase), nameof(PlayerRoleBase.RoleTypeId))), + new(OpCodes.Ldc_I4_S, (sbyte)RoleTypeId.Tutorial), + new(OpCodes.Bne_Un_S, secondCheckPointer), + + // ExiledEvents.Instance.Config.TutorialAffectedByScp939AmnesticCloud) + new(OpCodes.Call, PropertyGetter(typeof(ExiledEvents), nameof(ExiledEvents.Instance))), + new(OpCodes.Callvirt, PropertyGetter(typeof(Plugin), nameof(Plugin.Config))), + new(OpCodes.Callvirt, PropertyGetter(typeof(Config), nameof(Config.TutorialAffectedByScp939AmnesticCloud))), + new(OpCodes.Brtrue_S, returnLabel), + + // || Scp939Role.TurnedPlayers.Contains(Player.Get(referenceHub)) + new CodeInstruction(OpCodes.Call, PropertyGetter(typeof(Scp939Role), nameof(Scp939Role.TurnedPlayers))).WithLabels(secondCheckPointer), + new(OpCodes.Ldarg_1), + new(OpCodes.Call, Method(typeof(Player), nameof(Player.Get), new[] { typeof(ReferenceHub) })), + new(OpCodes.Callvirt, Method(typeof(HashSet), nameof(HashSet.Contains))), + new(OpCodes.Brfalse_S, continueLabel), + + // return; + new CodeInstruction(OpCodes.Ret).WithLabels(returnLabel), + }); + + for (int i = 0; i < newInstructions.Count; i++) + yield return newInstructions[i]; + + ListPool.Pool.Return(newInstructions); + } + } +} \ No newline at end of file