diff --git a/SillySCP/API/Extensions/InventoryExtensions.cs b/SillySCP/API/Extensions/InventoryExtensions.cs new file mode 100644 index 0000000..395adf6 --- /dev/null +++ b/SillySCP/API/Extensions/InventoryExtensions.cs @@ -0,0 +1,41 @@ +using InventorySystem.Items.Pickups; +using LabApi.Features.Wrappers; +using Mirror; +using PlayerRoles; +using SecretAPI.Extensions; +using UnityEngine; +using Logger = LabApi.Features.Console.Logger; + +namespace SillySCP.API.Extensions; + +public static class InventoryExtensions + +{ + public static void FreezeItemPickup(ItemPickupBase pickup) + { + Logger.Info($"Freezing item pickup {pickup.name}"); + pickup.Info.Locked = true; + pickup.Info.InUse = true; + + PickupStandardPhysics physics = pickup.PhysicsModule as PickupStandardPhysics; + + physics!.Rb.constraints = RigidbodyConstraints.FreezeAll; + physics!.Rb.isKinematic = true; + if (pickup is InventorySystem.Items.ThrowableProjectiles.Scp018Projectile projectile) projectile.TargetTime = NetworkTime.time + 60 * 60; + } + public static List CloneItems(this Player player) + { + List pickups = new (); + foreach (var item in player.Items) + { + + ItemPickupBase ipb = UnityEngine.Object.Instantiate(item.Base.PickupDropModel, Vector3.up, Quaternion.identity); + ipb.NetworkInfo = new PickupSyncInfo(item.Base.ItemTypeId,item.Base.Weight,item.Base.ItemSerial,locked:true); + FreezeItemPickup(ipb); + Pickup pickup = Pickup.Get(ipb); + + pickups.Add(pickup); + } + return pickups; + } +} \ No newline at end of file diff --git a/SillySCP/API/Features/TowerSitSystem.cs b/SillySCP/API/Features/TowerSitSystem.cs new file mode 100644 index 0000000..7aa37af --- /dev/null +++ b/SillySCP/API/Features/TowerSitSystem.cs @@ -0,0 +1,111 @@ +using CustomPlayerEffects; +using InventorySystem.Items.Usables.Scp1344; +using LabApi.Features.Wrappers; +using NorthwoodLib.Pools; +using PlayerRoles; +using PlayerRoles.PlayableScps.Scp079; +using SillySCP.API.Extensions; +using UnityEngine; +using Scp1344Item = LabApi.Features.Wrappers.Scp1344Item; + +namespace SillySCP.API.Features; + +public sealed class SitInfo // if need be this could be repurposed into a way to Stash a players state +{ + public readonly Player Player; + public readonly PlayerRoleBase Role; + + public readonly List<(StatusEffectBase Effect,float RemainingDuration,byte Intensity)> ActiveEffects = []; + public readonly List Inventory; + public readonly KeyValuePair[] Ammo; + + public readonly float MaxHealth; + public readonly float Health; + + public readonly float MaxHume; + public readonly float Hume; + + public readonly Vector3 Position; + + public readonly int ComputerXp; + public SitInfo(Player player) + { + Player = player; + Role = player.RoleBase; + + foreach (var effect in player.ActiveEffects) ActiveEffects.Add((effect,effect.TimeLeft,effect.Intensity)); + + Inventory = player.CloneItems(); + Ammo = player.Ammo.ToArray(); + + MaxHealth = player.MaxHealth; + Health = player.Health; + + MaxHume = player.MaxHumeShield; + Hume = player.HumeShield; + + Position = player.Position; + if (Player.RoleBase is Scp079Role computerRole) + { + computerRole.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager); + ComputerXp = tierManager.TotalExp ; + } + } + + public void RestorePlayer() + { + if (!Player.IsOnline) return; // just in case they disconnect and something tries to restore them + Player.SetRole(Role.RoleTypeId,reason:RoleChangeReason.RemoteAdmin, flags:RoleSpawnFlags.None); + + foreach (var effect in ActiveEffects) Player.EnableEffect(effect.Effect,effect.Intensity,effect.RemainingDuration); + + foreach (Pickup pickup in Inventory) + { + Item item = Player.AddItem(pickup); + + if (ActiveEffects.Exists(activeEffect => activeEffect.Effect is Scp1344)) + if (item is Scp1344Item scp1344) { + Player.DisableEffect(); + scp1344.Use(); //any effect that puts something on the players screen doesn't like being set frame 0 + scp1344.Status = Scp1344Status.Active; + } + } + ListPool.Shared.Return(Inventory); + + foreach (KeyValuePair ammo in Ammo) Player.AddAmmo(ammo.Key,ammo.Value); + + Player.MaxHealth = MaxHealth; + Player.Health = Health; + + Player.MaxHumeShield = MaxHume; + Player.HumeShield = Hume; + + Player.Position = Position; + if (Player.RoleBase is Scp079Role computerRole) + { + computerRole.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager); + tierManager.TotalExp = ComputerXp; + } + } +} + +public static class TowerSitSystem +{ + public static Dictionary ActiveSits = new (); + + public static void Start(Player player) + { + if (ActiveSits.ContainsKey(player)) return; + SitInfo sit = new(player); + ActiveSits[player] = sit; + + player.SetRole(RoleTypeId.Tutorial); + } + + public static void End(Player player) + { + if (!ActiveSits.TryGetValue(player, out SitInfo sit)) return; + sit.RestorePlayer(); + ActiveSits.Remove(player); + } +} \ No newline at end of file diff --git a/SillySCP/Commands/TowerSit.cs b/SillySCP/Commands/TowerSit.cs index 4004341..5bb2b5e 100644 --- a/SillySCP/Commands/TowerSit.cs +++ b/SillySCP/Commands/TowerSit.cs @@ -1,10 +1,10 @@ using CommandSystem; using LabApi.Features.Stores; using LabApi.Features.Wrappers; -using PlayerRoles; -using SecretAPI.Extensions; +using SillySCP.API.Features; using UnityEngine; + namespace SillySCP.Commands { public class DataStore : CustomDataStore @@ -19,34 +19,13 @@ public DataStore(Player player) [CommandHandler(typeof(RemoteAdminCommandHandler))] public class TowerSit : ICommand { - private void AddPlayer(Player player) - { - player.GetDataStore().Position = player.Position; - player.IsGodModeEnabled = true; - if (player.IsSCP) - { - player.SendBroadcast("If you go to Settings, Server-specific, you can set a bind for proximity chat",10); - } - RoleTypeId.Tutorial.GetRandomSpawnPosition(out Vector3 position, out float _); - player.Position = position; - } - private static bool RestorePlayer(Player player) - { - DataStore store = player.GetDataStore(); - player.Position = (Vector3)store.Position!; - store.Position = null; - player.IsGodModeEnabled = false; - return true; - } - - public string Command { get; } = "towersit"; public string Description { get; } = "Teleport a player to the tower and return them after"; public string[] Aliases { get; } = ["ts"]; private const string Usage = "Invalid Usage, Applicable usages:" + - "\n ts add - teleport a player to the tower (Both IDs and Partial Usernames are accepted)" + - "\n ts restore - restore a players original position (Both IDs and Partial Usernames are accepted)"; + "\n ts add - Create a towersit (Usernames are accepted)" + + "\n ts restore - End a towersit (Usernames are accepted)"; public bool Execute( ArraySegment arguments, @@ -54,44 +33,40 @@ public bool Execute( out string response ) { - if (arguments.Count != 2) { response = Usage; return false; } - if (!Player.TryGet(arguments.At(1), out Player player)) + if (!Player.TryGetPlayersByName(arguments.At(1), out List players)) // no method for the first player to match { response = "Must be a player!"; return false; } - - if (!player.IsAlive) - { - response = "Player is dead!"; - return false; - } + Player player = players.First(); switch (arguments.At(0)) { case "add": - if (player.GetDataStore().Position != null) + if (TowerSitSystem.ActiveSits.ContainsKey(player)) { - response = "Player already has a return position!"; + response =$"{player.Nickname} is already in the tower!"; return false; } - AddPlayer(player); - response = $"{player.Nickname} teleporting to the tower!"; + TowerSitSystem.Start(player); + response = $"{player.Nickname} has been towered!"; return true; case "restore": - if (!RestorePlayer(player)) + if (!TowerSitSystem.ActiveSits.ContainsKey(player)) { - response = $"Player {player.Nickname} doesnt have a return position!"; + response = $"Player {player.Nickname} is not actively in the tower!"; return false; } - response = $"{player.Nickname} restored from the tower!"; + + TowerSitSystem.End(player); + response = $"returned {player.Nickname} from the tower!"; return true; default: @@ -99,7 +74,6 @@ out string response return false; } - } } } \ No newline at end of file diff --git a/SillySCP/Patches/DropAllAmmoPatch.cs b/SillySCP/Patches/DropAllAmmoPatch.cs new file mode 100644 index 0000000..3171e37 --- /dev/null +++ b/SillySCP/Patches/DropAllAmmoPatch.cs @@ -0,0 +1,17 @@ +using HarmonyLib; +using NorthwoodLib.Pools; + +namespace SillySCP.Patches; +[HarmonyPatch(typeof(LabApi.Features.Wrappers.Player), nameof(LabApi.Features.Wrappers.Player.DropAllAmmo))] +public class DropAllAmmoPatch +{ + public static bool Prefix(LabApi.Features.Wrappers.Player __instance, ref List __result) + { + List ammo = ListPool.Shared.Rent(); + foreach (KeyValuePair pair in __instance.Ammo.ToDictionary(e => e.Key, e => e.Value)) + ammo.AddRange(__instance.DropAmmo(pair.Key, pair.Value)); + + __result = ammo; + return false; + } +} \ No newline at end of file