/*▄▄▄ ███▄ ▄███▓ ▄████ ▄▄▄██▀▀▀▓█████▄▄▄█████▓ ▓█████▄ ▓██▒▀█▀ ██▒ ██▒ ▀█▒ ▒██ ▓█ ▀▓ ██▒ ▓▒ ▒██▒ ▄██▓██ ▓██░▒██░▄▄▄░ ░██ ▒███ ▒ ▓██░ ▒░ ▒██░█▀ ▒██ ▒██ ░▓█ ██▓▓██▄██▓ ▒▓█ ▄░ ▓██▓ ░ ░▓█ ▀█▓▒██▒ ░██▒░▒▓███▀▒ ▓███▒ ░▒████▒ ▒██▒ ░ ░▒▓███▀▒░ ▒░ ░ ░ ░▒ ▒ ▒▓▒▒░ ░░ ▒░ ░ ▒ ░░ ▒░▒ ░ ░ ░ ░ ░ ░ ▒ ░▒░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░*/ using Oxide.Game.Rust.Cui; using ProtoBuf; using Newtonsoft.Json; using System.Collections.Generic; using System.Linq; using UnityEngine; using Oxide.Core.Plugins; namespace Oxide.Plugins { [Info("Personal Vault Door", "bmgjet", "1.0.6")] [Description("Lets you place a vault door")] public class PersonalVaultDoor : RustPlugin { [PluginReference] private Plugin ServerRewards, Economics; #region Configuration private Configuration config; private class Configuration { [JsonProperty("Vault Max Health")] public float VaultMaxHealth = 2000; [JsonProperty("Show Health % Bar CUI")] public bool HealthBarEnabled = false; [JsonProperty(PropertyName = "Range to show health")] public float vaultrange = 3f; [JsonProperty("Slide Door Settings")] public SlideSettings SlideDoor = new SlideSettings(); public class SlideSettings { [JsonProperty("Enable Slide Mode (false = classic swing)")] public bool Enabled = false; [JsonProperty("Slide Distance (meters)")] public float Distance = 2.0f; [JsonProperty("Slide Duration (seconds)")] public float Duration = 3.5f; [JsonProperty("Auto Close Delay (seconds, 0 = disabled)")] public float AutoCloseDelay = 0f; } [JsonProperty("Health Bar Settings")] public HealthBarSettings HealthBar = new HealthBarSettings(); public class HealthBarSettings { [JsonProperty("Healthy Color (health >= 60%)")] public string HighColor = "#4CAF50"; [JsonProperty("Damaged Color (health 25-60%)")] public string MidColor = "#FFC107"; [JsonProperty("Critical Color (health < 25%)")] public string LowColor = "#F44336"; [JsonProperty("Damage Flash Color")] public string FlashColor = "#FF5252"; [JsonProperty("Damage Flash Duration (seconds)")] public float FlashDuration = 0.3f; } [JsonProperty("Repair Delay")] public float RepairDelay = 60; [JsonProperty("Repair Ammount")] public int RepairAmmount = 25; [JsonProperty("Spawn Cost type (serverrewards,economics,resources)")] public string Costtype = "resources"; [JsonProperty("Repair Cost type (serverrewards,economics,resources)")] public string RepairCosttype = "resources"; [JsonProperty("Spawn Cost for (serverrewards,economics)")] public float Spawncost = 2000; [JsonProperty("Repair Cost for (serverrewards,economics)")] public float Repaircost = 50; [JsonProperty("Currency Symbol for (serverrewards,economics)")] public string CurrencySymbol = "$"; [JsonProperty("Charge to craft")] public bool craftcosts = true; [JsonProperty("Repair Cost")] //Items and quanity needed to repair public Dictionary RepairCost = new Dictionary { {"scrap", 1}, {"metal.fragments", 100}, {"metal.refined", 10}, }; [JsonProperty("Craft Cost")] //Items and quanity needed per craft public Dictionary CraftCost = new Dictionary { {"scrap", 10}, {"metal.fragments", 1000}, {"metal.refined", 100}, }; public string ToJson() => JsonConvert.SerializeObject(this); public Dictionary ToDictionary() => JsonConvert.DeserializeObject>(ToJson()); } protected override void LoadDefaultConfig() => config = new Configuration(); protected override void LoadConfig() { base.LoadConfig(); try { config = Config.ReadObject(); if (config == null) { throw new JsonException(); } if (!config.ToDictionary().Keys.SequenceEqual(Config.ToDictionary(x => x.Key, x => x.Value).Keys)) { PrintWarning("Configuration appears to be outdated; updating and saving"); SaveConfig(); } } catch { PrintWarning($"Configuration file {Name}.json is invalid; using defaults"); LoadDefaultConfig(); } } protected override void SaveConfig() { PrintWarning($"Configuration changes saved to {Name}.json"); Config.WriteObject(config, true); } #endregion Configuration #region Vars //Offset from door slot position to move lock. Vector3 codeoffset = new Vector3(0.71f, -0.1f, -0.3f); //Puts it on flat pannel, On handles was too far for it to trigger on door still. //Skin of Icon private const ulong skinID = 2643584466; //Replacement prefab private const string prefab = "assets/bundled/prefabs/modding/asset_store/bankheist_package/bankheist_vol03/prefabs/door.vault.static.prefab"; //Permission private const string permUse = "PersonalVaultDoor.use"; //Show Debug Info private bool showDebug = false; //Sound Effects to play on vault break. static List effects = new List { "assets/bundled/prefabs/fx/entities/loot_barrel/gib.prefab", "assets/bundled/prefabs/fx/building/metal_sheet_gib.prefab" }; private List REPlaced = new List(); private static PersonalVaultDoor plugin; #endregion #region Language protected override void LoadDefaultMessages() { lang.RegisterMessages(new Dictionary { {"Name", "Vault Door"}, {"Pickup", "You picked up Vault Door!"}, {"Receive", "You received Vault Door!"}, {"Repair", "You need more resources:\n{0}"}, {"Wait", "You must wait: {0} before you can repair."}, {"Permission", "You need permission to do that!"} }, this); } private static string HexToRustFormat(string hex) { Color color; return ColorUtility.TryParseHtmlString(hex, out color) ? $"{color.r:F2} {color.g:F2} {color.b:F2} {color.a:F2}" : "false"; } private static string HexToRustFormat(string hex, float alpha) { Color color; return ColorUtility.TryParseHtmlString(hex, out color) ? $"{color.r:F2} {color.g:F2} {color.b:F2} {alpha:F2}" : "false"; } //Send player message private void message(BasePlayer player, string key, params object[] args) { if (player == null) { return; } var message = string.Format(lang.GetMessage(key, this, player.UserIDString), args); player.ChatMessage(message); } #endregion #region Oxide Hooks private void OnServerInitialized(bool initial) { plugin = this; Fstartup(initial ? 30 : 1); } private void Fstartup(int delay) { //Wait to start up vault door componant for slow servers. timer.Once(delay, () => { CheckVaultDoor(); }); } private void Init() { //Setup Permissions permission.RegisterPermission(permUse, this); } private void Unload() { //Clear Vault Door Health CUI foreach (BasePlayer player in BasePlayer.activePlayerList.ToArray()) { CuiHelper.DestroyUi(player, "VaultHealthUI"); } //Snap any mid-slide vault doors back to closed before the component is destroyed foreach (var slider in VaultSlide.ActiveSliders.ToArray()) { slider?.ResetToClosed(); } int VDS = DestroyVaultDoorScript(); if (showDebug) Puts("Destroyed " + VDS.ToString() + " VaultDoor Scripts"); //Unload Statics effects = null; plugin = null; } void OnEntityKill(Door entity) { //Check if vault door if (entity.ShortPrefabName == "door.vault.static") { VaultDoor VD = entity.GetComponent(); if (VD == null) return; //Remove Door Frame NextTick(() => { if (VD.Frame != null) { VD.Frame.Kill(); } }); if (VD.PlayersCUIed == null) return; //Remove CUI foreach (BasePlayer player in VD.PlayersCUIed) { if (player != null) { CuiHelper.DestroyUi(player, "VaultHealthUI"); } } UnityEngine.Object.DestroyImmediate(VD); } } void OnEntitySpawned(CodeLock cl) { BaseEntity Door = cl.GetParentEntity(); if (Door != null && Door.ToString().Contains("door.vault.static")) { //Moves codelock and update cl.transform.localPosition += codeoffset; cl.SendNetworkUpdateImmediate(); } } void OnEntitySpawned(KeyLock cl) { BaseEntity Door = cl.GetParentEntity(); if (Door != null && Door.ToString().Contains("door.vault.static")) { //Moves codelock and update cl.transform.localPosition += codeoffset; cl.SendNetworkUpdateImmediate(); } } //Code and KeyLock position Fix void OnEntitySpawned(Door cl) { if (Rust.Application.isLoading) { return; } //Add component in spawn here so Copypaste can Work with vaultdoors. if (cl is Door && cl.ToString().Contains("door.vault.static")) { if (cl.GetComponent() == null) { if (REPlaced.Contains(cl.transform.position)) { if (showDebug) Puts("Skipping Rust Edit Placed Vault Door"); return; } if (showDebug) Puts("Adding VaultDoor Component"); //Delay since copypaste might not of spawn door frame yet. timer.Once(2f, () => { try { cl.gameObject.AddComponent(); if (config.SlideDoor.Enabled && cl.GetComponent() == null) { cl.gameObject.AddComponent(); } } catch { }; }); } } } //Hook vault placement to switch in. private void OnEntityBuilt(Planner plan, GameObject go) { CheckDeploy(go.ToBaseEntity()); } //Hooks if should pickup private void OnHammerHit(BasePlayer player, HitInfo info) { CheckHit(player, info?.HitEntity); } //Flashes the health bar red when the vault door takes damage private void OnEntityTakeDamage(Door entity, HitInfo info) { if (entity == null || !IsVaultDoor(entity.skinID)) { return; } entity.GetComponent()?.FlashDamage(); } //Slide Mode: replaces the vault door's native swing with a sliding animation private void OnDoorOpened(Door door, BasePlayer player) { if (!config.SlideDoor.Enabled || door == null || !IsVaultDoor(door.skinID)) { return; } VaultSlide slider = door.GetComponent(); if (slider == null) { return; } //Cancel the native open flag/animation - the slider tracks open/closed itself door.SetOpen(false); slider.Toggle(); } #endregion #region Core List FindVaultDoors(Vector3 pos, float radius) { //Casts a sphere at given position and find all doors there var hits = Physics.SphereCastAll(pos, radius, Vector3.one); var x = new List(); foreach (var hit in hits) { var entity = hit.GetEntity()?.GetComponent(); if (entity && !x.Contains(entity)) x.Add(entity); } return x; } void DestroyGroundComp(BaseEntity ent) { UnityEngine.Object.DestroyImmediate(ent.GetComponent()); UnityEngine.Object.DestroyImmediate(ent.GetComponent()); //Stops Decay UnityEngine.Object.DestroyImmediate(ent.GetComponent()); } void DestroyMeshCollider(BaseEntity ent) { foreach (var mesh in ent.GetComponentsInChildren()) { UnityEngine.Object.DestroyImmediate(mesh); } } //Resets the component after server restart private void CheckVaultDoor() { //Build list of VaultDoors In Map File for (int i = World.Serialization.world.prefabs.Count - 1; i >= 0; i--) { PrefabData prefabdata = World.Serialization.world.prefabs[i]; if (prefabdata.id == 3595032872) { REPlaced.Add(prefabdata.position); //Check its still there and fix if not if (FindVaultDoors(prefabdata.position, 4f).Count == 0) { Door replacement = GameManager.server.CreateEntity(StringPool.Get(prefabdata.id), prefabdata.position, prefabdata.rotation) as Door; if (replacement == null) return; DestroyGroundComp(replacement); DestroyMeshCollider(replacement); replacement.Spawn(); replacement.transform.position = prefabdata.position; replacement.transform.rotation = prefabdata.rotation; replacement.pickup.enabled = false; replacement.SendNetworkUpdateImmediate(); } } } if (showDebug) Puts("Founded " + REPlaced.Count.ToString() + " Map Placed Vault Doors"); int VaultsUpdated = 0; foreach (var vaultdoor in GameObject.FindObjectsOfType()) { //Skip Servers Vault Doors if (REPlaced.Contains(vaultdoor.transform.position)) { if (showDebug) Puts("Skipping Map Placed Vault Door @ " + vaultdoor.transform.position.ToString()); continue; } if (vaultdoor.ShortPrefabName == "door.vault.static" && vaultdoor.GetComponent() == null) { if (showDebug) Puts("Found vaultdoor " + vaultdoor.ToString() + " " + vaultdoor.OwnerID.ToString() + " Adding Component"); vaultdoor.gameObject.AddComponent(); if (config.SlideDoor.Enabled && vaultdoor.GetComponent() == null) { vaultdoor.gameObject.AddComponent(); } VaultsUpdated++; } } Puts("Updated " + VaultsUpdated.ToString() + " VaultDoors"); } //Gives player vault door private void GiveVaultDoor(BasePlayer player, bool pickup = false) { var item = CreateItem(); if (item != null && player != null) { player.GiveItem(item); message(player, pickup ? "Pickup" : "Receive"); } } bool ChargePlayer(BasePlayer player) { object result = null; if (!config.craftcosts) { return true; } else { if (config.Costtype == "serverrewards" && ServerRewards != null) { result = ServerRewards.Call("TakePoints", player.UserIDString, (int)config.Spawncost); } else if (config.Costtype == "economics" && Economics != null) { result = Economics.Call("Withdraw", player.UserIDString, (double)config.Spawncost); } else { // No supported rewards plugin loaded or configured message(player, "Currency Type Not supported on this server"); return false; } if (result == null || (result is bool && (bool)result == false)) { message(player, "Looks like you can not afford to buy this"); return false; } message(player, "Charged {amount} {currency} for Vault Door".Replace("{amount}", config.Spawncost.ToString()).Replace("{currency}", config.CurrencySymbol)); return true; } } bool CanaffordFix(BasePlayer player, Door VD) { object result = null; if (VD.SecondsSinceAttacked < config.RepairDelay) { message(player, "Wait", (config.RepairDelay - VD.SecondsSinceAttacked).ToString("#.#")); return false; } else { if (config.RepairCosttype == "serverrewards" && ServerRewards != null) { result = ServerRewards.Call("TakePoints", player.UserIDString, (int)config.Repaircost); } else if (config.RepairCosttype == "economics" && Economics != null) { result = Economics.Call("Withdraw", player.UserIDString, (double)config.Repaircost); } else { // No supported rewards plugin loaded or configured message(player, "Currency Type Not supported on this server"); return false; } if (result == null || (result is bool && (bool)result == false)) { message(player, "Looks like you can not afford to repair this"); return false; } message(player, "Charged {amount} {currency} for repairing Vault Door".Replace("{amount}", config.Repaircost.ToString()).Replace("{currency}", config.CurrencySymbol)); return true; } } //Checks if has the correct permission private bool CanCraft(BasePlayer player) { if (!permission.UserHasPermission(player.UserIDString, permUse)) { message(player, "Permission"); return false; } return CanFix(player, null, true); } //Creates vault door private Item CreateItem() { var item = ItemManager.CreateByName("wall.frame.garagedoor", 1, skinID); if (item != null) { item.text = "Vault Door"; item.name = item.text; } return item; } //Checks if its a vault door when hit with hammer private void CheckHit(BasePlayer player, BaseEntity entity) { if (entity == null) { return; } if (!IsVaultDoor(entity.skinID)) { return; } //Check if door is open and has no lock to remove Otherwise heal door Door VD = entity as Door; VaultSlide slider = entity.GetComponent(); bool isOpen = slider != null ? slider.IsOpen : VD.IsOpen(); if (VD.GetSlot(0) == null && isOpen) { entity.GetComponent()?.TryPickup(player); } else { if (VD._health < VD._maxHealth) { if (config.RepairCosttype != "resources") { if (CanaffordFix(player, VD)) { Effect.server.Run("assets/bundled/prefabs/fx/build/repair_full_metal.prefab", VD.transform.position); VD.health += config.RepairAmmount; } } else { if (CanFix(player, VD)) { Effect.server.Run("assets/bundled/prefabs/fx/build/repair_full_metal.prefab", VD.transform.position); VD.health += config.RepairAmmount; } } } else { message(player, "Vault Door Is full health"); } } } //Checks if can fix private bool CanFix(BasePlayer player, Door VD, bool craft = false) { Dictionary Needed = new Dictionary(); Dictionary Parts = new Dictionary(); if (!craft) { Parts = config.RepairCost; //Check repair delay if (VD.SecondsSinceAttacked < config.RepairDelay) { message(player, "Wait", (config.RepairDelay - VD.SecondsSinceAttacked).ToString("#.#")); return false; } } else { if (!config.craftcosts) return true; Parts = config.CraftCost; } //Check has needed meterials foreach (var component in Parts) { string name = component.Key; if (player.inventory.GetAmount(ItemManager.FindItemDefinition(component.Key).itemid) < component.Value) { if (!Needed.ContainsKey(name)) { Needed.Add(name, 0); } Needed[name] += component.Value; } } //Has everything needed so remove from player and send can fix. if (Needed.Count == 0) { foreach (var item in Parts) { player.inventory.Take(null, ItemManager.FindItemDefinition(item.Key).itemid, item.Value); } return true; } //Doesnt have everything needed to build list and message use. Send cant fix else { string text = ""; foreach (var item in Needed) { text += $" * {item.Key} x{item.Value}\n"; } message(player, "Repair", text); return false; } } private bool IsVaultDoor(ulong skin) { return skin != 0 && skin == skinID; } //Checks if vault door should be swapped in private void CheckDeploy(BaseEntity entity) { if (entity == null) { return; } //Checks if is using vault door skin if (!IsVaultDoor(entity.skinID)) { return; } //Creates Doorway var doorway = GameManager.server.CreateEntity("assets/prefabs/building core/wall.doorway/wall.doorway.prefab", entity.transform.position, entity.transform.rotation); if (doorway == null) { //Something Failed return; } doorway.Spawn(); doorway.OwnerID = entity.OwnerID; //sets up door frame to fill in gaps around vault door var buildingBlock = doorway as BuildingBlock; if (buildingBlock != null) { //Upgrade HQM buildingBlock.SetGrade((BuildingGrade.Enum)4); //Grounded so doesnt fall apart buildingBlock.grounded = false; //Sets health as max buildingBlock.health = buildingBlock.MaxHealth(); //Rotate from Door Way Vector3 rot = buildingBlock.transform.rotation.eulerAngles; rot = new Vector3(rot.x, rot.y + 180, rot.z); //Create Vault Door Door vaultdoor = GameManager.server.CreateEntity(prefab, buildingBlock.transform.position, buildingBlock.transform.rotation) as Door; if (vaultdoor == null) { return; } //Stupid rotation based move stuff Vector3 movepos = buildingBlock.transform.position; movepos += buildingBlock.transform.forward * 1.0f; movepos += buildingBlock.transform.right * -0.54f; movepos += buildingBlock.transform.up * 0.2f; //Sets door way as creator so can destory vault on door way being destroyed vaultdoor.creatorEntity = doorway; vaultdoor.transform.rotation = Quaternion.Euler(rot); vaultdoor.transform.position = movepos; //Set skin and owner vaultdoor.skinID = skinID; vaultdoor.OwnerID = entity.OwnerID; //Delay setting Max health and health other wise it defaults to 800 timer.Once(1f, () => { vaultdoor.SetMaxHealth(config.VaultMaxHealth); vaultdoor.SetHealth(config.VaultMaxHealth); if (config.SlideDoor.Enabled && vaultdoor.GetComponent() == null) { vaultdoor.gameObject.AddComponent(); } }); //Sets up functions vaultdoor.Spawn(); vaultdoor.SendNetworkUpdateImmediate(); } //Cleans out placeholder NextTick(() => { entity?.Kill(); }); } //Removes script on unload incase some ones restarting plugin but not restarted server int DestroyVaultDoorScript() { int killed = 0; foreach (var vd in VaultDoor.ActiveVaultDoors.ToArray()) { if (vd != null) { UnityEngine.Object.DestroyImmediate(vd); killed++; } } foreach (var vs in VaultSlide.ActiveSliders.ToArray()) { if (vs != null) { UnityEngine.Object.DestroyImmediate(vs); killed++; } } return killed; } #endregion #region Commands //Chat command [ChatCommand("vaultdoor")] private void Craft(BasePlayer player) { if (config.Costtype != "resources") { if (ChargePlayer(player)) { GiveVaultDoor(player); } } else { if (CanCraft(player)) { GiveVaultDoor(player); } } } //Console command [ConsoleCommand("vaultdoor.give")] private void Cmd(ConsoleSystem.Arg arg) { if (arg.IsAdmin && arg.Args?.Length > 0) { var player = BasePlayer.Find(arg.Args[0].ToString()) ?? BasePlayer.FindSleeping(arg.Args[0].ToString()); if (player == null) { PrintWarning($"Can't find player with that name/ID! {arg.Args[0]}"); return; } GiveVaultDoor(player); } } #endregion #region Scripts public class VaultDoor : MonoBehaviour { //Tracked so DestroyVaultDoorScript doesn't need to scan every Door on the map public static List ActiveVaultDoors = new List(); //Hold doors info public Door vdoor = null; //Holds Position that can be used after Vdoors destroyed. public Vector3 Position; //Hold door frames info public BaseEntity Frame = null; //List of players that have CUI active public List PlayersCUIed = new List(); private void Awake() { //Setup vdoor = this.GetComponent(); if (vdoor == null) return; ActiveVaultDoors.Add(this); Position = vdoor.transform.position; //Set Custom Health vdoor.SetMaxHealth(plugin.config.VaultMaxHealth); vdoor.SetHealth(plugin.config.VaultMaxHealth); //Safety net: make sure the slider is attached if slide mode is on if (plugin.config.SlideDoor.Enabled && vdoor.GetComponent() == null) { vdoor.gameObject.AddComponent(); } Frame = vdoor.creatorEntity; //If no creatorEntity most likey a server restart so find it. if (Frame == null) { //Offsets scan position since where vault shows isnt where its position is. Vector3 scanpos = Position; scanpos += vdoor.transform.forward * 1.0f; scanpos += vdoor.transform.right * -0.54f; scanpos += vdoor.transform.up * 0.0f; if (plugin.showDebug) foreach (BasePlayer BP in BasePlayer.activePlayerList) { if (BP.IsAdmin) BP.SendConsoleCommand("ddraw.sphere", 8f, Color.red, scanpos, 0.5f); } List BuildingBlock = new List(); //Scans area for players Vis.Entities(scanpos, 0.5f, BuildingBlock); foreach (BaseEntity doorframe in BuildingBlock) { if (doorframe.ShortPrefabName == "wall.doorway") { if (plugin.showDebug) plugin.Puts("Found Creator Doorway " + doorframe.ToString()); Frame = doorframe; break; } } } //Setup checking if door frame has been destroyed InvokeRepeating("CheckFrame", 5, 8); if (plugin.config.HealthBarEnabled) { InvokeRepeating("VaultHealthBar", 1, 1); } else { plugin.Unsubscribe(nameof(OnEntityTakeDamage)); } } private void OnDestroy() { ActiveVaultDoors.Remove(this); } void VaultHealthBar() { List PlayersInRange = new List(); //Scans area for players Vis.Entities(vdoor.transform.position, plugin.config.vaultrange, PlayersInRange); //Logic to remove CUI from players that have left area foreach (BasePlayer bp in PlayersCUIed.ToArray()) { if (!PlayersInRange.Contains(bp)) { CuiHelper.DestroyUi(bp, "VaultHealthUI"); PlayersCUIed.Remove(bp); } } //Shows CUI to each player in range if (PlayersInRange.Count == 0) { return; } float pct = Mathf.Clamp01(vdoor._maxHealth > 0 ? vdoor._health / vdoor._maxHealth : 0f); var barsettings = plugin.config.HealthBar; string barColor = HexToRustFormat(pct >= 0.6f ? barsettings.HighColor : pct >= 0.25f ? barsettings.MidColor : barsettings.LowColor); string pctText = Mathf.RoundToInt(pct * 100f) + "%"; foreach (BasePlayer player in PlayersInRange.ToArray()) { if (player.IsSleeping()) { continue; } CuiHelper.DestroyUi(player, "VaultHealthUI"); var elements = new CuiElementContainer(); //Backing panel elements.Add(new CuiPanel { Image = { Color = "0.05 0.05 0.05 0.55" }, RectTransform = { AnchorMin = "0.79 0.93", AnchorMax = "0.995 0.975" } }, "Overlay", "VaultHealthUI"); //Title elements.Add(new CuiLabel { Text = { Text = "VAULT DOOR", FontSize = 11, Align = TextAnchor.MiddleLeft, Color = "0.85 0.85 0.85 0.9" }, RectTransform = { AnchorMin = "0.04 0.5", AnchorMax = "0.45 1" } }, "VaultHealthUI"); //Bar background elements.Add(new CuiPanel { Image = { Color = "0.15 0.15 0.15 0.9" }, RectTransform = { AnchorMin = "0.04 0.12", AnchorMax = "0.96 0.46" } }, "VaultHealthUI", "VaultHealthUI_BarBG"); //Bar fill, proportional to remaining health, color shifts green -> yellow -> red elements.Add(new CuiPanel { Image = { Color = barColor }, RectTransform = { AnchorMin = "0 0", AnchorMax = $"{pct:F3} 1" } }, "VaultHealthUI_BarBG", "VaultHealthUI_BarFill"); //Percent readout on top of the bar elements.Add(new CuiLabel { Text = { Text = pctText, FontSize = 10, Align = TextAnchor.MiddleCenter, Color = "1 1 1 0.95" }, RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" } }, "VaultHealthUI_BarBG"); CuiHelper.AddUi(player, elements); if (!PlayersCUIed.Contains(player)) { PlayersCUIed.Add(player); } } } //Realistic damage indicator: a brief red flash over the health bar every time the door is hit public void FlashDamage() { if (PlayersCUIed.Count == 0) { return; } var barsettings = plugin.config.HealthBar; foreach (BasePlayer player in PlayersCUIed.ToArray()) { if (player == null) { continue; } CuiHelper.DestroyUi(player, "VaultHealthUI_Flash"); var elements = new CuiElementContainer(); elements.Add(new CuiPanel { Image = { Color = HexToRustFormat(barsettings.FlashColor, 0.55f) }, RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" } }, "VaultHealthUI_BarBG", "VaultHealthUI_Flash"); CuiHelper.AddUi(player, elements); } plugin.timer.Once(barsettings.FlashDuration, () => { foreach (BasePlayer player in PlayersCUIed.ToArray()) { if (player != null) { CuiHelper.DestroyUi(player, "VaultHealthUI_Flash"); } } }); } void CheckFrame() { //Frame has been destoryed so destroy vault try { if (Frame == null && vdoor != null) { foreach (var effect in effects) { Effect.server.Run(effect, Position); } vdoor.Kill(); } } catch { } } public void TryPickup(BasePlayer player) { //Owner has hit with hammer, Destroy frame and vault door and refund them one. if (vdoor.OwnerID == player.userID) { vdoor.Kill(); if (Frame != null) { Frame.Kill(); } plugin.GiveVaultDoor(player, true); } } } //Slide Mode: moves the whole vault door sideways instead of using the native swing animation public class VaultSlide : MonoBehaviour { public static List ActiveSliders = new List(); private Door entity; private Transform tr; private Vector3 closedPosition; private Vector3 openPosition; private bool opening; private float elapsed; private float duration; public bool IsOpen { get { return tr != null && tr.position != closedPosition; } } private void Awake() { entity = GetComponent(); if (entity == null) { Destroy(this); return; } tr = entity.transform; closedPosition = tr.position; openPosition = closedPosition + (-tr.forward * plugin.config.SlideDoor.Distance); duration = Mathf.Max(0.1f, plugin.config.SlideDoor.Duration); enabled = false; ActiveSliders.Add(this); } private void OnDestroy() { ActiveSliders.Remove(this); } public void Toggle() { if (entity == null || entity.IsDestroyed) { return; } CancelInvoke(nameof(AutoClose)); elapsed = Mathf.Max(0f, duration - elapsed); opening = !opening; enabled = true; Effect.server.Run("assets/prefabs/building/door.hinged/effects/door-metal-open-end.prefab", tr.position); } private void Update() { elapsed += Time.deltaTime; float t = Mathf.InverseLerp(0f, duration, elapsed); tr.position = opening ? Vector3.Lerp(closedPosition, openPosition, t) : Vector3.Lerp(openPosition, closedPosition, t); tr.hasChanged = true; SendPositionUpdate(); if (elapsed >= duration) { elapsed = duration; enabled = false; if (opening && plugin.config.SlideDoor.AutoCloseDelay > 0f) { Invoke(nameof(AutoClose), plugin.config.SlideDoor.AutoCloseDelay); } } } private void AutoClose() { if (opening) { Toggle(); } } public void ResetToClosed() { CancelInvoke(nameof(AutoClose)); enabled = false; opening = false; elapsed = 0f; if (entity != null && !entity.IsDestroyed && tr != null) { tr.position = closedPosition; tr.hasChanged = true; SendPositionUpdate(); } } private void SendPositionUpdate() { if (entity == null || entity.net == null) { return; } entity.SendNetworkUpdateImmediate(); } } #endregion } }