C#:
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using UnityEngine;
using System.Collections.Generic;
namespace Oxide.Plugins
{
[Info("KillFeedChat", "BroShelbi", "1.0.0")]
[Description("Plugin for displaying player kills with item icons and distance calculation")]
public class KillFeedChat : RustPlugin
{
private const bool LanguageEn = true;
private HashSet<ulong> _disabledNotificationUsers;
#region Constants
private const string ON_MESSAGE_DEATHS = "ON_MESSAGE_DEATHS";
private const string OFF_MESSAGE_DEATHS = "OFF_MESSAGE_DEATHS";
private const string DISTANCE_PREFIX = "DISTANCE_PREFIX";
private const string KILL_MESSAGE = "KILL_MESSAGE";
private const string FOR_ATTAKER = "FOR_ATTAKER";
private const string FOR_VICTIM = "FOR_VICTIM";
private const string ATTAKER_NAME = "{0}";
private const string VICTIM_NAME = "{1}";
private const string WEAPON_NAME = "{2}";
private const string DISTANCE_NUMBER = "{3}";
#endregion
#region Configuration
private Configuration _configuration;
private class Configuration
{
[JsonProperty(LanguageEn ? "Plugin setup" : "Настройка плагина")]
public PluginSettings pluginSettings;
internal class PluginSettings
{
[JsonProperty(LanguageEn ? "Command to toggle kill display on and off" : "Команда для включения и выключения отображения убийств")]
public string UseCommand;
[JsonProperty(LanguageEn ? "Kill radius to display in chat for other players" : "Радиус от убийства для отображения в чате для остальных игроков")]
public float BroadCastRadius;
[JsonProperty(LanguageEn ? "Setting up display in chat" : "Настройка отображения в чате")]
public ChatSettings chatSettings;
[JsonProperty(LanguageEn ? "Nick color by privileges (Descending)" : "Цвет ника по привилегиям (По убыванию)")]
public Dictionary<string, NicknameColor> NicknameColorPermissions;
internal class ChatSettings
{
[JsonProperty(LanguageEn ? "Show weapon icons in chat? (If false, names will be shown)" : "Отображать иконки оружия в чате? (Если нет то будут отображаться названия)")]
public bool ShowIcons;
[JsonProperty(LanguageEn ? "Whether to display distance in prefix?" : "Отображать ли дистанцию в префиксе?")]
public bool ShowDistanceInPrefix;
[JsonProperty(LanguageEn ? "Prefix" : "Префикс")]
public string ChatPrefix;
[JsonProperty(LanguageEn ? "Custom chat avatar(Steam ID)" : "Кастомный аватар в чате(Steam ID)")]
public ulong ChatAvatar;
}
internal class NicknameColor
{
[JsonProperty(LanguageEn ? "Nick color if the player was killed (hex)" : "Цвет ника если игрока убили (hex)")]
public string ColorDeath;
[JsonProperty(LanguageEn ? "Nick color if the player killed (hex)" : "Цвет ника если игрок убил (hex)")]
public string ColorKill;
}
}
public static Configuration CreateDefaultConfiguration()
{
return new Configuration()
{
pluginSettings = new PluginSettings()
{
UseCommand = "kills",
BroadCastRadius = 200f,
chatSettings = new PluginSettings.ChatSettings()
{
ShowIcons = true,
ShowDistanceInPrefix = true,
ChatPrefix = "<color=#fc3e3e>[KillFeedChat]</color>",
ChatAvatar = 0
},
NicknameColorPermissions = new Dictionary<string, PluginSettings.NicknameColor>
{
{ "killfeedchat.king", new PluginSettings.NicknameColor { ColorDeath = "#ef1b70", ColorKill = "#ef1b70" } },
{ "killfeedchat.ultra", new PluginSettings.NicknameColor { ColorDeath = "#cf1bef", ColorKill = "#cf1bef" } },
{ "killfeedchat.premium", new PluginSettings.NicknameColor { ColorDeath = "#2adaf5", ColorKill = "#2adaf5" } },
{ "killfeedchat.vip", new PluginSettings.NicknameColor { ColorDeath = "#ffff99", ColorKill = "#ffff99" } },
{ "killfeedchat.default", new PluginSettings.NicknameColor { ColorDeath = "#aee6e6", ColorKill = "#aee6e6" } }
}
}
};
}
}
protected override void LoadDefaultConfig() => _configuration = Configuration.CreateDefaultConfiguration();
protected override void SaveConfig() => Config.WriteObject(_configuration);
protected override void LoadConfig()
{
base.LoadConfig();
try
{
_configuration = Config.ReadObject<Configuration>();
if (_configuration == null)
{
PrintWarning(LanguageEn ? $"Configuration not found. Create new configuration oxide/config/{nameof(KillFeedChat)}.json" : $"Конфигурации не найдено. Создаем новую конфигурацию oxide/config/{nameof(KillFeedChat)}.json");
LoadDefaultConfig();
PrintWarning(LanguageEn ? "Configuration created and saved successfully!" : "Конфигурации создана и успешна сохранена!");
NextTick(SaveConfig);
}
PrintWarning(LanguageEn ? "Configuration loaded successfully!" : "Конфигурации успешна загружена!");
}
catch
{
PrintWarning(LanguageEn ? $"Error read configuration 'oxide/config/{nameof(KillFeedChat)}', create a new configuration!!" : $"Ошибка чтения конфигурации 'oxide/config/{nameof(KillFeedChat)}', создаём новую конфигурацию!!");
LoadDefaultConfig();
NextTick(SaveConfig);
}
}
#endregion
#region Data
private void LoadData()
{
_disabledNotificationUsers = Interface.Oxide.DataFileSystem.ReadObject<HashSet<ulong>>($"{nameof(KillFeedChat)}") ?? new HashSet<ulong>();
}
private void SaveData()
{
Interface.Oxide.DataFileSystem.WriteObject($"{nameof(KillFeedChat)}", _disabledNotificationUsers);
}
#endregion
#region ChatCommand
private void ToggleKillFeed(BasePlayer player, string command, string[] args)
{
ulong playerId = player.userID;
string chatMessage = string.Empty;
// Переключаем настройку
if (_disabledNotificationUsers.Contains(playerId))
{
_disabledNotificationUsers.Remove(playerId);
chatMessage = GetLang(ON_MESSAGE_DEATHS, player.UserIDString);
}
else
{
_disabledNotificationUsers.Add(playerId);
chatMessage = GetLang(OFF_MESSAGE_DEATHS, player.UserIDString);
}
SendChat(player, chatMessage, _configuration.pluginSettings.chatSettings.ChatPrefix);
}
#endregion
#region Lang
public string GetLang(string LangKey, string userID = null) => lang.GetMessage(LangKey, this, userID);
protected override void LoadDefaultMessages()
{
lang.RegisterMessages(new Dictionary<string, string>
{
[ON_MESSAGE_DEATHS] = " Kill messages in chat are now <color=#008000>enabled</color>!",
[OFF_MESSAGE_DEATHS] = " Kill messages in chat are now <color=#FF0000>disabled</color>!",
[DISTANCE_PREFIX] = "<color=#77f754>[{3} m from you]</color>",
[KILL_MESSAGE] = " {0} killed {1} with {2} from <color=#ffff00>{3} m</color>",
[FOR_ATTAKER] = " You killed {1} with {2} from <color=#ffff00>{3} m</color>",
[FOR_VICTIM] = " You were killed by {0} with {2} from <color=#ffff00>{3} m</color>"
}, this);
lang.RegisterMessages(new Dictionary<string, string>
{
[ON_MESSAGE_DEATHS] = " Сообщения об убийствах в чате теперь <color=#008000>включены</color>!",
[OFF_MESSAGE_DEATHS] = " Сообщения об убийствах в чате теперь <color=#FF0000>выключены</color>!",
[DISTANCE_PREFIX] = "<color=#77f754>[{3} м от вас]</color>",
[KILL_MESSAGE] = " {0} убил {1} с помощью {2} с расстояния <color=#ffff00>{3} м</color>",
[FOR_ATTAKER] = " Вы убили {1} с помощью {2} с расстояния <color=#ffff00>{3} м</color>",
[FOR_VICTIM] = " Вас убил {0} с помощью {2} с расстояния <color=#ffff00>{3} м</color>"
}, this, "ru");
}
#endregion
#region Hooks
private void Init()
{
LoadData();
#region Command
cmd.AddChatCommand(_configuration.pluginSettings.UseCommand, this, ToggleKillFeed);
#endregion
#region Permissions
foreach (var perm in _configuration.pluginSettings.NicknameColorPermissions)
permission.RegisterPermission(perm.Key, this);
#endregion
}
private void Unload()
{
SaveData();
#region Command
cmd.RemoveChatCommand(_configuration.pluginSettings.UseCommand, this);
#endregion
}
private void OnEntityDeath(BaseCombatEntity entity, HitInfo info)
{
if (entity is BasePlayer victim && info?.Initiator is BasePlayer attacker)
{
if (victim == attacker) return;
if (victim.IsNpc || attacker.IsNpc) return;
string weaponName = GetWeaponName(info) ?? "Unknown Weapon";
float distance = Vector3.Distance(attacker.transform.position, victim.transform.position);
string attackerColorName = "#ffffff";
string victimColorName = "#ffffff";
foreach (var perm in _configuration.pluginSettings.NicknameColorPermissions)
{
if (permission.UserHasPermission(attacker.UserIDString, perm.Key))
{
attackerColorName = perm.Value.ColorKill;
}
if (permission.UserHasPermission(victim.UserIDString, perm.Key))
{
victimColorName = perm.Value.ColorDeath;
}
}
// Отправляем сообщение только тем игрокам, которые находятся в радиусе N метров от жертвы (Настраивается в конфиге)
foreach (var player in BasePlayer.activePlayerList)
{
if (player == null || _disabledNotificationUsers.Contains(player.userID)) continue;
string message;
if (player == victim)
{
message = GetLang(FOR_VICTIM, player.UserIDString);
message = message.Replace(ATTAKER_NAME, $"<color={attackerColorName}>{attacker.displayName}</color>")
.Replace(WEAPON_NAME, weaponName)
.Replace(DISTANCE_NUMBER, $"{distance:F2}");
SendChat(player, message, _configuration.pluginSettings.chatSettings.ChatPrefix);
}
else if (player == attacker)
{
message = GetLang(FOR_ATTAKER, player.UserIDString);
message = message.Replace(VICTIM_NAME, $"<color={victimColorName}>{victim.displayName}</color>")
.Replace(WEAPON_NAME, weaponName)
.Replace(DISTANCE_NUMBER, $"{distance:F2}");
SendChat(player, message, _configuration.pluginSettings.chatSettings.ChatPrefix);
}
else
{
float playerDistance = Vector3.Distance(player.transform.position, victim.transform.position);
if (playerDistance <= _configuration.pluginSettings.BroadCastRadius)
{
message = GetLang(KILL_MESSAGE, player.UserIDString);
message = message.Replace(ATTAKER_NAME, $"<color={attackerColorName}>{attacker.displayName}</color>")
.Replace(VICTIM_NAME, $"<color={victimColorName}>{victim.displayName}</color>")
.Replace(WEAPON_NAME, weaponName)
.Replace(DISTANCE_NUMBER, $"{distance:F2}");
if (_configuration.pluginSettings.chatSettings.ShowDistanceInPrefix)
{
string prefix = GetLang(DISTANCE_PREFIX, player.UserIDString);
prefix = prefix.Replace(DISTANCE_NUMBER, $"{playerDistance:F2}");
SendChat(player, message, prefix);
}
else
{
SendChat(player, message, _configuration.pluginSettings.chatSettings.ChatPrefix);
}
}
}
}
}
}
#endregion
private string GetWeaponName(HitInfo info)
{
string weapon = null;
if (_configuration.pluginSettings.chatSettings.ShowIcons)
{
weapon = info.Weapon?.GetItem()?.info?.shortname;
if (string.IsNullOrEmpty(weapon))
{
if (info.WeaponPrefab != null)
{
_uncnownWeaponsShortName.TryGetValue(info.WeaponPrefab.prefabID, out weapon);
if (!string.IsNullOrEmpty(weapon))
{
return $":{weapon}:";
}
PrintWarning($"Unknown WeaponpPefab id = {info.WeaponPrefab.prefabID}");
return ":skull:";
}
else
{
PrintWarning($"Null WeaponPrefab");
return ":skull:";
}
}
return $":{weapon}:";
}
else
{
weapon = info.Weapon?.GetItem()?.info?.displayName.english;
if (string.IsNullOrEmpty(weapon))
{
if (info.WeaponPrefab != null)
{
_uncnownWeaponsDisplayName.TryGetValue(info.WeaponPrefab.prefabID, out weapon);
if (!string.IsNullOrEmpty(weapon))
{
return weapon;
}
PrintWarning($"Unknown WeaponPrefab id = {info.WeaponPrefab.prefabID}");
}
else
{
PrintWarning($"Null WeaponPrefab");
}
}
}
return weapon;
}
public void SendChat(BasePlayer player, string message, string prefix, ConVar.Chat.ChatChannel channel = ConVar.Chat.ChatChannel.Global)
{
player.SendConsoleCommand("chat.add", channel, _configuration.pluginSettings.chatSettings.ChatAvatar, $"{prefix} {message}");
}
#region Uncnowns
private Dictionary<uint, string> _uncnownWeaponsShortName = new Dictionary<uint, string>()
{
{ 3826414185,"axe.salvaged" },
{ 3097934597,"bone.club" },
{ 2742759844,"explosive.satchel" },
{ 3898309212,"explosive.timed" },
{ 3717106868,"flamethrower" },
{ 4075317686,"flameturret" },
{ 2144399804,"grenade.beancan" },
{ 1128089209,"grenade.f1" },
{ 1744180387,"hammer.salvaged" },
{ 365233245,"hatchet" },
{ 3540736579,"stone.pickaxe" },
{ 109244214,"icepick.salvaged" },
{ 1483241467,"knife.bone" },
{ 1463807579,"landmine" },
{ 3395979968,"longsword" },
{ 2927698044,"mace" },
{ 2942508801,"machete" },
{ 1587077350,"pickaxe" },
{ 3940068399,"rock" },
{ 3046924118,"rocket.launcher" },
{ 901927673,"rocket.launcher" },
{ 1217937936,"rocket.launcher" },
{ 601440135,"rocket.launcher" },
{ 1790390506,"rocket.launcher" },
{ 288982380,"rocket.launcher" },
{ 1859672190,"multiplegrenadelauncher" },
{ 1502533292,"multiplegrenadelauncher" },
{ 3340056040,"salvaged.cleaver" },
{ 1663991785,"salvaged.sword" },
{ 1943636975,"spear.stone" },
{ 2828546575,"spear.wooden" },
{ 2141863453,"surveycharge" },
{ 2144253630,"grenade.molotov" },
{ 1710208928,"flamethrower" },
};
private Dictionary<uint, string> _uncnownWeaponsDisplayName = new Dictionary<uint, string>()
{
{ 3826414185,"Salvaged Axe" },
{ 3097934597,"Bone Club" },
{ 2742759844,"Satchel Charge" },
{ 3898309212,"C4" },
{ 3717106868,"Flame Thrower" },
{ 4075317686,"Flame Turret" },
{ 2144399804,"Beancan Grenade" },
{ 1128089209,"F1 Grenade" },
{ 1744180387,"Salvaged Hammer" },
{ 365233245,"Hatchet" },
{ 3540736579,"Stone Hatchet" },
{ 109244214,"icepick.salvaged" },
{ 1483241467,"Salvaged Icepick" },
{ 1463807579,"Landmine" },
{ 3395979968,"Longsword" },
{ 2927698044,"Mace" },
{ 2942508801,"Machete" },
{ 1587077350,"Pickaxe" },
{ 3940068399,"Rock" },
{ 3046924118,"Rocket Launcher" },
{ 901927673,"Rocket Launcher" },
{ 1217937936,"Rocket Launcher" },
{ 601440135,"Rocket Launcher" },
{ 1790390506,"Rocket Launcher" },
{ 288982380,"Rocket Launcher" },
{ 1859672190,"Grenade Launcher" },
{ 1502533292,"Grenade Launcher" },
{ 3340056040,"Salvaged Cleaver" },
{ 1663991785,"Salvaged Sword" },
{ 1943636975,"Stone Spear" },
{ 2828546575,"Wooden Spear" },
{ 2141863453,"Survey Charge" },
{ 2144253630,"Molotov Cocktail" },
{ 1710208928,"Military Flame Thrower" },
};
#endregion
}
}