From 2d34b1e645c827936e3d03719067c70e60e4e237 Mon Sep 17 00:00:00 2001 From: blankochan Date: Thu, 26 Jun 2025 21:01:45 -0500 Subject: [PATCH 1/5] refactor the towersit system --- SillySCP/API/Features/TowerSitSystem.cs | 143 ++++++++++++++++++++++++ SillySCP/Commands/TowerSit.cs | 55 +++------ 2 files changed, 160 insertions(+), 38 deletions(-) create mode 100644 SillySCP/API/Features/TowerSitSystem.cs diff --git a/SillySCP/API/Features/TowerSitSystem.cs b/SillySCP/API/Features/TowerSitSystem.cs new file mode 100644 index 0000000..21b7355 --- /dev/null +++ b/SillySCP/API/Features/TowerSitSystem.cs @@ -0,0 +1,143 @@ +using LabApi.Features.Wrappers; +using PlayerRoles; +using PlayerRoles.PlayableScps.Scp079; +using SecretAPI.Extensions; +using UnityEngine; + +namespace SillySCP.API.Features; + +public sealed class SitInfo // if need be this could be renamed into PlayerState and used in other things (like a mid-game volunteer system) +{ + + public readonly Player Player; + public readonly PlayerRoleBase Role; + + public readonly List Inventory; + public List 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; + + Inventory = player.DropAllItems(); + Ammo = player.DropAllAmmo(); + + // teleport items/ammo above the tutorial tower and lock the physics + foreach (var item in Inventory) + { + item.IsLocked = true; + item.IsInUse = true; + RoleTypeId.Tutorial.GetRandomSpawnPosition(out Vector3 spawnPos, out _); + item.Position = spawnPos + (Vector3.up * 25); + item.PickupStandardPhysics!.Rb.detectCollisions = false; + item.PickupStandardPhysics!.Rb.constraints = RigidbodyConstraints.FreezeAll; + item.PickupStandardPhysics!.Rb.isKinematic = true; + + if (item is Scp018Projectile projectile) + { + projectile.RemainingTime = 60 * 60; // if a round lasts more than an hour we have a problem qwq + } + + } + foreach (var ammo in Ammo) + { + ammo.IsLocked = true; + ammo.IsInUse = true; + RoleTypeId.Tutorial.GetRandomSpawnPosition(out Vector3 spawnPos, out _); + ammo.Position = spawnPos + (Vector3.up * 25); + ammo.PickupStandardPhysics!.Rb.detectCollisions = false; + ammo.PickupStandardPhysics!.Rb.constraints = RigidbodyConstraints.FreezeAll; + ammo.PickupStandardPhysics!.Rb.isKinematic = true; + } + + 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); + + + + + 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; + } + + foreach (var item in Inventory) + { + Player.AddItem(item); + item.Destroy(); + } + + foreach (var ammo in Ammo) + { + Player.AddAmmo(ammo.Type,ammo.Ammo); + ammo.Destroy(); + } + + } +} + + +public static class TowerSitSystem +{ + public static Dictionary ActiveSits = new (); + + public static bool Start(Player player) + { + + if (ActiveSits.ContainsKey(player)) return false; + + SitInfo sit = new(player); + ActiveSits[player] = sit; + + player.SetRole(RoleTypeId.Tutorial); + return true; + } + + public static bool End(Player player) + { + if (!ActiveSits.TryGetValue(player, out SitInfo sit)) return false; + sit.RestorePlayer(); + ActiveSits.Remove(player); + return true; + } + +} \ No newline at end of file diff --git a/SillySCP/Commands/TowerSit.cs b/SillySCP/Commands/TowerSit.cs index 4004341..5466a37 100644 --- a/SillySCP/Commands/TowerSit.cs +++ b/SillySCP/Commands/TowerSit.cs @@ -3,7 +3,9 @@ using LabApi.Features.Wrappers; using PlayerRoles; using SecretAPI.Extensions; +using SillySCP.API.Features; using UnityEngine; +using Logger = LabApi.Features.Console.Logger; namespace SillySCP.Commands { @@ -19,34 +21,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 +35,42 @@ 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: From 4476042208a88b7c23c0a22a5d81076bf2936ab7 Mon Sep 17 00:00:00 2001 From: blankochan Date: Thu, 26 Jun 2025 21:36:25 -0500 Subject: [PATCH 2/5] stylistic cleanup --- SillySCP/API/Features/TowerSitSystem.cs | 33 ++++--------------------- SillySCP/Commands/TowerSit.cs | 7 +----- 2 files changed, 6 insertions(+), 34 deletions(-) diff --git a/SillySCP/API/Features/TowerSitSystem.cs b/SillySCP/API/Features/TowerSitSystem.cs index 21b7355..c26488f 100644 --- a/SillySCP/API/Features/TowerSitSystem.cs +++ b/SillySCP/API/Features/TowerSitSystem.cs @@ -8,7 +8,6 @@ namespace SillySCP.API.Features; public sealed class SitInfo // if need be this could be renamed into PlayerState and used in other things (like a mid-game volunteer system) { - public readonly Player Player; public readonly PlayerRoleBase Role; @@ -17,8 +16,7 @@ namespace SillySCP.API.Features; public readonly float MaxHealth; public readonly float Health; - - + public readonly float MaxHume; public readonly float Hume; @@ -27,7 +25,6 @@ namespace SillySCP.API.Features; public readonly int ComputerXp; public SitInfo(Player player) { - Player = player; Role = player.RoleBase; @@ -35,7 +32,7 @@ public SitInfo(Player player) Ammo = player.DropAllAmmo(); // teleport items/ammo above the tutorial tower and lock the physics - foreach (var item in Inventory) + foreach (Pickup item in Inventory.Concat(Ammo)) { item.IsLocked = true; item.IsInUse = true; @@ -45,23 +42,8 @@ public SitInfo(Player player) item.PickupStandardPhysics!.Rb.constraints = RigidbodyConstraints.FreezeAll; item.PickupStandardPhysics!.Rb.isKinematic = true; - if (item is Scp018Projectile projectile) - { - projectile.RemainingTime = 60 * 60; // if a round lasts more than an hour we have a problem qwq - } - + if (item is Scp018Projectile projectile) projectile.RemainingTime = 60 * 60; // if a round lasts more than an hour we have bigger problems qwq } - foreach (var ammo in Ammo) - { - ammo.IsLocked = true; - ammo.IsInUse = true; - RoleTypeId.Tutorial.GetRandomSpawnPosition(out Vector3 spawnPos, out _); - ammo.Position = spawnPos + (Vector3.up * 25); - ammo.PickupStandardPhysics!.Rb.detectCollisions = false; - ammo.PickupStandardPhysics!.Rb.constraints = RigidbodyConstraints.FreezeAll; - ammo.PickupStandardPhysics!.Rb.isKinematic = true; - } - MaxHealth = player.MaxHealth; Health = player.Health; @@ -79,18 +61,13 @@ public SitInfo(Player player) 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); - - - Player.MaxHealth = MaxHealth; Player.Health = Health; Player.MaxHumeShield = MaxHume; Player.HumeShield = Hume; - Player.Position = Position; @@ -100,13 +77,13 @@ public void RestorePlayer() tierManager.TotalExp = ComputerXp; } - foreach (var item in Inventory) + foreach (Pickup item in Inventory) { Player.AddItem(item); item.Destroy(); } - foreach (var ammo in Ammo) + foreach (AmmoPickup ammo in Ammo) { Player.AddAmmo(ammo.Type,ammo.Ammo); ammo.Destroy(); diff --git a/SillySCP/Commands/TowerSit.cs b/SillySCP/Commands/TowerSit.cs index 5466a37..5bb2b5e 100644 --- a/SillySCP/Commands/TowerSit.cs +++ b/SillySCP/Commands/TowerSit.cs @@ -1,11 +1,9 @@ using CommandSystem; using LabApi.Features.Stores; using LabApi.Features.Wrappers; -using PlayerRoles; -using SecretAPI.Extensions; using SillySCP.API.Features; using UnityEngine; -using Logger = LabApi.Features.Console.Logger; + namespace SillySCP.Commands { @@ -48,8 +46,6 @@ out string response } Player player = players.First(); - - switch (arguments.At(0)) { case "add": @@ -78,7 +74,6 @@ out string response return false; } - } } } \ No newline at end of file From 76507b314ddd23cc594adcce33b66077ca233176 Mon Sep 17 00:00:00 2001 From: blankochan Date: Sat, 28 Jun 2025 15:55:24 -0500 Subject: [PATCH 3/5] harmony patch (delete when labapi updates) --- SillySCP/Patches/DropAllAmmoPatch.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 SillySCP/Patches/DropAllAmmoPatch.cs 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 From 6192b8e3aa3d431cebff926bf063f54d59c2d789 Mon Sep 17 00:00:00 2001 From: blankochan Date: Sat, 28 Jun 2025 20:13:32 -0500 Subject: [PATCH 4/5] fix: Effect support for TowerSit Rework --- SillySCP/API/Features/TowerSitSystem.cs | 35 ++++++++++++++----------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/SillySCP/API/Features/TowerSitSystem.cs b/SillySCP/API/Features/TowerSitSystem.cs index c26488f..ddc8b0a 100644 --- a/SillySCP/API/Features/TowerSitSystem.cs +++ b/SillySCP/API/Features/TowerSitSystem.cs @@ -1,8 +1,11 @@ -using LabApi.Features.Wrappers; +using CustomPlayerEffects; +using GameCore; +using LabApi.Features.Wrappers; using PlayerRoles; using PlayerRoles.PlayableScps.Scp079; using SecretAPI.Extensions; using UnityEngine; +using Logger = LabApi.Features.Console.Logger; namespace SillySCP.API.Features; @@ -11,9 +14,10 @@ namespace SillySCP.API.Features; public readonly Player Player; public readonly PlayerRoleBase Role; + public readonly List<(StatusEffectBase Effect,float RemainingDuration,byte Intensity)> ActiveEffects = []; public readonly List Inventory; - public List Ammo; - + public readonly List Ammo; + public readonly float MaxHealth; public readonly float Health; @@ -28,6 +32,7 @@ public SitInfo(Player player) Player = player; Role = player.RoleBase; + foreach (var effect in player.ActiveEffects) ActiveEffects.Add((effect,effect.TimeLeft,effect.Intensity)); Inventory = player.DropAllItems(); Ammo = player.DropAllAmmo(); @@ -63,6 +68,18 @@ 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 item in Inventory) + { + Player.AddItem(item); + item.Destroy(); + } + foreach (AmmoPickup ammo in Ammo) + { + Player.AddAmmo(ammo.Type,ammo.Ammo); + ammo.Destroy(); + } + Player.MaxHealth = MaxHealth; Player.Health = Health; @@ -70,24 +87,12 @@ public void RestorePlayer() Player.HumeShield = Hume; Player.Position = Position; - if (Player.RoleBase is Scp079Role computerRole) { computerRole.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager); tierManager.TotalExp = ComputerXp; } - foreach (Pickup item in Inventory) - { - Player.AddItem(item); - item.Destroy(); - } - - foreach (AmmoPickup ammo in Ammo) - { - Player.AddAmmo(ammo.Type,ammo.Ammo); - ammo.Destroy(); - } } } From d573a7890db3b1c15aa771d4a0f6c5024b6f22da Mon Sep 17 00:00:00 2001 From: blankochan Date: Mon, 14 Jul 2025 17:29:56 -0500 Subject: [PATCH 5/5] fix: clone items instead of drop --- .../API/Extensions/InventoryExtensions.cs | 41 ++++++++++++ SillySCP/API/Features/TowerSitSystem.cs | 66 ++++++++----------- 2 files changed, 67 insertions(+), 40 deletions(-) create mode 100644 SillySCP/API/Extensions/InventoryExtensions.cs 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 index ddc8b0a..7aa37af 100644 --- a/SillySCP/API/Features/TowerSitSystem.cs +++ b/SillySCP/API/Features/TowerSitSystem.cs @@ -1,22 +1,23 @@ using CustomPlayerEffects; -using GameCore; +using InventorySystem.Items.Usables.Scp1344; using LabApi.Features.Wrappers; +using NorthwoodLib.Pools; using PlayerRoles; using PlayerRoles.PlayableScps.Scp079; -using SecretAPI.Extensions; +using SillySCP.API.Extensions; using UnityEngine; -using Logger = LabApi.Features.Console.Logger; +using Scp1344Item = LabApi.Features.Wrappers.Scp1344Item; namespace SillySCP.API.Features; -public sealed class SitInfo // if need be this could be renamed into PlayerState and used in other things (like a mid-game volunteer system) +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 List Ammo; + public readonly KeyValuePair[] Ammo; public readonly float MaxHealth; public readonly float Health; @@ -33,22 +34,10 @@ public SitInfo(Player player) Role = player.RoleBase; foreach (var effect in player.ActiveEffects) ActiveEffects.Add((effect,effect.TimeLeft,effect.Intensity)); - Inventory = player.DropAllItems(); - Ammo = player.DropAllAmmo(); - // teleport items/ammo above the tutorial tower and lock the physics - foreach (Pickup item in Inventory.Concat(Ammo)) - { - item.IsLocked = true; - item.IsInUse = true; - RoleTypeId.Tutorial.GetRandomSpawnPosition(out Vector3 spawnPos, out _); - item.Position = spawnPos + (Vector3.up * 25); - item.PickupStandardPhysics!.Rb.detectCollisions = false; - item.PickupStandardPhysics!.Rb.constraints = RigidbodyConstraints.FreezeAll; - item.PickupStandardPhysics!.Rb.isKinematic = true; - - if (item is Scp018Projectile projectile) projectile.RemainingTime = 60 * 60; // if a round lasts more than an hour we have bigger problems qwq - } + Inventory = player.CloneItems(); + Ammo = player.Ammo.ToArray(); + MaxHealth = player.MaxHealth; Health = player.Health; @@ -69,16 +58,21 @@ public void RestorePlayer() 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 item in Inventory) - { - Player.AddItem(item); - item.Destroy(); - } - foreach (AmmoPickup ammo in Ammo) + + foreach (Pickup pickup in Inventory) { - Player.AddAmmo(ammo.Type,ammo.Ammo); - ammo.Destroy(); + 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; @@ -92,34 +86,26 @@ public void RestorePlayer() computerRole.SubroutineModule.TryGetSubroutine(out Scp079TierManager tierManager); tierManager.TotalExp = ComputerXp; } - - } } - public static class TowerSitSystem { public static Dictionary ActiveSits = new (); - public static bool Start(Player player) + public static void Start(Player player) { - - if (ActiveSits.ContainsKey(player)) return false; - + if (ActiveSits.ContainsKey(player)) return; SitInfo sit = new(player); ActiveSits[player] = sit; player.SetRole(RoleTypeId.Tutorial); - return true; } - public static bool End(Player player) + public static void End(Player player) { - if (!ActiveSits.TryGetValue(player, out SitInfo sit)) return false; + if (!ActiveSits.TryGetValue(player, out SitInfo sit)) return; sit.RestorePlayer(); ActiveSits.Remove(player); - return true; } - } \ No newline at end of file