diff --git a/Spectator-Disabler/Patches/PocketItemManagerPatch.cs b/Spectator-Disabler/Patches/PocketItemManagerPatch.cs new file mode 100644 index 0000000..22be431 --- /dev/null +++ b/Spectator-Disabler/Patches/PocketItemManagerPatch.cs @@ -0,0 +1,73 @@ +using System.Collections.Generic; +using System.Reflection.Emit; +using Exiled.API.Features.Pools; +using HarmonyLib; +using JetBrains.Annotations; +using PlayerRoles; +using PlayerRoles.PlayableScps.Scp106; + +namespace SpectatorDisabler.Patches +{ + [HarmonyPatch(typeof(Scp106PocketItemManager), nameof(Scp106PocketItemManager.GetRandomValidSpawnPosition))] + internal static class PocketItemManagerPatch + { + /// + /// This transpiler replaces the following code: + /// + /// if (allHub.roleManager.CurrentRole is IFpcRole currentRole) + /// { + /// // get position to spawn item + /// } + /// + /// With this one: + /// + /// if (allHub.roleManager.CurrentRole is IFpcRole currentRole + /// && allHub.roleManager.CurrentRole.RoleTypeId != RoleTypeId.Tutorial) + /// { + /// // get position to spawn item + /// } + /// + /// + /// + /// The s of the original + /// method. + /// + /// + /// The new patched s of the + /// method. + /// + [UsedImplicitly] + private static IEnumerable Transpiler(IEnumerable instructions) + { + var newInstructions = ListPool.Pool.Get(instructions); + + for (var i = 0; i < newInstructions.Count; i++) + { + if (newInstructions[i].opcode == OpCodes.Isinst + && newInstructions[i - 1].opcode == OpCodes.Callvirt + && newInstructions[i - 2].opcode == OpCodes.Ldfld + && newInstructions[i - 3].opcode == OpCodes.Call) + { + // duplicate value of roleManager.CurrentRole on stack to use later + yield return new CodeInstruction(OpCodes.Dup); + } + + if (newInstructions[i].opcode == OpCodes.Ldloc_3 + && newInstructions[i - 1].opcode == OpCodes.Brfalse_S + && newInstructions[i - 2].opcode == OpCodes.Ldloc_3 + && newInstructions[i - 3].opcode == OpCodes.Stloc_3) + { + // add check for tutorial + yield return new CodeInstruction(OpCodes.Callvirt, AccessTools.PropertyGetter(typeof(PlayerRoleBase), nameof(PlayerRoleBase.RoleTypeId))); + yield return new CodeInstruction(OpCodes.Ldc_I4_S, 14); // RoleTypeId.Tutorial + yield return new CodeInstruction(OpCodes.Ceq); // currentRole.RoleTypeId == RoleTypeId.Tutorial + yield return new CodeInstruction(OpCodes.Brtrue_S, newInstructions[i - 1].operand); // continue + } + + yield return newInstructions[i]; + } + + ListPool.Pool.Return(newInstructions); + } + } +} diff --git a/docs/Testing.md b/docs/Testing.md index df627ff..d0d4162 100644 --- a/docs/Testing.md +++ b/docs/Testing.md @@ -20,4 +20,8 @@ Some test require more than one player and are marked with a remark. - A recently killed player can be revived by SCP-049 and spawns as a zombie - Requires 2 players - A zombie recently killed by another player can be revived one time - - Requires 3 players (SCP-049, human to be revived, human that kills zombie) + - Requires 3 players (SCP-049, zombie to be revived, human that kills zombie) + +## Pocket Dimension +- Items lost in the pocket dimension spawn around players that are not in the tutorial role + - Requires 2 players