C#:
/*
TODO:
- Add option to hide as a deployed entity of choice
- Add command cooldown option
- Add daily limit option
- Add option to now show other vanished for mods?
- Add AppearWhileRunning option (player.IsRunning())
- Add AppearWhenDamaged option (player.IsWounded())
- Add options for where to position status indicator
- Add restoring after reconnection (datafile/static dictionary)
- Fix 'CanUseWeapon' only visually hiding item; find a better way
- Fix CUI overlay overlapping HUD elements/inventory (if possible)
- Fix player becoming visible when switching weapons? (need to verify)
- Prevent animals/NPCs from detecting player while vanished
*/
using System;
using System.Collections.Generic;
using System.Linq;
using Network;
using Oxide.Core.Libraries.Covalence;
using Oxide.Game.Rust.Cui;
namespace Oxide.Plugins
{
[Info("Vanish", "", "0.3.4", ResourceId = 1420)]
[Description("Allows players with permission to become truly invisible.")]
class Vanish : RustPlugin
{
// Do NOT edit this file, instead edit Vanish.json in oxide/config and Vanish.en.json in the oxide/lang directory,
// or create a new language file for another language using the 'en' file as a default.#region Initialization
const string permVanish = "vanish.allowed";
void Init()
{
#if !RUST
throw new NotSupportedException("This plugin does not support this game");
#endif
LoadDefaultConfig();
LoadDefaultMessages();
Unsubscribe(nameof(CanNetworkTo));
permission.RegisterPermission(permVanish, this);
cmd.AddChatCommand(command, this, "VanishChatCmd");
cmd.AddConsoleCommand($"global.{command}", this, "VanishConsoleCmd");
}
#endregion
#region Configuration
bool canBeHurt;
bool canDamageBuilds;
bool canHurtAnimals;
bool canHurtPlayers;
bool canUseTeleport;
//bool canUseWeapons;
string command;
bool showEffect;
bool showIndicator;
bool showOverlay;
float vanishTimeout;
bool visibleToAdmin;
protected override void LoadDefaultConfig()
{
Config["CanBeHurt"] = canBeHurt = GetConfig("CanBeHurt", false);
Config["CanDamageBuilds"] = canDamageBuilds = GetConfig("CanDamageBuilds", true);
Config["CanHurtAnimals"] = canHurtAnimals = GetConfig("CanHurtAnimals", true);
Config["CanHurtPlayers"] = canHurtPlayers = GetConfig("CanHurtPlayers", true);
Config["CanUseTeleport"] = canUseTeleport = GetConfig("CanUseTeleport", true);
//Config["CanUseWeapons"] = canUseWeapons = GetConfig("CanUseWeapons", true);
Config["Command"] = command = GetConfig("Command", "vanish");
Config["ShowEffect"] = showEffect = GetConfig("ShowEffect", true);
Config["ShowIndicator"] = showIndicator = GetConfig("ShowIndicator", true);
Config["ShowOverlay"] = showOverlay = GetConfig("ShowOverlay", false);
Config["VanishTimeout"] = vanishTimeout = GetConfig("VanishTimeout", 0f);
Config["VisibleToAdmin"] = visibleToAdmin = GetConfig("VisibleToAdmin", true);
SaveConfig();
}
#endregion
#region Localization
void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
{"CantDamageBuilds", "Ты не можешь ломать строения в ванише."},
{"CantHurtAnimals", "Ты не можешь наносить урон животным в ванише."},
{"CantHurtPlayers", "Ты не можешь наносить урон игрокам в ванише."},
{"CantUseTeleport", "Ты не можешь телепортироваться в ванише."},
{"NoPermission", "Нет прав."},
{"VanishDisabled", "Ты стал видимым."},
{"VanishEnabled", "Ты стал невидимым."},
{"VanishTimedOut", "Тайм-аут Vanish достигнут!"}
}, this);
}
#endregion
#region Data Storage
class OnlinePlayer
{
public BasePlayer Player;
public bool IsInvisible;
}
[OnlinePlayers] Hash<BasePlayer, OnlinePlayer> onlinePlayers = new Hash<BasePlayer, OnlinePlayer>();
#endregion
#region Chat/Console Commands
void VanishChatCmd(BasePlayer player)
{
if (!IsAllowed(player.UserIDString, permVanish))
{
PrintToChat(player, Lang("NoPermission", player.UserIDString));
return;
}
// Vanishing visual effect
if (showEffect) Effect.server.Run("assets/prefabs/npc/patrol helicopter/effects/rocket_fire.prefab", player.transform.position);
// Remove player/held item from view
if (IsInvisible(player))
{
Reappear(player);
return;
}
Disappear(player);
}
void VanishConsoleCmd(ConsoleSystem.Arg arg)
{
var player = arg.connection?.player as BasePlayer;
if (player != null) VanishChatCmd(player);
}
#endregion
#region Vanishing Act
void Disappear(BasePlayer player)
{
// Destroy player/item entities
var connections = new List<Connection>();
foreach (var basePlayer in BasePlayer.activePlayerList)
{
if (player == basePlayer || !basePlayer.IsConnected()) continue;
if (visibleToAdmin && IsAdmin(basePlayer)) continue;
connections.Add(basePlayer.net.connection);
}
if (Net.sv.write.Start()) // Try catch?
{
Net.sv.write.PacketID(Message.Type.EntityDestroy);
Net.sv.write.EntityID(player.net.ID);
Net.sv.write.UInt8((byte)BaseNetworkable.DestroyMode.None);
Net.sv.write.Send(new SendInfo(connections));
}
var item = player.GetActiveItem();
if (item?.GetHeldEntity() != null && Net.sv.write.Start())
{
Net.sv.write.PacketID(Message.Type.EntityDestroy);
Net.sv.write.EntityID(item.GetHeldEntity().net.ID);
Net.sv.write.UInt8((byte)BaseNetworkable.DestroyMode.None);
Net.sv.write.Send(new SendInfo(connections));
}
// Add overlay effect if enabled
if (showOverlay || showIndicator) VanishGui(player);
// Set max vanish time if enabled
if (vanishTimeout > 0f) timer.Once(vanishTimeout, () =>
{
if (!onlinePlayers[player].IsInvisible) return;
PrintToChat(player, Lang("VanishTimedOut", player.UserIDString));
Reappear(player);
});
// Save and notify
PrintToChat(player, Lang("VanishEnabled", player.UserIDString));
onlinePlayers[player].IsInvisible = true;
Subscribe(nameof(CanNetworkTo));
}
object CanNetworkTo(BaseNetworkable entity, BasePlayer target)
{
var player = entity as BasePlayer ?? (entity as HeldEntity)?.GetOwnerPlayer();
if (player == null || target == null || player == target) return null;
if (visibleToAdmin && IsAdmin(target)) return null;
// Hide from other players
if (IsInvisible(player)) return false;
return null;
}
object CanBeTargeted(BaseCombatEntity entity)
{
// Hide from helis/turrets
var player = entity as BasePlayer;
if (player != null && IsInvisible(player)) return false;
return null;
}
/*void OnPlayerSleepEnded(BasePlayer player)
{
// Notify if still vanished
if (IsInvisible(player)) Disappear(player);
}*/
#endregion
#region Reappearing Act
void Reappear(BasePlayer player)
{
// Make player visible
onlinePlayers[player].IsInvisible = false;
player.SendNetworkUpdate();
// Restore held item visibility
player.GetActiveItem()?.GetHeldEntity()?.SendNetworkUpdate();
// Destroy existing UI
string gui;
if (guiInfo.TryGetValue(player.userID, out gui)) CuiHelper.DestroyUi(player, gui);
PrintToChat(player, Lang("VanishDisabled", player.UserIDString));
if (onlinePlayers.Values.Count(p => p.IsInvisible) <= 0) Unsubscribe(nameof(CanNetworkTo));
}
#endregion
#region GUI Indicator/Overlay
Dictionary<ulong, string> guiInfo = new Dictionary<ulong, string>();
void VanishGui(BasePlayer player)
{
// Destroy existing UI
string gui;
if (guiInfo.TryGetValue(player.userID, out gui)) CuiHelper.DestroyUi(player, gui);
var elements = new CuiElementContainer();
guiInfo[player.userID] = CuiHelper.GetGuid();
// Show GUI indicator if enabled
if (showIndicator)
{
elements.Add(new CuiElement
{
Name = guiInfo[player.userID],
Components =
{
new CuiRawImageComponent { Color = "1 1 1 0.3", Url = "http://i.imgur.com/Gr5G3YI.png" },
new CuiRectTransformComponent { AnchorMin = "0.175 0.017", AnchorMax = "0.22 0.08" }
}
});
}
// Show GUI overlay if enabled
if (showOverlay)
{
elements.Add(new CuiElement
{
Name = guiInfo[player.userID],
Components =
{
new CuiRawImageComponent { Sprite = "assets/content/ui/overlay_freezing.png" },
new CuiRectTransformComponent { AnchorMin = "0 0", AnchorMax = "1 1" }
}
});
}
// Create the UI elements
CuiHelper.AddUi(player, elements);
}
#endregion
#region Damage Blocking
object OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
{
var player = (info?.Initiator as BasePlayer) ?? entity as BasePlayer;
if (player == null || !player.IsConnected() || !onlinePlayers[player].IsInvisible) return null;
// Block damage to animals
if (entity is BaseNPC)
{
if (canHurtAnimals) return null;
PrintToChat(player, Lang("CantHurtAnimals", player.UserIDString));
return true;
}
// Block damage to builds
if (!(entity is BasePlayer))
{
if (canDamageBuilds) return null;
PrintToChat(player, Lang("CantDamageBuilds", player.UserIDString));
return true;
}
// Block damage to players
if (info?.Initiator is BasePlayer)
{
if (canHurtPlayers) return null;
PrintToChat(player, Lang("CantHurtPlayers", player.UserIDString));
return true;
}
// Block damage to self
if (!canBeHurt) return true;
return null;
}
#endregion
#region Weapon Blocking
/*void OnPlayerTick(BasePlayer player)
{
// Hide active item
if (onlinePlayers[player].IsInvisible && !CanUseWeapons && player.GetActiveItem() != null)
{
var heldEntity = player.GetActiveItem().GetHeldEntity() as HeldEntity;
heldEntity?.SetHeld(false);
}
}*/
#endregion
#region Teleport Blocking
object CanTeleport(BasePlayer player)
{
if (onlinePlayers[player] == null) return null;
return onlinePlayers[player].IsInvisible && !canUseTeleport ? Lang("CantUseTeleport", player.UserIDString) : null;
}
#endregion
#region Cleanup
void Unload()
{
foreach (var player in BasePlayer.activePlayerList)
{
// Destroy existing UI
string gui;
if (guiInfo.TryGetValue(player.userID, out gui)) CuiHelper.DestroyUi(player, gui);
}
}
#endregion
#region Helpers
T GetConfig<T>(string name, T defaultValue) => Config[name] == null ? defaultValue : (T)Convert.ChangeType(Config[name], typeof(T));
string Lang(string key, string id = null, params object[] args) => string.Format(lang.GetMessage(key, this, id), args);
static void Reply(IPlayer player, string message, params object[] args) => player.Reply(string.Format(message, args));
bool IsAdmin(BasePlayer player) => permission.UserHasGroup(player.UserIDString, "admin") || player.net?.connection?.authLevel > 0;
bool IsAllowed(string id, string perm) => permission.UserHasPermission(id, perm);
bool IsInvisible(BasePlayer player) => onlinePlayers[player]?.IsInvisible ?? false;
#endregion
}
}