using HarmonyLib; using Oxide.Core; using Oxide.Core.Configuration; using Oxide.Core.Libraries.Covalence; using Oxide.Core.Plugins; using System.Collections.Generic; using System; using System.Globalization; using ConVar; using System.Linq; using UnityEngine; namespace Oxide.Plugins { [Info("AdminCuff", "Pumpline", "1.1.1")] [Description("Be able to permanently cuff players.")] public class AdminCuff : RustPlugin { private HashSet cuffedPlayers = new HashSet(); private HashSet godPlayers = new HashSet(); private static AdminCuff ins; private const string PERM_USE = "admincuff.use"; private const string PERM_PROTECTED = "admincuff.protected"; private const int HANDCUFF_ITEMID = -839576748; #region Init private void OnServerInitialized() { ins = this; LoadDefaultMessages(); permission.RegisterPermission(PERM_USE, this); permission.RegisterPermission(PERM_PROTECTED, this); AddCovalenceCommand("cuff", nameof(CuffCommand)); AddCovalenceCommand("uncuff", nameof(UnCuffCommand)); } private void Unload() { ins = null; foreach (var cuffedPlayer in cuffedPlayers.ToArray()) { var player = BasePlayer.FindByID(cuffedPlayer); if (player == null) continue; UncuffPlayer(player); } } #endregion #region Lang protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary() { ["NoPerms"] = "You are not allowed to use this command!", ["InvalidPlayer"] = "Invalid player, please provide a name or steamid.", ["InvalidUsageCuff"] = "Invalid usage! /cuff ", ["InvalidUsageUncuff"] = "Invalid usage! /uncuff ", ["TargetNotFound"] = "Target not found.", ["TargetNotCuffed"] = "The target player is not cuffed", ["TargetProtected"] = "The targeted user is protected.", ["TargetCuffed"] = "Cuffed player {0}", ["TargetUncuffed"] = "Uncuffed player {0}", ["TargetGodEnabled"] = "Enabled god mode for {0}", ["ErrorCuffedByAdmin"] = "You cannot uncuff someone who has been cuffed by an admin." }, this); } #endregion #region Hooks private object CanBeTargeted(BasePlayer player, MonoBehaviour behaviour) { if (godPlayers.Contains(player.userID.Get())) return false; return null; } private object CanBradleyApcTarget(BradleyAPC apc, BasePlayer player) { if (godPlayers.Contains(player.userID.Get())) return false; return null; } private object OnNpcTarget(BaseEntity npc, BasePlayer player) { if (godPlayers.Contains(player.userID.Get())) return false; return null; } private object OnEntityTakeDamage(BasePlayer player, HitInfo info) { if (godPlayers.Contains(player.userID.Get())) info.damageTypes.ScaleAll(0); return null; } private void OnPlayerSleepEnded(BasePlayer player) { if (cuffedPlayers.Contains(player.userID.Get()) && player.GetHeldEntity() is not Handcuffs) DoHandcuff(player); } private object CanDropActiveItem(BasePlayer player) { if (player != null && cuffedPlayers.Contains(player.userID.Get()) && player.GetHeldEntity() is Handcuffs) return false; return null; } private void OnPlayerRecovered(BasePlayer player) { if (cuffedPlayers.Contains(player.userID.Get())) ins.DoHandcuff(player); } private void OnPlayerSleep(BasePlayer player) { if (cuffedPlayers.Contains(player.userID.Get())) { var item = GetAdminHandcuff(player); if (item == null) return; item.Remove(); } } private object OnPlayerLand(BasePlayer player, float num) { if (cuffedPlayers.Contains(player.userID.Get())) return false; return null; } private void OnPlayerDeath(BasePlayer player, HitInfo info) { if (cuffedPlayers.Contains(player.userID.Get())) { var item = GetAdminHandcuff(player); if (item == null) return; item.Remove(); } } #endregion #region Commands private void CuffCommand(IPlayer player, string command, string[] args) { BasePlayer? bplayer = ToPlayer(player); if (bplayer == null) return; if (!permission.UserHasPermission(bplayer.UserIDString, PERM_USE)) { SendLangMessage(player, "NoPerms"); return; } if (args.Length >= 1) { if (GetSteamId(args[0]) == 0) { SendLangMessage(player, "InvalidPlayer"); return; } BasePlayer? target = GetUser(args[0]); if (target == null) { SendLangMessage(player, "TargetNotFound"); return; } if (permission.UserHasPermission(target.UserIDString, PERM_PROTECTED)) { SendLangMessage(player, "TargetProtected"); return; } CuffPlayer(target); SendLangMessage(player, "TargetCuffed", target.displayName); if (args.Length >= 2) { string shouldHaveGod = args[1]; if (bool.TryParse(shouldHaveGod, out bool godMode)) { if (godMode) { godPlayers.Add(target.userID.Get()); SendLangMessage(player, "TargetGodEnabled", target.displayName); } } } } else { SendLangMessage(player, "InvalidUsageCuff"); } } private void UnCuffCommand(IPlayer player, string command, string[] args) { BasePlayer? bplayer = ToPlayer(player); if (bplayer == null) return; if (!permission.UserHasPermission(bplayer.UserIDString, PERM_USE)) { SendLangMessage(player, "NoPerms"); return; } if (args.Length >= 1) { if (GetSteamId(args[0]) == 0) { SendLangMessage(player, "InvalidPlayer"); return; } BasePlayer? target = GetUser(args[0]); if (target == null) { SendLangMessage(player, "TargetNotFound"); return; } if (!cuffedPlayers.Contains(target.userID)) { SendLangMessage(player, "TargetNotCuffed"); return; } UncuffPlayer(target); SendLangMessage(player, "TargetUncuffed", target.displayName); } else { SendLangMessage(player, "InvalidUsageUncuff"); } } #endregion #region Core private void CuffPlayer(BasePlayer player) { if (cuffedPlayers.Contains(player.userID.Get())) return; if (DoHandcuff(player)) cuffedPlayers.Add(player.userID.Get()); } private void UncuffPlayer(BasePlayer player) { if (!cuffedPlayers.Contains(player.userID.Get())) return; var ent = player.GetHeldEntity(); if (ent is Handcuffs cuff) { cuff.SetLocked(false); GetOwnerItem(cuff)?.Remove(); } cuffedPlayers.Remove(player.userID.Get()); if (godPlayers.Contains(player.userID.Get())) godPlayers.Remove(player.userID.Get()); } private bool DoHandcuff(BasePlayer player) { var cuff = ItemManager.CreateByItemID(HANDCUFF_ITEMID); if (!cuff.MoveToContainer(player.inventory.containerBelt)) { Item slot = player.inventory.containerBelt.GetSlot(0); if (slot != null) { if (!slot.MoveToContainer(player.inventory.containerMain)) slot.DropAndTossUpwards(player.transform.position); if (!cuff.MoveToContainer(player.inventory.containerBelt)) return false; } } player.ClientRPC(RpcTarget.Player("SetActiveBeltSlot", player), cuff.position, cuff.uid); // to make it his held item var heldCuff = cuff.GetHeldEntity() as Handcuffs; if (heldCuff == null) return false; heldCuff.ConditionLossPerSecond = 0; player.SetPlayerFlag(BasePlayer.PlayerFlags.IsRestrained, true); player.SendNetworkUpdateImmediate(); cuff.SetFlag(global::Item.Flag.IsOn, true); cuff.MarkDirty(); player.Server_CancelGesture(); heldCuff.SetLocked(true, player, cuff); Effect.server.Run(heldCuff.lockEffect.resourcePath, player, 0U, Vector3.zero, Vector3.zero); return true; } #endregion #region Helpers private static Item? GetOwnerItem(HeldEntity entity) { BasePlayer ownerPlayer = entity.GetOwnerPlayer(); return ownerPlayer == null || ownerPlayer.inventory == null ? null : ownerPlayer.inventory.FindItemByUID(entity.ownerItemUID); } private BasePlayer? GetUser(string prompt) { var userID = GetSteamId(prompt); return userID == 0 ? null : BasePlayer.FindByID(userID); } private static Item? GetAdminHandcuff(BasePlayer player) { return !player.inventory.FindItemsByItemID(HANDCUFF_ITEMID).Any() ? null : player.inventory.FindItemsByItemID(HANDCUFF_ITEMID).FirstOrDefault(); } private ulong GetSteamId(string prompt) { if (prompt.IsSteamId()) return ulong.Parse(prompt); if (!BasePlayer.allPlayerList.Any()) return 0; foreach (BasePlayer player in BasePlayer.allPlayerList) if (player.displayName.Contains(prompt, CompareOptions.OrdinalIgnoreCase)) return player.userID.Get(); return 0; } private static BasePlayer? ToPlayer(IPlayer user) { return user.Object as BasePlayer; } private void SendLangMessage(IPlayer player, string key, params string[] format) { if (format == null || format.Length == 0) player.Reply(lang.GetMessage(key, this, player.Id)); else player.Reply(string.Format(lang.GetMessage(key, this, player.Id), format)); } #endregion #region Harmony // Prevent unlocking minigame if they are admin cuffed [AutoPatch] [HarmonyPatch(typeof(Handcuffs), "SV_StartUnlockMiniGame")] public static class Handcuffs_SV_StartUnlockMiniGame_Patch { public static bool Prefix(Handcuffs __instance, BasePlayer player) { if (ins == null) return true; if (ins.cuffedPlayers.Contains(player.userID.Get())) { __instance.InterruptUnlockMiniGame(true); return false; } return true; } } // Prevent others unlocking admin cuffs :^) [AutoPatch] [HarmonyPatch(typeof(Handcuffs), nameof(Handcuffs.UnlockAndReturnToPlayer))] public static class Handcuffs_UnlockAndReturnToPlayer_Patch { public static bool Prefix(Handcuffs __instance, BasePlayer returnToPlayer) { if (ins == null) return true; if (__instance.GetOwnerPlayer() == null) return true; if (ins.cuffedPlayers.Contains(__instance.GetOwnerPlayer().userID.Get())) { ins.SendLangMessage(returnToPlayer.IPlayer, "ErrorCuffedByAdmin"); return false; } return true; } } [AutoPatch] [HarmonyPatch(typeof(BasePlayer), nameof(BasePlayer.BecomeWounded))] public static class BasePlayer_BecomeWounded_Patch { public static void Postfix(BasePlayer __instance) { if (ins == null) return; if (ins.cuffedPlayers.Contains(__instance.userID.Get())) { var item = GetAdminHandcuff(__instance); if (item != null) item.Remove(); } } } #endregion } }