diff --git a/SillySCP/API/Features/Scp049DataStore.cs b/SillySCP/API/Features/Scp049DataStore.cs
new file mode 100644
index 0000000..b076111
--- /dev/null
+++ b/SillySCP/API/Features/Scp049DataStore.cs
@@ -0,0 +1,42 @@
+using LabApi.Events.Arguments.PlayerEvents;
+using LabApi.Events.Arguments.Scp049Events;
+using LabApi.Events.Handlers;
+using LabApi.Features.Stores;
+using LabApi.Features.Wrappers;
+using PlayerRoles;
+using SecretAPI.Features;
+using UnityEngine;
+using Logger = LabApi.Features.Console.Logger;
+
+namespace SillySCP.API.Features
+{
+ public class LastKnownInformation
+ {
+ public Vector3 Position { get; set; }
+
+ public float Health { get; set; }
+
+ public float MaxHealth { get; set; }
+
+ public float HumeShield { get; set; }
+
+ public float MaxHumeShield { get; set; }
+ }
+ public class Scp049DataStore : CustomDataStore
+ {
+ public Scp049DataStore(Player player)
+ : base(player)
+ {
+ ActiveStores.Add(this);
+ }
+
+ ///
+ /// List of IDs of people who have left which should be SCP-049-2.
+ ///
+ public Dictionary Leavers { get; } = [];
+
+ public List ActivePlayers { get; } = [];
+
+ public static List ActiveStores { get; } = [];
+ }
+}
\ No newline at end of file
diff --git a/SillySCP/Handlers/Scp049Handler.cs b/SillySCP/Handlers/Scp049Handler.cs
new file mode 100644
index 0000000..2d3d215
--- /dev/null
+++ b/SillySCP/Handlers/Scp049Handler.cs
@@ -0,0 +1,74 @@
+using LabApi.Events.Arguments.PlayerEvents;
+using LabApi.Events.Arguments.Scp049Events;
+using LabApi.Events.Handlers;
+using LabApi.Features.Console;
+using PlayerRoles;
+using SecretAPI.Features;
+using SillySCP.API.Features;
+
+namespace SillySCP.Handlers
+{
+ public class Scp049Handler : IRegister
+ {
+ public void TryRegister()
+ {
+ Scp049Events.ResurrectedBody += OnRevived;
+ PlayerEvents.Dying += OnDying;
+ PlayerEvents.Joined += OnJoined;
+ ServerEvents.RoundRestarted += OnRoundRestarted;
+ }
+
+ public void TryUnregister()
+ {
+ Scp049Events.ResurrectedBody -= OnRevived;
+ PlayerEvents.Dying -= OnDying;
+ PlayerEvents.Joined -= OnJoined;
+ ServerEvents.RoundRestarted -= OnRoundRestarted;
+ }
+
+ public static void OnRevived(Scp049ResurrectedBodyEventArgs ev)
+ {
+ Scp049DataStore store = ev.Player.GetDataStore();
+ store.ActivePlayers.Add(ev.Target);
+ }
+
+ public static void OnDying(PlayerDyingEventArgs ev)
+ {
+ if(ev.Player.Role == RoleTypeId.Scp049)
+ Scp049DataStore.ActiveStores.Remove(Scp049DataStore.ActiveStores.FirstOrDefault(store => store.Owner == ev.Player));
+
+ if (ev.Player.Role != RoleTypeId.Scp0492)
+ return;
+
+ foreach (Scp049DataStore store in Scp049DataStore.ActiveStores.Where(store => store.ActivePlayers.Contains(ev.Player)))
+ {
+ store.ActivePlayers.Remove(ev.Player);
+ }
+ }
+
+ public static void OnJoined(PlayerJoinedEventArgs ev)
+ {
+ Scp049DataStore store = Scp049DataStore.ActiveStores.FirstOrDefault(store => store.Leavers.ContainsKey(ev.Player.UserId));
+ if (store == null)
+ return;
+
+ store.ActivePlayers.Add(ev.Player);
+
+ LastKnownInformation info = store.Leavers[ev.Player.UserId];
+
+ ev.Player.Role = RoleTypeId.Scp0492;
+ ev.Player.MaxHealth = info.MaxHealth;
+ ev.Player.Health = info.Health;
+ ev.Player.MaxHumeShield = info.MaxHumeShield;
+ ev.Player.HumeShield = info.HumeShield;
+ ev.Player.Position = info.Position;
+
+ store.Leavers.Remove(ev.Player.UserId);
+ }
+
+ public static void OnRoundRestarted()
+ {
+ Scp049DataStore.ActiveStores.Clear();
+ }
+ }
+}
\ No newline at end of file
diff --git a/SillySCP/Patches/DisconnectPatch.cs b/SillySCP/Patches/DisconnectPatch.cs
new file mode 100644
index 0000000..0b487cd
--- /dev/null
+++ b/SillySCP/Patches/DisconnectPatch.cs
@@ -0,0 +1,86 @@
+using System.Reflection.Emit;
+using HarmonyLib;
+using Interactables.Interobjects.DoorUtils;
+using LabApi.Features.Wrappers;
+using MapGeneration;
+using PlayerRoles;
+using PlayerStatsSystem;
+using SillySCP.API.Features;
+using UnityEngine;
+using Logger = LabApi.Features.Console.Logger;
+
+namespace SillySCP.Patches
+{
+ [HarmonyPatch(typeof(CustomNetworkManager), nameof(CustomNetworkManager.OnServerDisconnect))]
+ public static class DisconnectPatch
+ {
+ public static IEnumerable Transpiler(IEnumerable instructions)
+ {
+ CodeMatcher matcher = new CodeMatcher(instructions)
+ .MatchStartForward(
+ new CodeMatch(OpCodes.Callvirt, AccessTools.Method(typeof(PlayerStats), nameof(PlayerStats.DealDamage)))
+ )
+ .Advance(-6)
+ .Insert(
+ new CodeInstruction(OpCodes.Ldloc_1),
+ new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(DisconnectPatch), nameof(OnBeforeDeath)))
+ );
+
+ return matcher.InstructionEnumeration();
+ }
+
+ public static void OnBeforeDeath(ReferenceHub hub)
+ {
+ Player player = Player.Get(hub);
+
+ if (player is not { Role: RoleTypeId.Scp0492 })
+ return;
+
+ Scp049DataStore store = Scp049DataStore.ActiveStores.FirstOrDefault(store => store.ActivePlayers.Contains(player));
+ if (store == null)
+ return;
+
+ Vector3 position;
+
+ if (player.Room != null)
+ {
+ IEnumerable doors = player.Room.Doors.Where(door =>
+ door is not ElevatorDoor && !door.IsLocked && door.Permissions.HasFlag(DoorPermissionFlags.None));
+
+ float closestDistance = float.MaxValue;
+ Door closestDoor = null;
+ foreach (Door door in doors)
+ {
+ if (Vector3.Distance(door.Position, player.Position) >= closestDistance)
+ continue;
+ closestDistance = Vector3.Distance(door.Position, player.Position);
+ closestDoor = door;
+ }
+
+ position = closestDoor?.Position ?? player.Room.Position;
+ position += Vector3.one;
+
+ if (player.Room.Name is RoomName.EzGateA or RoomName.EzGateB or RoomName.HczCheckpointToEntranceZone)
+ {
+ position = player.Position;
+ }
+ }
+ else
+ {
+ position = player.Position;
+ }
+
+ LastKnownInformation info = new()
+ {
+ Position = position,
+ Health = player.Health,
+ MaxHealth = player.MaxHealth,
+ HumeShield = player.HumeShield,
+ MaxHumeShield = player.MaxHumeShield,
+ };
+
+ store.Leavers.Add(player.UserId, info);
+ store.ActivePlayers.Remove(player);
+ }
+ }
+}
\ No newline at end of file