├── EN └── source │ ├── CounterStrikeSharp.API.dll │ ├── RanksPoints.cs │ └── RanksPoints.csproj ├── README.md └── RU └── source ├── CounterStrikeSharp.API.dll ├── RanksPoints.cs └── RanksPoints.csproj /EN/source/CounterStrikeSharp.API.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ABKAM2023/CS2-RanksPoints/1d36ea4750df91fe591b0c6a0b5c36cce1f15f21/EN/source/CounterStrikeSharp.API.dll -------------------------------------------------------------------------------- /EN/source/RanksPoints.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | using System.Text.Json; 8 | using CounterStrikeSharp.API; 9 | using CounterStrikeSharp.API.Core; 10 | using System.Reflection; 11 | using System.Threading; 12 | using System.Threading.Tasks; 13 | using CounterStrikeSharp.API.Core.Attributes.Registration; 14 | using CounterStrikeSharp.API.Modules.Commands; 15 | using CounterStrikeSharp.API.Modules.Utils; 16 | using YamlDotNet.Serialization; 17 | using YamlDotNet.Serialization.NamingConventions; 18 | using MySqlConnector; 19 | using Dapper; 20 | 21 | namespace RanksPointsNamespace 22 | { 23 | public class RanksPoints : BasePlugin 24 | { 25 | private const string PluginAuthor = "ABKAM"; 26 | private const string PluginName = "[RanksPoints]"; 27 | private const string PluginVersion = "2.0.7"; 28 | private const string DbConfigFileName = "dbconfig.json"; 29 | private DatabaseConfig? dbConfig; 30 | private PluginConfig config; 31 | private List _pendingActions = new List(); 32 | private bool isActiveRoundForPoints; 33 | private HashSet activePlayers = new HashSet(); 34 | private Dictionary playerResetTimes = new Dictionary(); 35 | public class PluginConfig 36 | { 37 | public int MinPlayersForExperience { get; set; } = 4; 38 | public int PointsPerRoundWin { get; set; } = 2; 39 | public int PointsPerRoundLoss { get; set; } = -2; 40 | public int PointsPerMVP { get; set; } = 3; 41 | public int PointsForSuicide { get; set; } = -6; 42 | public int PointsForKill { get; set; } = 5; 43 | public int PointsForDeath { get; set; } = -5; 44 | public int PointsForAssist { get; set; } = 1; 45 | public int PointsForNoScopeAWP { get; set; } = 1; 46 | public int PointsForHeadshot { get; set; } = 1; 47 | public int PointsForBombDefusal { get; set; } = 2; 48 | public int PointsForBombExploded { get; set; } = 2; 49 | public int PointsForBombPlanting { get; set; } = 2; 50 | public int PointsForBombDropping { get; set; } = -2; 51 | public int PointsForBombPickup { get; set; } = 1; 52 | public int PointsForWallbang { get; set; } = 3; 53 | public int PointsForHostageFollows { get; set; } = 2; 54 | public int PointsForHostageStopsFollowing { get; set; } = -2; 55 | public int PointsForHostageRescued { get; set; } = 4; 56 | public int PointsForKillThroughSmoke { get; set; } = 3; 57 | public string KillThroughSmokeMessage { get; set; } = "kill through smoke"; 58 | public string KillThroughSmokeMessageColor { get; set; } = "{Green}"; 59 | public int PointsForBlindKill { get; set; } = 5; 60 | public string BlindKillMessage { get; set; } = "blind kill"; 61 | public string BlindKillMessageColor { get; set; } = "{Yellow}"; 62 | public bool GivePointsForBotKills { get; set; } = false; 63 | public bool EnableClanTags { get; set; } = true; 64 | public bool IsRankCommandEnabled { get; set; } = true; 65 | public bool IsTopCommandEnabled { get; set; } = true; 66 | public bool IsTopkillsCommandEnabled { get; set; } = true; 67 | public bool IsTopdeathsCommandEnabled { get; set; } = true; 68 | public bool IsTopkdrCommandEnabled { get; set; } = true; 69 | public bool IsToptimeCommandEnabled { get; set; } = true; 70 | public bool IsResetstatsCommandEnabled { get; set; } = true; 71 | public bool IsRanksCommandEnabled { get; set; } = true; 72 | public bool IsLvlCommandEnabled { get; set; } = true; 73 | public bool IsTagRankCommandEnabled { get; set; } = true; 74 | public bool EnableSpecialNicknameBonus { get; set; } = true; 75 | public double BonusMultiplierForSpecialNickname { get; set; } = 1.5; 76 | public string SpecialNicknameContains { get; set; } = "example.com"; 77 | public string GetActivePlayerCountMsg { get; set; } = "[ {Yellow}RanksPoints {White}] At least {Red}{MIN_PLAYERS} {White}players are required to grant experience."; 78 | public string PointsChangeMessage { get; set; } = "[ {Yellow}RanksPoints{White} ] Your experience:{COLOR} {POINTS} [{SIGN}{CHANGE_POINTS} for {REASON}]"; 79 | public string SuicideMessage { get; set; } = "suicide"; 80 | public string SuicideMessageColor { get; set; } = "{Red}"; 81 | public string DeathMessage { get; set; } = "death"; 82 | public string DeathMessageColor { get; set; } = "{Red}"; 83 | public string KillMessage { get; set; } = "kill"; 84 | public string KillMessageColor { get; set; } = "{Green}"; 85 | public string NoScopeAWPMessage { get; set; } = "no-scope AWP kill"; 86 | public string NoScopeAWPMessageColor { get; set; } = "{Blue}"; 87 | public string HeadshotMessage { get; set; } = "headshot"; 88 | public string HeadshotMessageColor { get; set; } = "{Yellow}"; 89 | public string AssistMessage { get; set; } = "assist"; 90 | public string AssistMessageColor { get; set; } = "{Blue}"; 91 | public string RoundWinMessage { get; set; } = "round win"; 92 | public string RoundWinMessageColor { get; set; } = "{Green}"; 93 | public string RoundLossMessage { get; set; } = "round loss"; 94 | public string RoundLossMessageColor { get; set; } = "{Red}"; 95 | public string MVPMessage { get; set; } = "MVP"; 96 | public string MVPMessageColor { get; set; } = "{Gold}"; 97 | public string BombDefusalMessage { get; set; } = "bomb defusal"; 98 | public string BombDefusalMessageColor { get; set; } = "{Green}"; 99 | public string BombExplodedMessage { get; set; } = "bomb exploded"; 100 | public string BombExplodedMessageColor { get; set; } = "{Green}"; 101 | public string BombPlantingMessage { get; set; } = "bomb planting"; 102 | public string BombPlantingMessageColor { get; set; } = "{Green}"; 103 | public string BombDroppingMessage { get; set; } = "bomb dropping"; 104 | public string BombDroppingMessageColor { get; set; } = "{Red}"; 105 | public string BombPickupMessage { get; set; } = "bomb pickup"; 106 | public string BombPickupMessageColor { get; set; } = "{Green}"; 107 | public string WallbangMessage { get; set; } = "wallbang"; 108 | public string WallbangMessageColor { get; set; } = "{Purple}"; 109 | public string HostageFollowsMessage { get; set; } = "hostage follows"; 110 | public string HostageFollowsMessageColor { get; set; } = "{Green}"; 111 | public string HostageStopsFollowingMessage { get; set; } = "hostage stops following"; 112 | public string HostageStopsFollowingMessageColor { get; set; } = "{Red}"; 113 | public string HostageRescuedMessage { get; set; } = "hostage rescued"; 114 | public string HostageRescuedMessageColor { get; set; } = "{Blue}"; 115 | public string RankCommandMessage { get; set; } = "[ {Yellow}RanksPoints {White}] Rank: {Green}{RANK_NAME} {White}| Position: {Blue}{PLACE}/{TOTAL_PLAYERS} {White}| Experience: {Gold}{POINTS} {White}| Kills: {Green}{KILLS} {White}| Deaths: {Red}{DEATHS} {White}| KDR: {Yellow}{KDR} {White}| Time on Server: {Gold}{PLAY_TIME}"; 116 | public string TimeFormat { get; set; } = "{0}d {1}h {2}min"; 117 | public string TopCommandIntroMessage { get; set; } = "[ {Blue}Top Players{White} ]"; 118 | public string TopCommandPlayerMessage { get; set; } = "{INDEX}. {Grey}{NAME} - {White}{RANK} {Grey}- {Blue}{POINTS} points"; 119 | public string TopCommandNoDataMessage { get; set; } = "[ {Red}Error{White} ] No data available for top players."; 120 | public string TopCommandErrorMessage { get; set; } = "[ {Red}Error{White} ] An error occurred while executing the command."; 121 | public string TopKillsCommandIntroMessage { get; set; } = "[ {Green}Top Players by Kills{White} ]"; 122 | public string TopKillsCommandPlayerMessage { get; set; } = "{INDEX}. {Grey}{NAME} - {Green}{KILLS} kills{White}"; 123 | public string TopKillsCommandNoDataMessage { get; set; } = "[ {Red}Error{White} ] No data available for top players by kills."; 124 | public string TopKillsCommandErrorMessage { get; set; } = "[ {Red}Error{White} ] An error occurred while executing the command."; 125 | public string TopDeathsCommandIntroMessage { get; set; } = "[ {Red}Top Players by Deaths{White} ]"; 126 | public string TopDeathsCommandPlayerMessage { get; set; } = "{INDEX}. {Grey}{NAME}{White} - {Red}{DEATHS} deaths{White}"; 127 | public string TopDeathsCommandNoDataMessage { get; set; } = "[ {Red}Error{White} ] No data available for top players by deaths."; 128 | public string TopDeathsCommandErrorMessage { get; set; } = "[ {Red}Error{White} ] An error occurred while executing the command."; 129 | public string TopKDRCommandIntroMessage { get; set; } = "[ {Yellow}Top Players by KDR{White} ]"; 130 | public string TopKDRCommandPlayerMessage { get; set; } = "{INDEX}. {Grey}{NAME}{White} - {Yellow}KDR: {KDR}"; 131 | public string TopKDRCommandNoDataMessage { get; set; } = "[ {Red}Error{White} ] No data available for top players by KDR."; 132 | public string TopKDRCommandErrorMessage { get; set; } = "[ {Red}Error{White} ] An error occurred while executing the command."; 133 | public string TopTimeCommandIntroMessage { get; set; } = "[ {Gold}Top Players by Time on Server{White} ]"; 134 | public string TopTimeCommandPlayerMessage { get; set; } = "{INDEX}. {Grey}{NAME} - {Gold}{TIME}{White}"; 135 | public string TopTimeCommandNoDataMessage { get; set; } = "[ {Red}Error{White} ] No data available for top players by time on server."; 136 | public string TopTimeCommandErrorMessage { get; set; } = "[ {Red}Error{White} ] An error occurred while executing the command."; 137 | public string TopTimeFormat { get; set; } = "{0}d {1}h {2}min"; 138 | public string ResetStatsCooldownMessage { get; set; } = "[ {Red}RanksPoints {White}] You can only reset your stats once every 3 hours."; 139 | public string ResetStatsSuccessMessage { get; set; } = "[ {Yellow}RanksPoints {White}] Your stats have been reset."; 140 | public double ResetStatsCooldownHours { get; set; } = 3.0; 141 | public string RanksCommandIntroMessage { get; set; } = "[ {Gold}Rank List{White} ]"; 142 | public string RanksCommandRankMessage { get; set; } = "{NAME} - {Green}{EXPERIENCE} experience{White}"; 143 | public string RanksCommandNoDataMessage { get; set; } = "[ {Red}Error{White} ] No data available for ranks."; 144 | public string RanksCommandErrorMessage { get; set; } = "[ {Red}Error{White} ] An error occurred while executing the command."; 145 | public string LvlCommandIntroMessage { get; set; } = "[ {Gold}Available Commands List{White} ]"; 146 | public string RankCommandDescription { get; set; } = "- {Green}!rank {White}- Shows your current rank and stats"; 147 | public string TopCommandDescription { get; set; } = "- {Green}!top {White}- Shows top 10 players by points"; 148 | public string TopKillsCommandDescription { get; set; } = "- {Green}!topkills {White}- Shows top 10 players by kills"; 149 | public string TopDeathsCommandDescription { get; set; } = "- {Green}!topdeaths {White}- Shows top 10 players by deaths"; 150 | public string TopKDRCommandDescription { get; set; } = "- {Green}!topkdr {White}- Shows top 10 players by KDR"; 151 | public string TopTimeCommandDescription { get; set; } = "- {Green}!toptime {White}- Shows top 10 players by time on server"; 152 | public string ResetStatsCommandDescription { get; set; } = "- {Green}!resetstats {White}- Resets your stats (can be used once every 3 hours)"; 153 | public string RanksCommandDescription { get; set; } = "- {Green}!ranks {White}- Shows a list of all ranks and the experience required to achieve them"; 154 | public string TagRankCommandDescription { get; set; } = "- {Green}!tagrank {White}- Enables or disables showing your clan tag"; 155 | public string RankUpMessage { get; set; } = "Your rank has been upgraded to {RANK_NAME}!"; 156 | public string RankDownMessage { get; set; } = "Your rank has been downgraded to {RANK_NAME}."; 157 | public string TagRankEnabledMessage { get; set; } = "[ {Yellow}RanksPoints {White}] Your clan tag will be displayed again starting next round."; 158 | public string TagRankDisabledMessage { get; set; } = "[ {Yellow}RanksPoints {White}] Your clan tag will no longer be displayed starting next round."; 159 | } 160 | public class RankConfig 161 | { 162 | public int Id { get; set; } 163 | public string? Name { get; set; } 164 | public int MinExperience { get; set; } 165 | public string? ClanTag { get; set; } 166 | } 167 | public void SaveConfig(PluginConfig config, string filePath) 168 | { 169 | var stringBuilder = new StringBuilder(); 170 | 171 | stringBuilder.AppendLine("# Configuration file for RankPoints"); 172 | stringBuilder.AppendLine(); 173 | stringBuilder.AppendLine("# Number of points awarded"); 174 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsForKill), config.PointsForKill, "Points for kill - the number of points awarded to a player for killing an opponent."); 175 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsForDeath), config.PointsForDeath, "Points deducted for death - the number of points deducted from a player for dying."); 176 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsForAssist), config.PointsForAssist, "Points for assist - the number of points awarded to a player for assisting in a kill."); 177 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsForSuicide), config.PointsForSuicide, "Points for suicide - the number of points deducted from a player for committing suicide."); 178 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsForHeadshot), config.PointsForHeadshot, "Points for headshot - additional points for killing with a headshot."); 179 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsPerRoundWin), config.PointsPerRoundWin, "Points for round win - the number of points awarded to a player for winning a round for their team."); 180 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsPerRoundLoss), config.PointsPerRoundLoss, "Points for round loss - the number of points deducted from a player for losing a round for their team."); 181 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsPerMVP), config.PointsPerMVP, "Points for MVP - the number of points awarded to a player for receiving the MVP title of a round."); 182 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsForNoScopeAWP), config.PointsForNoScopeAWP, "Points for no-scope AWP kill - additional points for killing without using the scope."); 183 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsForBombDefusal), config.PointsForBombDefusal, "Points for bomb defusal"); 184 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsForBombExploded), config.PointsForBombExploded, "Points for bomb exploded"); 185 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsForBombPlanting), config.PointsForBombPlanting, "Points for bomb planting - the number of points awarded to a player for successfully planting the bomb."); 186 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsForBombDropping), config.PointsForBombDropping, "Points for bomb dropping - the number of points deducted from a player for dropping the bomb."); 187 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsForBombPickup), config.PointsForBombPickup, "Points for bomb pickup - the number of points awarded to a player for picking up the bomb."); 188 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsForWallbang), config.PointsForWallbang, "Points for wallbang - points for killing through a wall."); 189 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsForHostageFollows), config.PointsForHostageFollows, "Points for hostage follows"); 190 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsForHostageStopsFollowing), config.PointsForHostageStopsFollowing, "Points for stopping hostage following"); 191 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsForHostageRescued), config.PointsForHostageRescued, "Points for hostage rescued"); 192 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsForKillThroughSmoke), config.PointsForKillThroughSmoke, "Points for kill through smoke - the number of points awarded to a player for killing an enemy through a smoke screen."); 193 | AppendConfigValueWithComment(stringBuilder, nameof(config.PointsForBlindKill), config.PointsForBlindKill, "Points for blind kill - the number of points awarded to a player for killing while blinded."); 194 | 195 | 196 | stringBuilder.AppendLine(); 197 | stringBuilder.AppendLine("# RanksPoints parameters"); 198 | stringBuilder.AppendLine("# Display clan tags for player ranks. true - enabled, false - disabled."); 199 | stringBuilder.AppendLine($"EnableClanTags: {config.EnableClanTags}"); 200 | stringBuilder.AppendLine("# Minimum number of players for experience to be granted - players only gain experience if this number of players is on the server."); 201 | stringBuilder.AppendLine($"GetActivePlayerCountMsg: \"{EscapeMessage(config.GetActivePlayerCountMsg)}\""); 202 | AppendConfigValue(stringBuilder, nameof(config.MinPlayersForExperience), config.MinPlayersForExperience); 203 | stringBuilder.AppendLine("# Enable or disable extra experience for special nicknames"); 204 | stringBuilder.AppendLine($"EnableSpecialNicknameBonus: {config.EnableSpecialNicknameBonus.ToString().ToLower()}"); 205 | stringBuilder.AppendLine("# Experience multiplier for special nicknames"); 206 | stringBuilder.AppendLine($"BonusMultiplierForSpecialNickname: {config.BonusMultiplierForSpecialNickname}"); 207 | stringBuilder.AppendLine("# String to search in the nickname for applying the multiplier"); 208 | stringBuilder.AppendLine($"SpecialNicknameContains: \"{EscapeMessage(config.SpecialNicknameContains)}\""); 209 | stringBuilder.AppendLine("# Enable or disable points for bot kills. true - enabled, false - disabled."); 210 | AppendConfigValue(stringBuilder, nameof(config.GivePointsForBotKills), config.GivePointsForBotKills); 211 | 212 | 213 | stringBuilder.AppendLine(); 214 | stringBuilder.AppendLine("# All RanksPoints messages"); 215 | stringBuilder.AppendLine("# Experience gain messages"); 216 | stringBuilder.AppendLine($"PointsChangeMessage: \"{EscapeMessage(config.PointsChangeMessage)}\""); 217 | stringBuilder.AppendLine("# Events"); 218 | stringBuilder.AppendLine($"SuicideMessage: \"{EscapeMessage(config.SuicideMessage)}\""); 219 | stringBuilder.AppendLine($"SuicideMessageColor: \"{EscapeMessage(config.SuicideMessageColor)}\""); 220 | stringBuilder.AppendLine($"DeathMessage: \"{EscapeMessage(config.DeathMessage)}\""); 221 | stringBuilder.AppendLine($"DeathMessageColor: \"{EscapeMessage(config.DeathMessageColor)}\""); 222 | stringBuilder.AppendLine($"KillMessage: \"{EscapeMessage(config.KillMessage)}\""); 223 | stringBuilder.AppendLine($"KillMessageColor: \"{EscapeMessage(config.KillMessageColor)}\""); 224 | stringBuilder.AppendLine($"NoScopeAWPMessage: \"{EscapeMessage(config.NoScopeAWPMessage)}\""); 225 | stringBuilder.AppendLine($"NoScopeAWPMessageColor: \"{EscapeMessage(config.NoScopeAWPMessageColor)}\""); 226 | stringBuilder.AppendLine($"HeadshotMessage: \"{EscapeMessage(config.HeadshotMessage)}\""); 227 | stringBuilder.AppendLine($"HeadshotMessageColor: \"{EscapeMessage(config.HeadshotMessageColor)}\""); 228 | stringBuilder.AppendLine($"AssistMessage: \"{EscapeMessage(config.AssistMessage)}\""); 229 | stringBuilder.AppendLine($"AssistMessageColor: \"{EscapeMessage(config.AssistMessageColor)}\""); 230 | stringBuilder.AppendLine($"RoundWinMessage: \"{EscapeMessage(config.RoundWinMessage)}\""); 231 | stringBuilder.AppendLine($"RoundWinMessageColor: \"{EscapeMessage(config.RoundWinMessageColor)}\""); 232 | stringBuilder.AppendLine($"RoundLossMessage: \"{EscapeMessage(config.RoundLossMessage)}\""); 233 | stringBuilder.AppendLine($"RoundLossMessageColor: \"{EscapeMessage(config.RoundLossMessageColor)}\""); 234 | stringBuilder.AppendLine($"MVPMessage: \"{EscapeMessage(config.MVPMessage)}\""); 235 | stringBuilder.AppendLine($"MVPMessageColor: \"{EscapeMessage(config.MVPMessageColor)}\""); 236 | stringBuilder.AppendLine($"BombDefusalMessage: \"{EscapeMessage(config.BombDefusalMessage)}\""); 237 | stringBuilder.AppendLine($"BombDefusalMessageColor: \"{EscapeMessage(config.BombDefusalMessageColor)}\""); 238 | stringBuilder.AppendLine($"BombExplodedMessage: \"{EscapeMessage(config.BombExplodedMessage)}\""); 239 | stringBuilder.AppendLine($"BombExplodedMessageColor: \"{EscapeMessage(config.BombExplodedMessageColor)}\""); 240 | stringBuilder.AppendLine($"BombPlantingMessage: \"{EscapeMessage(config.BombPlantingMessage)}\""); 241 | stringBuilder.AppendLine($"BombPlantingMessageColor: \"{EscapeMessage(config.BombPlantingMessageColor)}\""); 242 | stringBuilder.AppendLine($"BombDroppingMessage: \"{EscapeMessage(config.BombDroppingMessage)}\""); 243 | stringBuilder.AppendLine($"BombDroppingMessageColor: \"{EscapeMessage(config.BombDroppingMessageColor)}\""); 244 | stringBuilder.AppendLine($"BombPickupMessage: \"{EscapeMessage(config.BombPickupMessage)}\""); 245 | stringBuilder.AppendLine($"BombPickupMessageColor: \"{EscapeMessage(config.BombPickupMessageColor)}\""); 246 | stringBuilder.AppendLine($"WallbangMessage: \"{EscapeMessage(config.WallbangMessage)}\""); 247 | stringBuilder.AppendLine($"WallbangMessageColor: \"{EscapeMessage(config.WallbangMessageColor)}\""); 248 | stringBuilder.AppendLine($"HostageFollowsMessage: \"{EscapeMessage(config.HostageFollowsMessage)}\""); 249 | stringBuilder.AppendLine($"HostageFollowsMessageColor: \"{EscapeMessage(config.HostageFollowsMessageColor)}\""); 250 | stringBuilder.AppendLine($"HostageStopsFollowingMessage: \"{EscapeMessage(config.HostageStopsFollowingMessage)}\""); 251 | stringBuilder.AppendLine($"HostageStopsFollowingMessageColor: \"{EscapeMessage(config.HostageStopsFollowingMessageColor)}\""); 252 | stringBuilder.AppendLine($"HostageRescuedMessage: \"{EscapeMessage(config.HostageRescuedMessage)}\""); 253 | stringBuilder.AppendLine($"HostageRescuedMessageColor: \"{EscapeMessage(config.HostageRescuedMessageColor)}\""); 254 | stringBuilder.AppendLine($"KillThroughSmokeMessage: \"{EscapeMessage(config.KillThroughSmokeMessage)}\""); 255 | stringBuilder.AppendLine($"KillThroughSmokeMessageColor: \"{EscapeMessage(config.KillThroughSmokeMessageColor)}\""); 256 | stringBuilder.AppendLine($"BlindKillMessage: \"{EscapeMessage(config.BlindKillMessage)}\""); 257 | stringBuilder.AppendLine($"BlindKillMessageColor: \"{EscapeMessage(config.BlindKillMessageColor)}\""); 258 | 259 | 260 | stringBuilder.AppendLine(); 261 | AppendConfigValueWithComment(stringBuilder, nameof(config.RankUpMessage), config.RankUpMessage, "Message for rank upgrade."); 262 | AppendConfigValueWithComment(stringBuilder, nameof(config.RankDownMessage), config.RankDownMessage, "Message for rank downgrade."); 263 | stringBuilder.AppendLine(); 264 | stringBuilder.AppendLine("# !rank"); 265 | stringBuilder.AppendLine($"RankCommandMessage : \"{EscapeMessage(config.RankCommandMessage)}\""); 266 | stringBuilder.AppendLine($"TimeFormat: \"{EscapeMessage(config.TimeFormat)}\""); 267 | stringBuilder.AppendLine("# Enable or disable the !rank command"); 268 | stringBuilder.AppendLine($"IsRankCommandEnabled: {config.IsRankCommandEnabled.ToString().ToLower()}"); 269 | 270 | stringBuilder.AppendLine(); 271 | stringBuilder.AppendLine("# !top"); 272 | stringBuilder.AppendLine($"TopCommandIntroMessage : \"{EscapeMessage(config.TopCommandIntroMessage)}\""); 273 | stringBuilder.AppendLine($"TopCommandPlayerMessage: \"{EscapeMessage(config.TopCommandPlayerMessage)}\""); 274 | stringBuilder.AppendLine($"TopCommandNoDataMessage: \"{EscapeMessage(config.TopCommandNoDataMessage)}\""); 275 | stringBuilder.AppendLine($"TopCommandErrorMessage: \"{EscapeMessage(config.TopCommandErrorMessage)}\""); 276 | stringBuilder.AppendLine("# Enable or disable the !top command"); 277 | stringBuilder.AppendLine($"IsTopCommandEnabled: {config.IsTopCommandEnabled.ToString().ToLower()}"); 278 | 279 | stringBuilder.AppendLine(); 280 | stringBuilder.AppendLine("# !topkills"); 281 | stringBuilder.AppendLine($"TopKillsCommandIntroMessage: \"{EscapeMessage(config.TopKillsCommandIntroMessage)}\""); 282 | stringBuilder.AppendLine($"TopKillsCommandPlayerMessage: \"{EscapeMessage(config.TopKillsCommandPlayerMessage)}\""); 283 | stringBuilder.AppendLine($"TopKillsCommandNoDataMessage: \"{EscapeMessage(config.TopKillsCommandNoDataMessage)}\""); 284 | stringBuilder.AppendLine($"TopKillsCommandErrorMessage: \"{EscapeMessage(config.TopKillsCommandErrorMessage)}\""); 285 | stringBuilder.AppendLine("# Enable or disable the !topkills command"); 286 | stringBuilder.AppendLine($"IsTopkillsCommandEnabled: {config.IsTopkillsCommandEnabled.ToString().ToLower()}"); 287 | 288 | stringBuilder.AppendLine(); 289 | stringBuilder.AppendLine("# !topdeaths"); 290 | stringBuilder.AppendLine($"TopDeathsCommandIntroMessage: \"{EscapeMessage(config.TopDeathsCommandIntroMessage)}\""); 291 | stringBuilder.AppendLine($"TopDeathsCommandPlayerMessage: \"{EscapeMessage(config.TopDeathsCommandPlayerMessage)}\""); 292 | stringBuilder.AppendLine($"TopDeathsCommandNoDataMessage: \"{EscapeMessage(config.TopDeathsCommandNoDataMessage)}\""); 293 | stringBuilder.AppendLine($"TopDeathsCommandErrorMessage: \"{EscapeMessage(config.TopDeathsCommandErrorMessage)}\""); 294 | stringBuilder.AppendLine("# Enable or disable the !topdeaths command"); 295 | stringBuilder.AppendLine($"IsTopdeathsCommandEnabled: {config.IsTopdeathsCommandEnabled.ToString().ToLower()}"); 296 | 297 | stringBuilder.AppendLine(); 298 | stringBuilder.AppendLine("# !topkdr"); 299 | stringBuilder.AppendLine($"TopKDRCommandIntroMessage: \"{EscapeMessage(config.TopKDRCommandIntroMessage)}\""); 300 | stringBuilder.AppendLine($"TopKDRCommandPlayerMessage: \"{EscapeMessage(config.TopKDRCommandPlayerMessage)}\""); 301 | stringBuilder.AppendLine($"TopKDRCommandNoDataMessage: \"{EscapeMessage(config.TopKDRCommandNoDataMessage)}\""); 302 | stringBuilder.AppendLine($"TopKDRCommandErrorMessage: \"{EscapeMessage(config.TopKDRCommandErrorMessage)}\""); 303 | stringBuilder.AppendLine("# Enable or disable the !topkdr command"); 304 | stringBuilder.AppendLine($"IsTopkdrCommandEnabled: {config.IsTopkdrCommandEnabled.ToString().ToLower()}"); 305 | 306 | stringBuilder.AppendLine(); 307 | stringBuilder.AppendLine("# !toptime"); 308 | stringBuilder.AppendLine($"TopTimeCommandIntroMessage: \"{EscapeMessage(config.TopTimeCommandIntroMessage)}\""); 309 | stringBuilder.AppendLine($"TopTimeCommandPlayerMessage: \"{EscapeMessage(config.TopTimeCommandPlayerMessage)}\""); 310 | stringBuilder.AppendLine($"TopTimeCommandNoDataMessage : \"{EscapeMessage(config.TopTimeCommandNoDataMessage)}\""); 311 | stringBuilder.AppendLine($"TopTimeCommandErrorMessage: \"{EscapeMessage(config.TopTimeCommandErrorMessage)}\""); 312 | stringBuilder.AppendLine($"TopTimeFormat: \"{EscapeMessage(config.TopTimeFormat)}\""); 313 | stringBuilder.AppendLine("# Enable or disable the !toptime command"); 314 | stringBuilder.AppendLine($"IsToptimeCommandEnabled: {config.IsToptimeCommandEnabled.ToString().ToLower()}"); 315 | 316 | stringBuilder.AppendLine(); 317 | stringBuilder.AppendLine("# !resetstats"); 318 | stringBuilder.AppendLine($"ResetStatsCooldownMessage: \"{EscapeMessage(config.ResetStatsCooldownMessage)}\""); 319 | stringBuilder.AppendLine($"ResetStatsSuccessMessage: \"{EscapeMessage(config.ResetStatsSuccessMessage)}\""); 320 | stringBuilder.AppendLine($"ResetStatsCooldownHours: \"{EscapeMessage(config.ResetStatsCooldownHours.ToString())}\""); 321 | stringBuilder.AppendLine("# Enable or disable the !resetstats command"); 322 | stringBuilder.AppendLine($"IsResetstatsCommandEnabled: {config.IsResetstatsCommandEnabled.ToString().ToLower()}"); 323 | 324 | stringBuilder.AppendLine(); 325 | stringBuilder.AppendLine("# !ranks"); 326 | stringBuilder.AppendLine($"RanksCommandIntroMessage: \"{EscapeMessage(config.RanksCommandIntroMessage)}\""); 327 | stringBuilder.AppendLine($"RanksCommandRankMessage: \"{EscapeMessage(config.RanksCommandRankMessage)}\""); 328 | stringBuilder.AppendLine($"RanksCommandNoDataMessage: \"{EscapeMessage(config.RanksCommandNoDataMessage)}\""); 329 | stringBuilder.AppendLine($"RanksCommandErrorMessage: \"{EscapeMessage(config.RanksCommandErrorMessage)}\""); 330 | stringBuilder.AppendLine("# Enable or disable the !ranks command"); 331 | stringBuilder.AppendLine($"IsRanksCommandEnabled: {config.IsRanksCommandEnabled.ToString().ToLower()}"); 332 | 333 | stringBuilder.AppendLine(); 334 | stringBuilder.AppendLine("# !lvl"); 335 | stringBuilder.AppendLine($"LvlCommandIntroMessage: \"{EscapeMessage(config.LvlCommandIntroMessage)}\""); 336 | stringBuilder.AppendLine($"RankCommandDescription: \"{EscapeMessage(config.RankCommandDescription)}\""); 337 | stringBuilder.AppendLine($"TopCommandDescription: \"{EscapeMessage(config.TopCommandDescription)}\""); 338 | stringBuilder.AppendLine($"TopKillsCommandDescription: \"{EscapeMessage(config.TopKillsCommandDescription)}\""); 339 | stringBuilder.AppendLine($"TopDeathsCommandDescription: \"{EscapeMessage(config.TopDeathsCommandDescription)}\""); 340 | stringBuilder.AppendLine($"TopKDRCommandDescription: \"{EscapeMessage(config.TopKDRCommandDescription)}\""); 341 | stringBuilder.AppendLine($"TopTimeCommandDescription: \"{EscapeMessage(config.TopTimeCommandDescription)}\""); 342 | stringBuilder.AppendLine($"ResetStatsCommandDescription: \"{EscapeMessage(config.ResetStatsCommandDescription)}\""); 343 | stringBuilder.AppendLine($"RanksCommandDescription: \"{EscapeMessage(config.RanksCommandDescription)}\""); 344 | stringBuilder.AppendLine($"TagRankCommandDescription: \"{EscapeMessage(config.TagRankCommandDescription)}\""); 345 | stringBuilder.AppendLine("# Enable or disable the !lvl command"); 346 | stringBuilder.AppendLine($"IsLvlCommandEnabled: {config.IsLvlCommandEnabled.ToString().ToLower()}"); 347 | 348 | stringBuilder.AppendLine(); 349 | stringBuilder.AppendLine("# !tagrank"); 350 | stringBuilder.AppendLine($"TagRankEnabledMessage: \"{EscapeMessage(config.TagRankEnabledMessage)}\""); 351 | stringBuilder.AppendLine($"TagRankDisabledMessage: \"{EscapeMessage(config.TagRankDisabledMessage)}\""); 352 | stringBuilder.AppendLine("# Enable or disable the !tagrank command"); 353 | stringBuilder.AppendLine($"IsTagRankCommandEnabled: {config.IsTagRankCommandEnabled.ToString().ToLower()}"); 354 | 355 | File.WriteAllText(filePath, stringBuilder.ToString()); 356 | } 357 | 358 | private string EscapeMessage(string message) 359 | { 360 | return message.Replace("\"", "\\\"").Replace("\n", "\\n"); 361 | } 362 | public List WeaponPointsConfig { get; set; } 363 | private List weaponPointsConfig; 364 | 365 | private void AppendConfigValueWithComment(StringBuilder sb, string key, object value, string comment) 366 | { 367 | sb.AppendLine($"# {comment}"); 368 | sb.AppendLine($"{key}: {value}"); 369 | } 370 | private void AppendConfigValue(StringBuilder sb, string key, object value) 371 | { 372 | sb.AppendLine($"{key}: {value}"); 373 | } 374 | private List LoadRanksConfig() 375 | { 376 | var filePath = Path.Combine(ModuleDirectory, "settings_ranks.yml"); 377 | 378 | if (!File.Exists(filePath)) 379 | { 380 | var defaultRanks = new List 381 | { 382 | new RankConfig { Id = 0, Name = "Silver - I", MinExperience = 0, ClanTag = "[Silver - I]" }, 383 | new RankConfig { Id = 1, Name = "Silver - II", MinExperience = 10, ClanTag = "[Silver - II]" }, 384 | new RankConfig { Id = 2, Name = "Silver - III", MinExperience = 25, ClanTag = "[Silver - III]" }, 385 | new RankConfig { Id = 3, Name = "Silver - IV", MinExperience = 50, ClanTag = "[Silver - IV]" }, 386 | new RankConfig { Id = 4, Name = "Silver Elite", MinExperience = 75, ClanTag = "[Silver Elite]" }, 387 | new RankConfig { Id = 5, Name = "Silver - Grand Master", MinExperience = 100, ClanTag = "[Silver - GM]" }, 388 | new RankConfig { Id = 6, Name = "Gold Star - I", MinExperience = 150, ClanTag = "[Gold Star - I]" }, 389 | new RankConfig { Id = 7, Name = "Gold Star - II", MinExperience = 200, ClanTag = "[Gold Star - II]" }, 390 | new RankConfig { Id = 8, Name = "Gold Star - III", MinExperience = 300, ClanTag = "[Gold Star - III]" }, 391 | new RankConfig { Id = 9, Name = "Gold Star - Master", MinExperience = 500, ClanTag = "[Gold Star - M]" }, 392 | new RankConfig { Id = 10, Name = "Master Guardian - I", MinExperience = 750, ClanTag = "[Master Guardian - I]" }, 393 | new RankConfig { Id = 11, Name = "Master Guardian - II", MinExperience = 1000, ClanTag = "[Master Guardian - II]" }, 394 | new RankConfig { Id = 12, Name = "Master Guardian - Elite", MinExperience = 1500, ClanTag = "[Master Guardian - E]" }, 395 | new RankConfig { Id = 13, Name = "Distinguished Master Guardian", MinExperience = 2000, ClanTag = "[Distinguished MG]" }, 396 | new RankConfig { Id = 14, Name = "Legendary Eagle", MinExperience = 3000, ClanTag = "[Legendary Eagle]" }, 397 | new RankConfig { Id = 15, Name = "Legendary Eagle - Master", MinExperience = 5000, ClanTag = "[Leg. Eagle - M]" }, 398 | new RankConfig { Id = 16, Name = "Supreme Master First Class", MinExperience = 7500, ClanTag = "[Supreme Master FC]" }, 399 | new RankConfig { Id = 17, Name = "Global Elite", MinExperience = 10000, ClanTag = "[Global Elite]" } 400 | }; 401 | 402 | var serializer = new SerializerBuilder() 403 | .WithNamingConvention(CamelCaseNamingConvention.Instance) 404 | .Build(); 405 | 406 | var yaml = serializer.Serialize(defaultRanks); 407 | File.WriteAllText(filePath, yaml); 408 | 409 | return defaultRanks; 410 | } 411 | 412 | try 413 | { 414 | var yaml = File.ReadAllText(filePath); 415 | var deserializer = new DeserializerBuilder() 416 | .WithNamingConvention(CamelCaseNamingConvention.Instance) 417 | .Build(); 418 | var ranksConfig = deserializer.Deserialize>(yaml); 419 | 420 | return ranksConfig ?? new List(); 421 | } 422 | catch (Exception ex) 423 | { 424 | Console.WriteLine($"Error reading rank configuration file: {ex.Message}"); 425 | return new List(); 426 | } 427 | } 428 | private void SetPlayerClanTag(CCSPlayerController player) 429 | { 430 | if (!config.EnableClanTags) return; 431 | 432 | var steamID64 = player.SteamID.ToString(); 433 | var tagSettings = LoadTagSettings(); 434 | 435 | if (tagSettings.DisabledTags.Contains(steamID64)) 436 | { 437 | player.Clan = ""; 438 | return; 439 | } 440 | 441 | try 442 | { 443 | var rank = GetCurrentRank(steamID64); 444 | 445 | if (rank != null && !string.IsNullOrEmpty(rank.ClanTag)) 446 | { 447 | player.Clan = rank.ClanTag; 448 | } 449 | else 450 | { 451 | Console.WriteLine("[SetPlayerClanTag] Unable to set clan tag: player or rank is missing."); 452 | } 453 | } 454 | catch (Exception ex) 455 | { 456 | Console.WriteLine($"[SetPlayerClanTag] Error getting rank: {ex.Message}"); 457 | } 458 | } 459 | 460 | public static string ConvertSteamID64ToSteamID(string steamId64) 461 | { 462 | if (ulong.TryParse(steamId64, out var communityId) && communityId > 76561197960265728) 463 | { 464 | var authServer = (communityId - 76561197960265728) % 2; 465 | var authId = (communityId - 76561197960265728 - authServer) / 2; 466 | return $"STEAM_1:{authServer}:{authId}"; 467 | } 468 | return null; 469 | } 470 | private bool isWarmup = true; 471 | public override void Load(bool hotReload) 472 | { 473 | base.Load(hotReload); 474 | CreateDbConfigIfNotExists(); 475 | dbConfig = DatabaseConfig.ReadFromJsonFile(Path.Combine(ModuleDirectory, DbConfigFileName)); 476 | RegisterListener(OnClientConnected); 477 | RegisterEventHandler(OnPlayerDisconnect); 478 | RegisterListener(OnMapEnd); 479 | RegisterEventHandler(OnRoundEnd); 480 | RegisterEventHandler(OnPlayerDeath); 481 | RegisterEventHandler(OnWeaponFire); 482 | RegisterEventHandler(OnPlayerHurt); 483 | RegisterEventHandler(OnPlayerMVP); 484 | RegisterEventHandler(OnRoundStart); 485 | RegisterEventHandler(OnBombExploded); 486 | RegisterEventHandler(OnBombDefused); 487 | RegisterEventHandler(OnMatchStart); 488 | RegisterEventHandler(OnWarmupStart); 489 | RegisterEventHandler(OnBombPlanted); 490 | RegisterEventHandler(OnBombDropped); 491 | RegisterEventHandler(OnBombPickup); 492 | RegisterEventHandler(OnHostageFollows); 493 | RegisterEventHandler(OnHostageStopsFollowing); 494 | RegisterEventHandler(OnHostageRescued); 495 | isActiveRoundForPoints = true; 496 | CreateTable(); 497 | config = LoadOrCreateConfig(); 498 | LoadRanksConfig(); 499 | LoadTagSettings(); 500 | weaponPointsConfig = LoadWeaponPointsConfig(); 501 | 502 | CreateDbConfigIfNotExists(); 503 | dbConfig = DatabaseConfig.ReadFromJsonFile(Path.Combine(ModuleDirectory, DbConfigFileName)); 504 | } 505 | private void ClearAllPendingActions() 506 | { 507 | lock (_pendingActions) 508 | { 509 | _pendingActions.Clear(); 510 | } 511 | } 512 | private void OnMapEnd() 513 | { 514 | ClearAllPendingActions(); 515 | } 516 | 517 | private HookResult OnHostageFollows(EventHostageFollows hostageFollowsEvent, GameEventInfo info) 518 | { 519 | if (GetActivePlayerCount() < config.MinPlayersForExperience) 520 | { 521 | return HookResult.Continue; 522 | } 523 | 524 | if (isWarmup) 525 | { 526 | return HookResult.Continue; 527 | } 528 | 529 | if (!isActiveRoundForPoints) return HookResult.Continue; 530 | 531 | var playerSteamId64 = hostageFollowsEvent.Userid.SteamID.ToString(); 532 | var playerSteamId = ConvertSteamID64ToSteamID(playerSteamId64); 533 | 534 | if (config.PointsForHostageFollows != 0) 535 | { 536 | var pointsTask = AddOrRemovePoints(playerSteamId, config.PointsForHostageFollows, hostageFollowsEvent.Userid, config.HostageFollowsMessage, config.HostageFollowsMessageColor); 537 | } 538 | 539 | return HookResult.Continue; 540 | } 541 | private HookResult OnHostageStopsFollowing(EventHostageStopsFollowing hostageStopsFollowingEvent, GameEventInfo info) 542 | { 543 | if (GetActivePlayerCount() < config.MinPlayersForExperience) 544 | { 545 | return HookResult.Continue; 546 | } 547 | 548 | if (isWarmup) 549 | { 550 | return HookResult.Continue; 551 | } 552 | 553 | if (!isActiveRoundForPoints) return HookResult.Continue; 554 | 555 | var playerSteamId64 = hostageStopsFollowingEvent.Userid.SteamID.ToString(); 556 | var playerSteamId = ConvertSteamID64ToSteamID(playerSteamId64); 557 | 558 | if (config.PointsForHostageStopsFollowing != 0) 559 | { 560 | var pointsTask = AddOrRemovePoints(playerSteamId, config.PointsForHostageStopsFollowing, hostageStopsFollowingEvent.Userid, config.HostageStopsFollowingMessage, config.HostageStopsFollowingMessageColor); 561 | } 562 | 563 | return HookResult.Continue; 564 | } 565 | private HookResult OnHostageRescued(EventHostageRescued hostageRescuedEvent, GameEventInfo info) 566 | { 567 | if (GetActivePlayerCount() < config.MinPlayersForExperience) 568 | { 569 | return HookResult.Continue; 570 | } 571 | 572 | if (isWarmup) 573 | { 574 | return HookResult.Continue; 575 | } 576 | 577 | if (!isActiveRoundForPoints) return HookResult.Continue; 578 | 579 | var playerSteamId64 = hostageRescuedEvent.Userid.SteamID.ToString(); 580 | var playerSteamId = ConvertSteamID64ToSteamID(playerSteamId64); 581 | 582 | if (config.PointsForHostageRescued != 0) 583 | { 584 | var pointsTask = AddOrRemovePoints(playerSteamId, config.PointsForHostageRescued, hostageRescuedEvent.Userid, config.HostageRescuedMessage, config.HostageRescuedMessageColor); 585 | } 586 | 587 | return HookResult.Continue; 588 | } 589 | 590 | private const string TagSettingsFileName = "tags.json"; 591 | public class TagSettings 592 | { 593 | public HashSet DisabledTags { get; set; } = new HashSet(); 594 | } 595 | private TagSettings LoadTagSettings() 596 | { 597 | var filePath = Path.Combine(ModuleDirectory, TagSettingsFileName); 598 | if (File.Exists(filePath)) 599 | { 600 | var json = File.ReadAllText(filePath); 601 | return JsonSerializer.Deserialize(json) ?? new TagSettings(); 602 | } 603 | return new TagSettings(); 604 | } 605 | 606 | private void SaveTagSettings(TagSettings settings) 607 | { 608 | var filePath = Path.Combine(ModuleDirectory, TagSettingsFileName); 609 | var json = JsonSerializer.Serialize(settings, new JsonSerializerOptions { WriteIndented = true }); 610 | File.WriteAllText(filePath, json); 611 | } 612 | 613 | private HookResult OnBombPickup(EventBombPickup bombPickupEvent, GameEventInfo info) 614 | { 615 | if (GetActivePlayerCount() < config.MinPlayersForExperience) 616 | { 617 | return HookResult.Continue; 618 | } 619 | 620 | if (isWarmup) 621 | { 622 | return HookResult.Continue; 623 | } 624 | 625 | var pickerSteamId64 = bombPickupEvent.Userid.SteamID.ToString(); 626 | var pickerSteamId = ConvertSteamID64ToSteamID(pickerSteamId64); 627 | Console.WriteLine($"Bomb Pickup by {pickerSteamId}"); 628 | 629 | if (config.PointsForBombPickup != 0) 630 | { 631 | string BombPickupMessageColor = ReplaceColorPlaceholders(config.BombPickupMessageColor); 632 | var pointsTask = AddOrRemovePoints(pickerSteamId, config.PointsForBombPickup, bombPickupEvent.Userid, config.BombPickupMessage, BombPickupMessageColor); 633 | } 634 | 635 | return HookResult.Continue; 636 | } 637 | 638 | private HookResult OnBombDropped(EventBombDropped bombDroppedEvent, GameEventInfo info) 639 | { 640 | if (GetActivePlayerCount() < config.MinPlayersForExperience) 641 | { 642 | return HookResult.Continue; 643 | } 644 | 645 | var dropperSteamId64 = bombDroppedEvent.Userid.SteamID.ToString(); 646 | var dropperSteamId = ConvertSteamID64ToSteamID(dropperSteamId64); 647 | 648 | if (config.PointsForBombDropping != 0) 649 | { 650 | string BombDroppingMessageColor = ReplaceColorPlaceholders(config.BombDroppingMessageColor); 651 | var pointsTask = AddOrRemovePoints(dropperSteamId, config.PointsForBombDropping, bombDroppedEvent.Userid, config.BombDroppingMessage, BombDroppingMessageColor); 652 | } 653 | 654 | return HookResult.Continue; 655 | } 656 | private HookResult OnBombPlanted(EventBombPlanted bombPlantedEvent, GameEventInfo info) 657 | { 658 | if (GetActivePlayerCount() < config.MinPlayersForExperience) 659 | { 660 | return HookResult.Continue; 661 | } 662 | 663 | var planterSteamId64 = bombPlantedEvent.Userid.SteamID.ToString(); 664 | var planterSteamId = ConvertSteamID64ToSteamID(planterSteamId64); 665 | 666 | if (config.PointsForBombPlanting != 0) 667 | { 668 | string BombPlantingMessageColor = ReplaceColorPlaceholders(config.BombPlantingMessageColor); 669 | var pointsTask = AddOrRemovePoints(planterSteamId, config.PointsForBombPlanting, bombPlantedEvent.Userid, config.BombPlantingMessage, BombPlantingMessageColor); 670 | } 671 | 672 | return HookResult.Continue; 673 | } 674 | Dictionary g_Player = new Dictionary(); 675 | private void OnClientConnected(int playerSlot) 676 | { 677 | var player = Utilities.GetPlayerFromSlot(playerSlot); 678 | var client = player.Index; 679 | g_Player[client] = new PlayerIteam 680 | { 681 | ClanTag = null, 682 | value = 0, 683 | valuechange = 0, 684 | rank = 0 685 | }; 686 | if (player != null && !player.IsBot) 687 | { 688 | var steamId64 = player.SteamID.ToString(); 689 | var steamId = ConvertSteamID64ToSteamID(steamId64); 690 | var playerName = GetPlayerNickname(steamId64); 691 | var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); 692 | 693 | var getdateTask = GetPlayerConnectionAsync(steamId, playerName, currentTime, client, player); 694 | 695 | activePlayers.Add(steamId); 696 | 697 | } 698 | } 699 | private async Task GetPlayerConnectionAsync(string steamId, string playerName, long currentTime, uint client, CCSPlayerController player) 700 | { 701 | try 702 | { 703 | using (var connection = new MySqlConnection(ConnectionString)) 704 | { 705 | await connection.OpenAsync(); 706 | var insertQuery = $"INSERT INTO `{dbConfig.Name}` (steam, name, lastconnect) VALUES (@SteamID, @Name, @LastConnect) ON DUPLICATE KEY UPDATE lastconnect = @LastConnect, name = @Name;"; 707 | await connection.ExecuteAsync(insertQuery, new { SteamID = steamId, Name = playerName, LastConnect = currentTime }); 708 | string query = $"SELECT * FROM `{dbConfig.Name}` WHERE steam = '{steamId}'"; 709 | 710 | var playerdata = await connection.QueryFirstOrDefaultAsync(query); 711 | 712 | Server.NextFrame(() => 713 | { 714 | if (playerdata != null) 715 | { 716 | g_Player[client].value = playerdata.value; 717 | g_Player[client].rank = playerdata.rank; 718 | 719 | var ranksConfig = LoadRanksConfig(); 720 | RankConfig? defaultRank = ranksConfig.FirstOrDefault(r => r.Id == 0); 721 | RankConfig? currentRank = ranksConfig.FirstOrDefault(r => r.Id == g_Player[client].rank); 722 | var rank = currentRank ?? defaultRank; 723 | 724 | if (rank != null && !string.IsNullOrEmpty(rank.ClanTag)) 725 | { 726 | g_Player[client].ClanTag = rank.ClanTag; 727 | player.Clan = rank.ClanTag; 728 | } 729 | } 730 | }); 731 | } 732 | } 733 | catch (Exception ex) 734 | { 735 | Console.WriteLine($"Error in GetPlayerConnectionAsync: {ex.Message}"); 736 | Server.NextFrame(() => Console.WriteLine($"Error processing player data in main thread: {ex.Message}")); 737 | } 738 | } 739 | public class PlayerIteam 740 | { 741 | 742 | public string? ClanTag { get; set; } 743 | public int? value { get; set; } 744 | public int? valuechange { get; set; } 745 | public int? rank { get; set; } 746 | 747 | public PlayerIteam() 748 | { 749 | Init(); 750 | } 751 | 752 | public void Init() 753 | { 754 | ClanTag = null; 755 | value = 0; 756 | valuechange = 0; 757 | rank = 0; 758 | } 759 | } 760 | private HookResult OnRoundStart(EventRoundStart roundStartEvent, GameEventInfo info) 761 | { 762 | if (isWarmup) 763 | { 764 | isActiveRoundForPoints = false; 765 | } 766 | else 767 | { 768 | isActiveRoundForPoints = GetActivePlayerCount() >= config.MinPlayersForExperience; 769 | } 770 | 771 | if (!isActiveRoundForPoints) 772 | { 773 | string message = config.GetActivePlayerCountMsg 774 | .Replace("{MIN_PLAYERS}", config.MinPlayersForExperience.ToString()); 775 | message = ReplaceColorPlaceholders(message); 776 | BroadcastToPlayers(message); 777 | } 778 | 779 | return HookResult.Continue; 780 | } 781 | 782 | private HookResult OnMatchStart(EventRoundAnnounceMatchStart matchStartEvent, GameEventInfo info) 783 | { 784 | isWarmup = false; 785 | return HookResult.Continue; 786 | } 787 | 788 | private HookResult OnWarmupStart(EventRoundAnnounceWarmup warmupStartEvent, GameEventInfo info) 789 | { 790 | isWarmup = true; 791 | return HookResult.Continue; 792 | } 793 | private HookResult OnBombExploded(EventBombExploded eventBombPlanted, GameEventInfo info) 794 | { 795 | if (GetActivePlayerCount() < config.MinPlayersForExperience) 796 | { 797 | return HookResult.Continue; 798 | } 799 | 800 | var planterSteamId64 = eventBombPlanted.Userid.SteamID.ToString(); 801 | var planterSteamId = ConvertSteamID64ToSteamID(planterSteamId64); 802 | 803 | if (config.PointsForBombExploded != 0) 804 | { 805 | string BombExplodedMessageColor = ReplaceColorPlaceholders(config.BombExplodedMessageColor); 806 | 807 | var pointsTask = AddOrRemovePoints(planterSteamId, config.PointsForBombExploded, eventBombPlanted.Userid, config.BombExplodedMessage, BombExplodedMessageColor); 808 | } 809 | 810 | return HookResult.Continue; 811 | } 812 | private HookResult OnBombDefused(EventBombDefused eventBombDefused, GameEventInfo info) 813 | { 814 | if (GetActivePlayerCount() < config.MinPlayersForExperience) 815 | { 816 | return HookResult.Continue; 817 | } 818 | 819 | var defuserSteamId64 = eventBombDefused.Userid.SteamID.ToString(); 820 | var defuserSteamId = ConvertSteamID64ToSteamID(defuserSteamId64); 821 | 822 | if (config.PointsForBombDefusal != 0) 823 | { 824 | string BombDefusalMessageColor = ReplaceColorPlaceholders(config.BombDefusalMessageColor); 825 | 826 | var pointsTask = AddOrRemovePoints(defuserSteamId, config.PointsForBombDefusal, eventBombDefused.Userid, config.BombDefusalMessage, BombDefusalMessageColor); 827 | } 828 | 829 | return HookResult.Continue; 830 | } 831 | 832 | 833 | private void BroadcastToPlayers(string message) 834 | { 835 | foreach (var player in Utilities.FindAllEntitiesByDesignerName("cs_player_controller")) 836 | { 837 | if (player != null && player.IsValid && !player.IsBot && player.TeamNum != (int)CsTeam.Spectator) 838 | { 839 | player.PrintToChat(message); 840 | } 841 | } 842 | } 843 | private PluginConfig LoadOrCreateConfig() 844 | { 845 | var filePath = Path.Combine(ModuleDirectory, "Config.yml"); 846 | if (!File.Exists(filePath)) 847 | { 848 | var defaultConfig = new PluginConfig(); 849 | SaveConfig(defaultConfig, filePath); 850 | return defaultConfig; 851 | } 852 | else 853 | { 854 | var deserializer = new DeserializerBuilder().Build(); 855 | var yaml = File.ReadAllText(filePath); 856 | return deserializer.Deserialize(yaml); 857 | } 858 | } 859 | 860 | private int GetActivePlayerCount() 861 | { 862 | return activePlayers.Count; 863 | } 864 | private async Task UpdateShootsAsync(string steamId) 865 | { 866 | try 867 | { 868 | using (var connection = new MySqlConnection(ConnectionString)) 869 | { 870 | await connection.OpenAsync(); 871 | var updateQuery = $"UPDATE `{dbConfig.Name}` SET shoots = shoots + 1 WHERE steam = @SteamID;"; 872 | await connection.ExecuteAsync(updateQuery, new { SteamID = steamId }); 873 | } 874 | } 875 | catch (Exception ex) 876 | { 877 | Console.WriteLine($"Error in UpdateShootsAsync: {ex.Message}"); 878 | } 879 | } 880 | 881 | private async Task UpdateHitsAsync(string steamId) 882 | { 883 | try 884 | { 885 | using (var connection = new MySqlConnection(ConnectionString)) 886 | { 887 | await connection.OpenAsync(); 888 | var updateQuery = $"UPDATE `{dbConfig.Name}` SET hits = hits + 1 WHERE steam = @SteamID;"; 889 | await connection.ExecuteAsync(updateQuery, new { SteamID = steamId }); 890 | } 891 | } 892 | catch (Exception ex) 893 | { 894 | Console.WriteLine($"Error in UpdateHitsAsync: {ex.Message}"); 895 | } 896 | } 897 | 898 | private HookResult OnWeaponFire(EventWeaponFire fireEvent, GameEventInfo info) 899 | { 900 | var shooterSteamId64 = fireEvent.Userid.SteamID.ToString(); 901 | var shooterSteamId = ConvertSteamID64ToSteamID(shooterSteamId64); 902 | 903 | var updateTask = UpdateShootsAsync(shooterSteamId); 904 | 905 | return HookResult.Continue; 906 | } 907 | 908 | private HookResult OnPlayerHurt(EventPlayerHurt hurtEvent, GameEventInfo info) 909 | { 910 | if (hurtEvent.Attacker != null && IsValidPlayer(hurtEvent.Attacker)) 911 | { 912 | var attackerSteamId64 = hurtEvent.Attacker.SteamID.ToString(); 913 | var attackerSteamId = ConvertSteamID64ToSteamID(attackerSteamId64); 914 | 915 | var updateTask = UpdateHitsAsync(attackerSteamId); 916 | } 917 | 918 | return HookResult.Continue; 919 | } 920 | private async Task UpdatePlayerDisconnectAsync(string steamId, long currentTime) 921 | { 922 | try 923 | { 924 | using (var connection = new MySqlConnection(ConnectionString)) 925 | { 926 | await connection.OpenAsync(); 927 | 928 | var playerData = await connection.QueryFirstOrDefaultAsync($"SELECT lastconnect, playtime FROM `{dbConfig.Name}` WHERE steam = @SteamID", new { SteamID = steamId }); 929 | 930 | if (playerData != null) 931 | { 932 | var sessionTime = currentTime - playerData.lastconnect; 933 | var newPlaytime = playerData.playtime + sessionTime; 934 | 935 | var updateQuery = $"UPDATE `{dbConfig.Name}` SET playtime = @Playtime WHERE steam = @SteamID;"; 936 | await connection.ExecuteAsync(updateQuery, new { SteamID = steamId, Playtime = newPlaytime }); 937 | } 938 | } 939 | } 940 | catch (Exception ex) 941 | { 942 | Console.WriteLine($"Error in UpdatePlayerDisconnectAsync: {ex.Message}"); 943 | } 944 | } 945 | private HookResult OnPlayerDisconnect(EventPlayerDisconnect disconnectEvent, GameEventInfo info) 946 | { 947 | if (disconnectEvent?.Userid != null && !disconnectEvent.Userid.IsBot) 948 | { 949 | var steamId64 = disconnectEvent.Userid.SteamID.ToString(); 950 | var steamId = ConvertSteamID64ToSteamID(steamId64); 951 | var currentTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); 952 | 953 | var disconnectTask = UpdatePlayerDisconnectAsync(steamId, currentTime); 954 | 955 | activePlayers.Remove(steamId); 956 | 957 | foreach (var player in activePlayers) 958 | { 959 | Console.WriteLine("Remaining active player: " + player); 960 | } 961 | } 962 | 963 | return HookResult.Continue; 964 | } 965 | private HookResult OnRoundEnd(EventRoundEnd roundEndEvent, GameEventInfo info) 966 | { 967 | if (GetActivePlayerCount() < config.MinPlayersForExperience) 968 | { 969 | return HookResult.Continue; 970 | } 971 | 972 | if (isWarmup) 973 | { 974 | return HookResult.Continue; 975 | } 976 | 977 | CsTeam winnerTeam = (CsTeam)roundEndEvent.Winner; 978 | 979 | for (int playerIndex = 0; playerIndex <= Server.MaxPlayers; playerIndex++) 980 | { 981 | CCSPlayerController playerController = Utilities.GetPlayerFromUserid(playerIndex); 982 | 983 | if (playerController != null && playerController.IsValid && !playerController.IsBot && HasJoinedTeam(playerController)) 984 | { 985 | CsTeam playerTeam = (CsTeam)playerController.TeamNum; 986 | var steamID = playerController.SteamID.ToString(); 987 | var steamId = ConvertSteamID64ToSteamID(steamID); 988 | 989 | bool isWin = playerTeam == winnerTeam; 990 | 991 | int pointsChange = isWin ? config.PointsPerRoundWin : config.PointsPerRoundLoss; 992 | if (pointsChange != 0) 993 | { 994 | string messageColor = isWin ? ReplaceColorPlaceholders(config.RoundWinMessageColor) : ReplaceColorPlaceholders(config.RoundLossMessageColor); 995 | var pointsTask = AddOrRemovePoints(steamId, pointsChange, playerController, isWin ? config.RoundWinMessage : config.RoundLossMessage, messageColor); 996 | } 997 | 998 | var roundResultTask = UpdateRoundResultAsync(steamId, isWin); 999 | } 1000 | } 1001 | 1002 | return HookResult.Continue; 1003 | } 1004 | 1005 | private bool HasJoinedTeam(CCSPlayerController playerController) 1006 | { 1007 | if (playerController == null || !playerController.IsValid) 1008 | { 1009 | return false; 1010 | } 1011 | 1012 | return playerController.TeamNum == 2 || playerController.TeamNum == 3; 1013 | } 1014 | private HookResult OnPlayerMVP(EventRoundMvp mvpEvent, GameEventInfo info) 1015 | { 1016 | if (GetActivePlayerCount() < config.MinPlayersForExperience) 1017 | { 1018 | return HookResult.Continue; 1019 | } 1020 | var mvpPlayerSteamId64 = mvpEvent.Userid.SteamID.ToString(); 1021 | var mvpPlayerSteamId = ConvertSteamID64ToSteamID(mvpPlayerSteamId64); 1022 | 1023 | if (config.PointsPerMVP != 0) 1024 | { 1025 | string MVPMessageColor = ReplaceColorPlaceholders(config.MVPMessageColor); 1026 | 1027 | var pointsTask = AddOrRemovePoints(mvpPlayerSteamId, config.PointsPerMVP, mvpEvent.Userid, config.MVPMessage, MVPMessageColor); 1028 | } 1029 | 1030 | return HookResult.Continue; 1031 | } 1032 | private async Task UpdateRoundResultAsync(string steamId, bool isWin) 1033 | { 1034 | try 1035 | { 1036 | using (var connection = new MySqlConnection(ConnectionString)) 1037 | { 1038 | await connection.OpenAsync(); 1039 | 1040 | string columnToUpdate = isWin ? "round_win" : "round_lose"; 1041 | var updateQuery = $"UPDATE {dbConfig.Name} SET {columnToUpdate} = {columnToUpdate} + 1 WHERE steam = @SteamID;"; 1042 | await connection.ExecuteAsync(updateQuery, new { SteamID = steamId }); 1043 | } 1044 | } 1045 | catch (Exception ex) 1046 | { 1047 | Console.WriteLine($"Error in UpdatePlayerConnectionAsync: {ex.Message}"); 1048 | } 1049 | } 1050 | private HookResult OnPlayerDeath(EventPlayerDeath deathEvent, GameEventInfo info) 1051 | { 1052 | if ((deathEvent?.Userid?.IsBot ?? true) && !config.GivePointsForBotKills) 1053 | { 1054 | return HookResult.Continue; 1055 | } 1056 | 1057 | if (isWarmup) 1058 | { 1059 | return HookResult.Continue; 1060 | } 1061 | 1062 | if (GetActivePlayerCount() < config.MinPlayersForExperience) 1063 | { 1064 | return HookResult.Continue; 1065 | } 1066 | 1067 | try 1068 | { 1069 | var victimSteamId64 = deathEvent.Userid.SteamID.ToString(); 1070 | var victimSteamId = ConvertSteamID64ToSteamID(victimSteamId64); 1071 | 1072 | if (deathEvent.Attacker != null && deathEvent.Attacker == deathEvent.Userid) 1073 | { 1074 | if (config.PointsForSuicide != 0) 1075 | { 1076 | string suicideMessageColor = ReplaceColorPlaceholders(config.SuicideMessageColor); 1077 | var pointsTask = AddOrRemovePoints(victimSteamId, config.PointsForSuicide, deathEvent.Userid, config.SuicideMessage, suicideMessageColor); 1078 | } 1079 | } 1080 | else 1081 | { 1082 | if (config.PointsForDeath != 0) 1083 | { 1084 | string DeathMessageColor = ReplaceColorPlaceholders(config.DeathMessageColor); 1085 | var deathPointsTask = AddOrRemovePoints(victimSteamId, config.PointsForDeath, deathEvent.Userid, config.DeathMessage, DeathMessageColor); 1086 | } 1087 | var updateKillsOrDeathsTask = UpdateKillsOrDeathsAsync(victimSteamId, false); 1088 | 1089 | if (deathEvent.Attacker != null && IsValidPlayer(deathEvent.Attacker)) 1090 | { 1091 | var killerSteamId64 = deathEvent.Attacker.SteamID.ToString(); 1092 | var killerSteamId = ConvertSteamID64ToSteamID(killerSteamId64); 1093 | 1094 | if (config.PointsForKill != 0) 1095 | { 1096 | string KillMessageColor = ReplaceColorPlaceholders(config.KillMessageColor); 1097 | var killPointsTask = AddOrRemovePoints(killerSteamId, config.PointsForKill, deathEvent.Attacker, config.KillMessage, KillMessageColor); 1098 | } 1099 | var updateKillsTask = UpdateKillsOrDeathsAsync(killerSteamId, true); 1100 | 1101 | if (deathEvent.Weapon == "awp" && deathEvent.Noscope && config.PointsForNoScopeAWP != 0) 1102 | { 1103 | string NoScopeAWPMessageColor = ReplaceColorPlaceholders(config.NoScopeAWPMessageColor); 1104 | var noScopeTask = AddOrRemovePoints(killerSteamId, config.PointsForNoScopeAWP, deathEvent.Attacker, config.NoScopeAWPMessage, NoScopeAWPMessageColor); 1105 | } 1106 | 1107 | if (deathEvent.Headshot && config.PointsForHeadshot != 0) 1108 | { 1109 | string HeadshotMessageColor = ReplaceColorPlaceholders(config.HeadshotMessageColor); 1110 | var headshotPointsTask = AddOrRemovePoints(killerSteamId, config.PointsForHeadshot, deathEvent.Attacker, config.HeadshotMessage, HeadshotMessageColor); 1111 | var updateHeadshotsTask = UpdateHeadshotsAsync(killerSteamId); 1112 | } 1113 | 1114 | if (deathEvent.Penetrated > 0 && config.PointsForWallbang != 0) 1115 | { 1116 | string wallbangMessageColor = ReplaceColorPlaceholders(config.WallbangMessageColor); 1117 | var wallbangPointsTask = AddOrRemovePoints(killerSteamId, config.PointsForWallbang, deathEvent.Attacker, config.WallbangMessage, wallbangMessageColor); 1118 | } 1119 | 1120 | if (deathEvent.Thrusmoke && config.PointsForKillThroughSmoke != 0) 1121 | { 1122 | string messageColor = ReplaceColorPlaceholders(config.KillThroughSmokeMessageColor); 1123 | var pointsTask = AddOrRemovePoints(killerSteamId, config.PointsForKillThroughSmoke, deathEvent.Attacker, config.KillThroughSmokeMessage, messageColor); 1124 | } 1125 | 1126 | if (deathEvent.Attackerblind && config.PointsForBlindKill != 0) 1127 | { 1128 | string messageColor = ReplaceColorPlaceholders(config.BlindKillMessageColor); 1129 | var pointsTask = AddOrRemovePoints(killerSteamId, config.PointsForBlindKill, deathEvent.Attacker, config.BlindKillMessage, messageColor); 1130 | } 1131 | 1132 | var killerWeapon = deathEvent.Weapon.ToLowerInvariant(); 1133 | WeaponPoints weaponConfig; 1134 | 1135 | if (killerWeapon.Contains("knife")) 1136 | { 1137 | weaponConfig = weaponPointsConfig.FirstOrDefault(wp => wp.WeaponName.ToLowerInvariant() == "knife"); 1138 | } 1139 | else 1140 | { 1141 | weaponConfig = weaponPointsConfig.FirstOrDefault(wp => wp.WeaponName.ToLowerInvariant() == killerWeapon); 1142 | } 1143 | 1144 | if (weaponConfig != null) 1145 | { 1146 | string messageColor = ReplaceColorPlaceholders(weaponConfig.MessageColor); 1147 | var pointsTask = AddOrRemovePoints(killerSteamId, weaponConfig.Points, deathEvent.Attacker, weaponConfig.KillMessage, messageColor); 1148 | } 1149 | } 1150 | if (deathEvent.Assister != null && IsValidPlayer(deathEvent.Assister) && config.PointsForAssist != 0) 1151 | { 1152 | var assisterSteamId64 = deathEvent.Assister.SteamID.ToString(); 1153 | var assisterSteamId = ConvertSteamID64ToSteamID(assisterSteamId64); 1154 | 1155 | string AssistMessageColor = ReplaceColorPlaceholders(config.AssistMessageColor); 1156 | var assistPointsTask = AddOrRemovePoints(assisterSteamId, config.PointsForAssist, deathEvent.Assister, config.AssistMessage, AssistMessageColor); 1157 | var updateAssistsTask = UpdateAssistsAsync(assisterSteamId); 1158 | } 1159 | } 1160 | } 1161 | catch (Exception ex) 1162 | { 1163 | Console.WriteLine("Exception in OnPlayerDeath: " + ex.Message); 1164 | } 1165 | return HookResult.Continue; 1166 | } 1167 | 1168 | private bool IsValidPlayer(CCSPlayerController player) 1169 | { 1170 | return player != null && player.IsValid && !player.IsBot; 1171 | } 1172 | 1173 | private async Task UpdateHeadshotsAsync(string steamId) 1174 | { 1175 | try 1176 | { 1177 | using (var connection = new MySqlConnection(ConnectionString)) 1178 | { 1179 | await connection.OpenAsync(); 1180 | var updateQuery = $"UPDATE `{dbConfig.Name}` SET headshots = headshots + 1 WHERE steam = @SteamID;"; 1181 | await connection.ExecuteAsync(updateQuery, new { SteamID = steamId }); 1182 | } 1183 | } 1184 | catch (Exception ex) 1185 | { 1186 | Console.WriteLine($"Error in UpdateHeadshotsAsync: {ex.Message}"); 1187 | } 1188 | } 1189 | 1190 | private async Task UpdateAssistsAsync(string steamId) 1191 | { 1192 | try 1193 | { 1194 | using (var connection = new MySqlConnection(ConnectionString)) 1195 | { 1196 | await connection.OpenAsync(); 1197 | var updateQuery = $"UPDATE `{dbConfig.Name}` SET assists = assists + 1 WHERE steam = @SteamID;"; 1198 | await connection.ExecuteAsync(updateQuery, new { SteamID = steamId }); 1199 | } 1200 | } 1201 | catch (Exception ex) 1202 | { 1203 | Console.WriteLine($"Error in UpdateAssistsAsync: {ex.Message}"); 1204 | } 1205 | } 1206 | private async Task UpdateKillsOrDeathsAsync(string steamId, bool isKill) 1207 | { 1208 | try 1209 | { 1210 | using (var connection = new MySqlConnection(ConnectionString)) 1211 | { 1212 | await connection.OpenAsync(); 1213 | string columnToUpdate = isKill ? "kills" : "deaths"; 1214 | var updateQuery = $"UPDATE `{dbConfig.Name}` SET `{columnToUpdate}` = `{columnToUpdate}` + 1 WHERE steam = @SteamID;"; 1215 | await connection.ExecuteAsync(updateQuery, new { SteamID = steamId }); 1216 | } 1217 | } 1218 | catch (Exception ex) 1219 | { 1220 | Console.WriteLine($"Error in UpdateKillsOrDeathsAsync: {ex.Message}"); 1221 | } 1222 | } 1223 | private async Task UpdatePlayerPointsAsync(string steamId, int points, uint client) 1224 | { 1225 | int updatedPoints = 0; 1226 | updatedPoints = (int)(g_Player[client].value + points); 1227 | if (updatedPoints < 0) points = 0; 1228 | 1229 | try 1230 | { 1231 | using (var connection = new MySqlConnection(ConnectionString)) 1232 | { 1233 | await connection.OpenAsync(); 1234 | using (var transaction = await connection.BeginTransactionAsync()) 1235 | { 1236 | var updateQuery = $"UPDATE `{dbConfig.Name}` SET value = value + @NewPoints WHERE steam = @SteamID;"; 1237 | 1238 | await connection.ExecuteAsync(updateQuery, new { NewPoints = points, SteamID = steamId }, transaction); 1239 | 1240 | await transaction.CommitAsync(); 1241 | } 1242 | } 1243 | } 1244 | catch (Exception ex) 1245 | { 1246 | Console.WriteLine($"Error in UpdatePlayerPointsAsync: {ex.Message}"); 1247 | } 1248 | 1249 | return updatedPoints; 1250 | } 1251 | private int AddOrRemovePoints(string steamId, int points, CCSPlayerController playerController, string reason, string messageColor) 1252 | { 1253 | var client = playerController.Index; 1254 | 1255 | if (string.IsNullOrEmpty(steamId)) 1256 | { 1257 | return 0; 1258 | } 1259 | 1260 | if (playerController != null && !playerController.IsBot) 1261 | { 1262 | if (config.EnableSpecialNicknameBonus && playerController.PlayerName.Contains(config.SpecialNicknameContains, StringComparison.OrdinalIgnoreCase)) 1263 | { 1264 | if (points > 0 && config.BonusMultiplierForSpecialNickname > 1) 1265 | { 1266 | points = (int)(points * config.BonusMultiplierForSpecialNickname); 1267 | } 1268 | } 1269 | } 1270 | 1271 | int updatedPoints = (int)(g_Player[client].value + points); 1272 | if (updatedPoints < 0) updatedPoints = 0; 1273 | _ = UpdatePlayerPointsAsync(steamId, points, client); 1274 | g_Player[playerController.Index].value = updatedPoints; 1275 | 1276 | 1277 | Action chatUpdateAction = () => 1278 | { 1279 | if (playerController != null && playerController.IsValid && !playerController.IsBot) 1280 | { 1281 | string sign = points >= 0 ? "+" : "-"; 1282 | string rawMessage = config.PointsChangeMessage 1283 | .Replace("{COLOR}", messageColor) 1284 | .Replace("{POINTS}", updatedPoints.ToString()) 1285 | .Replace("{SIGN}", sign) 1286 | .Replace("{CHANGE_POINTS}", Math.Abs(points).ToString()) 1287 | .Replace("{REASON}", reason); 1288 | 1289 | string formattedMessage = ReplaceColorPlaceholders(rawMessage); 1290 | playerController.PrintToChat(formattedMessage); 1291 | } 1292 | }; 1293 | 1294 | lock (_pendingActions) 1295 | { 1296 | _pendingActions.Add(chatUpdateAction); 1297 | } 1298 | 1299 | Server.NextFrame(() => 1300 | { 1301 | lock (_pendingActions) 1302 | { 1303 | chatUpdateAction(); 1304 | _pendingActions.Remove(chatUpdateAction); 1305 | } 1306 | }); 1307 | 1308 | CheckAndUpdateRankAsync(steamId, updatedPoints); 1309 | 1310 | return updatedPoints; 1311 | } 1312 | private async Task CheckAndUpdateRankAsync(string steamId, int updatedPoints) 1313 | { 1314 | var ranksConfig = LoadRanksConfig(); 1315 | 1316 | var newRankIndex = -1; 1317 | 1318 | for (int i = 0; i < ranksConfig.Count; i++) 1319 | { 1320 | if (updatedPoints >= ranksConfig[i].MinExperience) 1321 | { 1322 | newRankIndex = i; 1323 | } 1324 | else 1325 | { 1326 | break; 1327 | } 1328 | } 1329 | 1330 | if (newRankIndex != -1) 1331 | { 1332 | var newRank = ranksConfig[newRankIndex]; 1333 | 1334 | int currentRankId = await GetCurrentRankId(steamId); 1335 | if (currentRankId != newRank.Id) 1336 | { 1337 | bool isRankUpdated = await UpdatePlayerRankAsync(steamId, newRank.Id); 1338 | 1339 | if (isRankUpdated) 1340 | { 1341 | bool isRankUp = newRank.Id > currentRankId; 1342 | NotifyPlayerOfRankChange(steamId, newRank.Name, isRankUp); 1343 | 1344 | string steamId64 = ConvertSteamIDToSteamID64(steamId); 1345 | foreach (var player in Utilities.FindAllEntitiesByDesignerName("cs_player_controller")) 1346 | { 1347 | if (player != null && player.IsValid && !player.IsBot && player.SteamID.ToString() == steamId64) 1348 | { 1349 | Server.NextFrame(() => 1350 | { 1351 | player.Clan = $"[{Regex.Replace(newRank.Name, @"\{[A-Za-z]+}", "")}]"; 1352 | Utilities.SetStateChanged(player, "CCSPlayerController", "m_szClan"); 1353 | }); 1354 | } 1355 | } 1356 | 1357 | 1358 | return true; 1359 | } 1360 | 1361 | 1362 | } 1363 | } 1364 | 1365 | return false; 1366 | } 1367 | private async Task GetCurrentRankId(string steamId) 1368 | { 1369 | try 1370 | { 1371 | using (var connection = new MySqlConnection(ConnectionString)) 1372 | { 1373 | await connection.OpenAsync(); 1374 | var currentRankQuery = $"SELECT `rank` FROM `{dbConfig.Name}` WHERE `steam` = @SteamID;"; 1375 | var rankId = await connection.ExecuteScalarAsync(currentRankQuery, new { SteamID = steamId }); 1376 | return rankId; 1377 | } 1378 | } 1379 | catch (Exception ex) 1380 | { 1381 | Console.WriteLine($"[GetCurrentRankId] Error fetching rank for {steamId}: {ex.Message}"); 1382 | return -1; 1383 | } 1384 | } 1385 | 1386 | 1387 | private async Task UpdatePlayerRankAsync(string steamId, int newRankId) 1388 | { 1389 | try 1390 | { 1391 | using (var connection = new MySqlConnection(ConnectionString)) 1392 | { 1393 | await connection.OpenAsync(); 1394 | var updateRankQuery = $"UPDATE `{dbConfig.Name}` SET `rank` = @NewRankId WHERE `steam` = @SteamID;"; 1395 | var affectedRows = await connection.ExecuteAsync(updateRankQuery, new { NewRankId = newRankId, SteamID = steamId }); 1396 | return affectedRows > 0; 1397 | } 1398 | } 1399 | catch (Exception ex) 1400 | { 1401 | Console.WriteLine($"Error in UpdatePlayerRankAsync: {ex.Message}"); 1402 | return false; 1403 | } 1404 | } 1405 | private List LoadWeaponPointsConfig() 1406 | { 1407 | var filePath = Path.Combine(ModuleDirectory, "Weapons.yml"); 1408 | 1409 | if (!File.Exists(filePath)) 1410 | { 1411 | var defaultWeaponPoints = new List 1412 | { 1413 | new WeaponPoints { WeaponName = "knife", Points = 10, MessageColor = "{LightYellow}", KillMessage = "убийство ножом" }, 1414 | new WeaponPoints { WeaponName = "awp", Points = 5, MessageColor = "{Blue}", KillMessage = "точный выстрел из AWP" } 1415 | }; 1416 | 1417 | var serializer = new SerializerBuilder().Build(); 1418 | var yaml = serializer.Serialize(defaultWeaponPoints); 1419 | File.WriteAllText(filePath, yaml); 1420 | 1421 | return defaultWeaponPoints; 1422 | } 1423 | 1424 | var deserializer = new DeserializerBuilder().Build(); 1425 | var yamlContents = File.ReadAllText(filePath); 1426 | var weaponPoints = deserializer.Deserialize>(yamlContents) ?? new List(); 1427 | 1428 | Console.WriteLine("Загружена конфигурация оружия:"); 1429 | foreach (var weaponPoint in weaponPoints) 1430 | { 1431 | Console.WriteLine($"Оружие: {weaponPoint.WeaponName}, Очки: {weaponPoint.Points}, Цвет сообщения: {weaponPoint.MessageColor}, Сообщение об убийстве: {weaponPoint.KillMessage}"); 1432 | } 1433 | 1434 | return weaponPoints; 1435 | } 1436 | private void NotifyPlayerOfRankChange(string steamId, string newRankName, bool isRankUp) 1437 | { 1438 | Server.NextFrame(() => 1439 | { 1440 | string steamId64 = ConvertSteamIDToSteamID64(steamId); 1441 | string message = isRankUp ? config.RankUpMessage.Replace("{RANK_NAME}", newRankName) 1442 | : config.RankDownMessage.Replace("{RANK_NAME}", newRankName); 1443 | 1444 | foreach (var player in Utilities.FindAllEntitiesByDesignerName("cs_player_controller")) 1445 | { 1446 | if (player != null && player.IsValid && !player.IsBot && player.SteamID.ToString() == steamId64) 1447 | { 1448 | player.PrintToCenter(message); 1449 | break; 1450 | } 1451 | } 1452 | }); 1453 | } 1454 | private string ConvertSteamIDToSteamID64(string steamID) 1455 | { 1456 | if (string.IsNullOrEmpty(steamID) || !steamID.StartsWith("STEAM_")) 1457 | { 1458 | return null; 1459 | } 1460 | 1461 | try 1462 | { 1463 | string[] split = steamID.Replace("STEAM_", "").Split(':'); 1464 | long steamID64 = 76561197960265728 + Convert.ToInt64(split[2]) * 2 + Convert.ToInt64(split[1]); 1465 | return steamID64.ToString(); 1466 | } 1467 | catch (Exception ex) 1468 | { 1469 | Console.WriteLine($"[ConvertSteamIDToSteamID64] Ошибка при конвертации SteamID: {ex.Message}"); 1470 | return null; 1471 | } 1472 | } 1473 | private RankConfig? GetCurrentRank(string steamID64) 1474 | { 1475 | var steamID = ConvertSteamID64ToSteamID(steamID64); 1476 | if (steamID == null) 1477 | { 1478 | Console.WriteLine("Invalid SteamID64 format."); 1479 | return null; 1480 | } 1481 | 1482 | var ranksConfig = LoadRanksConfig(); 1483 | 1484 | using (var connection = new MySqlConnection(ConnectionString)) 1485 | { 1486 | connection.Open(); 1487 | var query = $"SELECT rank FROM {dbConfig.Name} WHERE steam = @SteamID;"; 1488 | var rankId = connection.QueryFirstOrDefault(query, new { SteamID = steamID }); 1489 | 1490 | RankConfig? defaultRank = ranksConfig.FirstOrDefault(r => r.Id == 0); 1491 | RankConfig? currentRank = ranksConfig.FirstOrDefault(r => r.Id == rankId); 1492 | 1493 | return currentRank ?? defaultRank; 1494 | } 1495 | } 1496 | private string GetPlayerNickname(string steamID) 1497 | { 1498 | var player = FindPlayerBySteamID(steamID); 1499 | if (player != null) 1500 | { 1501 | return player.PlayerName; 1502 | } 1503 | return "Unkown"; 1504 | } 1505 | 1506 | private CCSPlayerController FindPlayerBySteamID(string steamID) 1507 | { 1508 | foreach (var player in Utilities.FindAllEntitiesByDesignerName("cs_player_controller")) 1509 | { 1510 | if (player != null && player.IsValid && !player.IsBot && player.SteamID.ToString() == steamID) 1511 | { 1512 | return player; 1513 | } 1514 | } 1515 | return null; 1516 | } 1517 | private CCSPlayerController FindPlayerByUserId(int userId) 1518 | { 1519 | foreach (var player in Utilities.FindAllEntitiesByDesignerName("cs_player_controller")) 1520 | { 1521 | if (player != null && player.IsValid && !player.IsBot && player.UserId == userId) 1522 | { 1523 | return player; 1524 | } 1525 | } 1526 | return null; 1527 | } 1528 | 1529 | private void CreateDbConfigIfNotExists() 1530 | { 1531 | string configFilePath = Path.Combine(ModuleDirectory, DbConfigFileName); 1532 | if (!File.Exists(configFilePath)) 1533 | { 1534 | var config = new DatabaseConfig 1535 | { 1536 | DbHost = "YourHost", 1537 | DbUser = "YourUser", 1538 | DbPassword = "YourPassword", 1539 | DbName = "YourDatabase", 1540 | DbPort = "3306" 1541 | }; 1542 | 1543 | string jsonConfig = JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true }); 1544 | File.WriteAllText(configFilePath, jsonConfig); 1545 | Console.WriteLine("Database configuration file created."); 1546 | } 1547 | } 1548 | private string ReplaceColorPlaceholders(string message) 1549 | { 1550 | if (message.Contains('{')) 1551 | { 1552 | string modifiedValue = message; 1553 | foreach (FieldInfo field in typeof(ChatColors).GetFields()) 1554 | { 1555 | string pattern = $"{{{field.Name}}}"; 1556 | if (message.Contains(pattern, StringComparison.OrdinalIgnoreCase)) 1557 | { 1558 | modifiedValue = modifiedValue.Replace(pattern, field.GetValue(null).ToString(), StringComparison.OrdinalIgnoreCase); 1559 | } 1560 | } 1561 | return modifiedValue; 1562 | } 1563 | 1564 | return message; 1565 | } 1566 | 1567 | private async Task GetPlayerStatsAsync(string steamId) 1568 | { 1569 | using (var connection = new MySqlConnection(ConnectionString)) 1570 | { 1571 | await connection.OpenAsync(); 1572 | var playerData = await connection.QueryFirstOrDefaultAsync($@" 1573 | SELECT p.rank, p.value as points, p.kills, p.deaths, p.playtime, 1574 | (SELECT COUNT(*) FROM `{dbConfig.Name}` WHERE value > p.value) + 1 as place, 1575 | (SELECT COUNT(*) FROM `{dbConfig.Name}`) as totalPlayers 1576 | FROM `{dbConfig.Name}` p 1577 | WHERE p.steam = @SteamID;", new { SteamID = steamId }); 1578 | 1579 | if (playerData == null) 1580 | { 1581 | throw new InvalidOperationException("Player data not found for the given SteamID."); 1582 | } 1583 | 1584 | var ranksConfig = LoadRanksConfig(); 1585 | var rankConfig = ranksConfig.FirstOrDefault(r => r.Id == Convert.ToInt32(playerData.rank)); 1586 | if (rankConfig == null) 1587 | { 1588 | throw new InvalidOperationException("Rank configuration not found for the given rank ID."); 1589 | } 1590 | 1591 | var kdr = (playerData.deaths > 0) ? (double)playerData.kills / playerData.deaths : playerData.kills; 1592 | 1593 | int place = Convert.ToInt32(playerData.place); 1594 | int totalPlayers = Convert.ToInt32(playerData.totalPlayers); 1595 | 1596 | return new PlayerStats 1597 | { 1598 | RankName = rankConfig.Name, 1599 | Place = place, 1600 | TotalPlayers = totalPlayers, 1601 | Points = Convert.ToInt32(playerData.points), 1602 | Kills = playerData.kills, 1603 | PlayTime = playerData.playtime, 1604 | Deaths = playerData.deaths, 1605 | KDR = kdr 1606 | }; 1607 | } 1608 | } 1609 | 1610 | public class PlayerResetInfo 1611 | { 1612 | public DateTime LastResetTime { get; set; } 1613 | } 1614 | public class PlayerStats 1615 | { 1616 | public string RankName { get; set; } 1617 | public int Place { get; set; } 1618 | public int TotalPlayers { get; set; } 1619 | public int Points { get; set; } 1620 | public int Kills { get; set; } 1621 | public int PlayTime { get; set; } 1622 | public int Deaths { get; set; } 1623 | public double KDR { get; set; } 1624 | } 1625 | 1626 | private string FormatTime(int playTimeSeconds) 1627 | { 1628 | TimeSpan timePlayed = TimeSpan.FromSeconds(playTimeSeconds); 1629 | return string.Format(config.TimeFormat, timePlayed.Days, timePlayed.Hours, timePlayed.Minutes); 1630 | } 1631 | 1632 | [ConsoleCommand("rank", "Shows your current rank and statistics")] 1633 | public void OnRankCommand(CCSPlayerController? player, CommandInfo command) 1634 | { 1635 | if (!config.IsRankCommandEnabled) 1636 | { 1637 | return; 1638 | } 1639 | 1640 | if (player == null) return; 1641 | 1642 | var steamID64 = player.SteamID.ToString(); 1643 | var steamID = ConvertSteamID64ToSteamID(steamID64); 1644 | 1645 | var stats = GetPlayerStatsAsync(steamID).GetAwaiter().GetResult(); 1646 | 1647 | string message = config.RankCommandMessage 1648 | .Replace("{RANK_NAME}", stats.RankName) 1649 | .Replace("{PLACE}", stats.Place.ToString()) 1650 | .Replace("{TOTAL_PLAYERS}", stats.TotalPlayers.ToString()) 1651 | .Replace("{POINTS}", stats.Points.ToString()) 1652 | .Replace("{KILLS}", stats.Kills.ToString()) 1653 | .Replace("{DEATHS}", stats.Deaths.ToString()) 1654 | .Replace("{KDR}", stats.KDR.ToString("F2")) 1655 | .Replace("{PLAY_TIME}", FormatTime(stats.PlayTime)); 1656 | 1657 | message = ReplaceColorPlaceholders(message); 1658 | player.PrintToChat(message); 1659 | } 1660 | [ConsoleCommand("top", "Shows top 10 players by points")] 1661 | public void OnTopCommand(CCSPlayerController? player, CommandInfo command) 1662 | { 1663 | if (!config.IsTopCommandEnabled) 1664 | { 1665 | return; 1666 | } 1667 | 1668 | if (player == null) 1669 | { 1670 | Console.WriteLine("This command can only be used by players."); 1671 | return; 1672 | } 1673 | 1674 | try 1675 | { 1676 | using (var connection = new MySqlConnection(ConnectionString)) 1677 | { 1678 | connection.Open(); 1679 | var topPlayersQuery = @" 1680 | SELECT steam, name, value, `rank` 1681 | FROM `" + dbConfig.Name + @"` 1682 | ORDER BY value DESC 1683 | LIMIT 10;"; 1684 | 1685 | var topPlayers = connection.Query(topPlayersQuery).ToList(); 1686 | 1687 | if (topPlayers.Any()) 1688 | { 1689 | var ranksConfig = LoadRanksConfig(); 1690 | 1691 | string introMessage = ReplaceColorPlaceholders(config.TopCommandIntroMessage); 1692 | player.PrintToChat(introMessage); 1693 | 1694 | for (int i = 0; i < topPlayers.Count; i++) 1695 | { 1696 | var topPlayerInfo = topPlayers[i]; 1697 | var rankName = ranksConfig.FirstOrDefault(r => r.Id == topPlayerInfo.rank)?.Name ?? "Unknown Rank"; 1698 | string playerMessage = config.TopCommandPlayerMessage 1699 | .Replace("{INDEX}", (i + 1).ToString()) 1700 | .Replace("{NAME}", topPlayerInfo.name) 1701 | .Replace("{POINTS}", topPlayerInfo.value.ToString()) 1702 | .Replace("{RANK}", rankName); 1703 | playerMessage = ReplaceColorPlaceholders(playerMessage); 1704 | player.PrintToChat(playerMessage); 1705 | } 1706 | } 1707 | else 1708 | { 1709 | string noDataMessage = ReplaceColorPlaceholders(config.TopCommandNoDataMessage); 1710 | player.PrintToChat(noDataMessage); 1711 | } 1712 | } 1713 | } 1714 | catch (Exception ex) 1715 | { 1716 | Console.WriteLine("Exception in OnTopCommand: " + ex.Message); 1717 | string errorMessage = ReplaceColorPlaceholders(config.TopCommandErrorMessage); 1718 | player.PrintToChat(errorMessage); 1719 | } 1720 | } 1721 | [ConsoleCommand("topkills", "Shows top 10 players by kills")] 1722 | public void OnTopKillsCommand(CCSPlayerController? player, CommandInfo command) 1723 | { 1724 | if (!config.IsTopkillsCommandEnabled) 1725 | { 1726 | return; 1727 | } 1728 | 1729 | if (player == null) 1730 | { 1731 | Console.WriteLine("This command can only be used by players."); 1732 | return; 1733 | } 1734 | 1735 | try 1736 | { 1737 | using (var connection = new MySqlConnection(ConnectionString)) 1738 | { 1739 | connection.Open(); 1740 | var topPlayersQuery = @" 1741 | SELECT steam, name, kills 1742 | FROM `" + dbConfig.Name + @"` 1743 | ORDER BY kills DESC 1744 | LIMIT 10;"; 1745 | 1746 | 1747 | var topPlayers = connection.Query(topPlayersQuery).ToList(); 1748 | 1749 | if (topPlayers.Any()) 1750 | { 1751 | string introMessage = ReplaceColorPlaceholders(config.TopKillsCommandIntroMessage); 1752 | player.PrintToChat(introMessage); 1753 | 1754 | for (int i = 0; i < topPlayers.Count; i++) 1755 | { 1756 | var topPlayerInfo = topPlayers[i]; 1757 | string playerMessage = ReplaceColorPlaceholders(config.TopKillsCommandPlayerMessage) 1758 | .Replace("{INDEX}", (i + 1).ToString()) 1759 | .Replace("{NAME}", topPlayerInfo.name) 1760 | .Replace("{KILLS}", topPlayerInfo.kills.ToString()); 1761 | player.PrintToChat(playerMessage); 1762 | } 1763 | } 1764 | else 1765 | { 1766 | player.PrintToChat(ReplaceColorPlaceholders(config.TopKillsCommandNoDataMessage)); 1767 | } 1768 | } 1769 | } 1770 | catch (Exception ex) 1771 | { 1772 | Console.WriteLine("Exception in OnTopKillsCommand: " + ex.Message); 1773 | player.PrintToChat(ReplaceColorPlaceholders(config.TopKillsCommandErrorMessage)); 1774 | } 1775 | } 1776 | [ConsoleCommand("topdeaths", "Shows top 10 players by deaths")] 1777 | public void OnTopDeathsCommand(CCSPlayerController? player, CommandInfo command) 1778 | { 1779 | if (!config.IsTopdeathsCommandEnabled) 1780 | { 1781 | return; 1782 | } 1783 | if (player == null) 1784 | { 1785 | Console.WriteLine("This command can only be used by players."); 1786 | return; 1787 | } 1788 | 1789 | try 1790 | { 1791 | using (var connection = new MySqlConnection(ConnectionString)) 1792 | { 1793 | connection.Open(); 1794 | var topPlayersQuery = $@" 1795 | SELECT steam, name, deaths 1796 | FROM `{dbConfig.Name}` 1797 | ORDER BY deaths DESC 1798 | LIMIT 10;"; 1799 | 1800 | var topPlayers = connection.Query(topPlayersQuery).ToList(); 1801 | 1802 | if (topPlayers.Any()) 1803 | { 1804 | string introMessage = ReplaceColorPlaceholders(config.TopDeathsCommandIntroMessage); 1805 | player.PrintToChat(introMessage); 1806 | 1807 | for (int i = 0; i < topPlayers.Count; i++) 1808 | { 1809 | var topPlayerInfo = topPlayers[i]; 1810 | string playerMessage = ReplaceColorPlaceholders(config.TopDeathsCommandPlayerMessage) 1811 | .Replace("{INDEX}", (i + 1).ToString()) 1812 | .Replace("{NAME}", topPlayerInfo.name) 1813 | .Replace("{DEATHS}", topPlayerInfo.deaths.ToString()); 1814 | player.PrintToChat(playerMessage); 1815 | } 1816 | } 1817 | else 1818 | { 1819 | player.PrintToChat(ReplaceColorPlaceholders(config.TopDeathsCommandNoDataMessage)); 1820 | } 1821 | } 1822 | } 1823 | catch (Exception ex) 1824 | { 1825 | Console.WriteLine("Exception in OnTopDeathsCommand: " + ex.Message); 1826 | player.PrintToChat(ReplaceColorPlaceholders(config.TopDeathsCommandErrorMessage)); 1827 | } 1828 | } 1829 | [ConsoleCommand("topkdr", "Shows top 10 players by KDR")] 1830 | public void OnTopKDRCommand(CCSPlayerController? player, CommandInfo command) 1831 | { 1832 | if (!config.IsTopkdrCommandEnabled) 1833 | { 1834 | return; 1835 | } 1836 | if (player == null) 1837 | { 1838 | Console.WriteLine("This command can only be used by players."); 1839 | return; 1840 | } 1841 | 1842 | try 1843 | { 1844 | using (var connection = new MySqlConnection(ConnectionString)) 1845 | { 1846 | connection.Open(); 1847 | var topPlayersQuery = $@" 1848 | SELECT steam, name, kills, deaths, 1849 | CASE 1850 | WHEN deaths = 0 THEN kills 1851 | ELSE kills / deaths 1852 | END AS kdr 1853 | FROM `{dbConfig.Name}` 1854 | ORDER BY kdr DESC, kills DESC 1855 | LIMIT 10;"; 1856 | 1857 | var topPlayers = connection.Query(topPlayersQuery).ToList(); 1858 | 1859 | if (topPlayers.Any()) 1860 | { 1861 | string introMessage = ReplaceColorPlaceholders(config.TopKDRCommandIntroMessage); 1862 | player.PrintToChat(introMessage); 1863 | 1864 | foreach (var topPlayerInfo in topPlayers) 1865 | { 1866 | string formattedKDR = topPlayerInfo.kdr.ToString("F2"); 1867 | string playerMessage = config.TopKDRCommandPlayerMessage 1868 | .Replace("{INDEX}", (topPlayers.IndexOf(topPlayerInfo) + 1).ToString()) 1869 | .Replace("{NAME}", topPlayerInfo.name) 1870 | .Replace("{KDR}", formattedKDR); 1871 | player.PrintToChat(ReplaceColorPlaceholders(playerMessage)); 1872 | } 1873 | } 1874 | else 1875 | { 1876 | player.PrintToChat(ReplaceColorPlaceholders(config.TopKDRCommandNoDataMessage)); 1877 | } 1878 | } 1879 | } 1880 | catch (Exception ex) 1881 | { 1882 | Console.WriteLine("Exception in OnTopKDRCommand: " + ex.Message); 1883 | player.PrintToChat(ReplaceColorPlaceholders(config.TopKDRCommandErrorMessage)); 1884 | } 1885 | } 1886 | [ConsoleCommand("toptime", "Shows top 10 players by server time")] 1887 | public void OnTopTimeCommand(CCSPlayerController? player, CommandInfo command) 1888 | { 1889 | if (!config.IsToptimeCommandEnabled) 1890 | { 1891 | return; 1892 | } 1893 | if (player == null) 1894 | { 1895 | Console.WriteLine("This command can only be used by players."); 1896 | return; 1897 | } 1898 | 1899 | try 1900 | { 1901 | using (var connection = new MySqlConnection(ConnectionString)) 1902 | { 1903 | connection.Open(); 1904 | var topPlayersQuery = @" 1905 | SELECT steam, name, playtime 1906 | FROM `" + dbConfig.Name + @"` 1907 | ORDER BY playtime DESC 1908 | LIMIT 10;"; 1909 | 1910 | var topPlayers = connection.Query(topPlayersQuery).ToList(); 1911 | 1912 | if (topPlayers.Any()) 1913 | { 1914 | string introMessage = ReplaceColorPlaceholders(config.TopTimeCommandIntroMessage); 1915 | player.PrintToChat(introMessage); 1916 | 1917 | for (int i = 0; i < topPlayers.Count; i++) 1918 | { 1919 | var topPlayerInfo = topPlayers[i]; 1920 | TimeSpan timePlayed = TimeSpan.FromSeconds(topPlayerInfo.playtime); 1921 | string formattedTime = string.Format(config.TopTimeFormat, 1922 | timePlayed.Days, timePlayed.Hours, timePlayed.Minutes); 1923 | string playerMessage = config.TopTimeCommandPlayerMessage 1924 | .Replace("{INDEX}", (i + 1).ToString()) 1925 | .Replace("{NAME}", topPlayerInfo.name) 1926 | .Replace("{TIME}", formattedTime); 1927 | player.PrintToChat(ReplaceColorPlaceholders(playerMessage)); 1928 | } 1929 | } 1930 | else 1931 | { 1932 | player.PrintToChat(ReplaceColorPlaceholders(config.TopTimeCommandNoDataMessage)); 1933 | } 1934 | } 1935 | } 1936 | catch (Exception ex) 1937 | { 1938 | Console.WriteLine("Exception in OnTopTimeCommand: " + ex.Message); 1939 | player.PrintToChat(ReplaceColorPlaceholders(config.TopTimeCommandErrorMessage)); 1940 | } 1941 | } 1942 | 1943 | [ConsoleCommand("resetstats", "Reset your statistics (can be used once every 3 hours)")] 1944 | public void OnResetStatsCommand(CCSPlayerController? player, CommandInfo command) 1945 | { 1946 | if (!config.IsResetstatsCommandEnabled) 1947 | { 1948 | return; 1949 | } 1950 | if (player == null) return; 1951 | 1952 | var steamId64 = player.SteamID.ToString(); 1953 | var steamId = ConvertSteamID64ToSteamID(steamId64); 1954 | 1955 | if (playerResetTimes.TryGetValue(steamId, out var resetInfo)) 1956 | { 1957 | if ((DateTime.UtcNow - resetInfo.LastResetTime).TotalHours < config.ResetStatsCooldownHours) 1958 | { 1959 | string cooldownMessage = ReplaceColorPlaceholders(config.ResetStatsCooldownMessage); 1960 | player.PrintToChat(cooldownMessage); 1961 | return; 1962 | } 1963 | } 1964 | 1965 | ResetPlayerStats(steamId); 1966 | playerResetTimes[steamId] = new PlayerResetInfo { LastResetTime = DateTime.UtcNow }; 1967 | 1968 | string successMessage = ReplaceColorPlaceholders(config.ResetStatsSuccessMessage); 1969 | player.PrintToChat(successMessage); 1970 | } 1971 | 1972 | private void ResetPlayerStats(string steamId) 1973 | { 1974 | try 1975 | { 1976 | using (var connection = new MySqlConnection(ConnectionString)) 1977 | { 1978 | connection.Open(); 1979 | var resetQuery = $"UPDATE `{dbConfig.Name}` SET kills = 0, deaths = 0, `value` = 0, shoots = 0, hits = 0, headshots = 0, assists = 0, round_win = 0, round_lose = 0, playtime = 0 WHERE steam = @SteamID;"; 1980 | int affectedRows = connection.Execute(resetQuery, new { SteamID = steamId }); 1981 | } 1982 | } 1983 | catch (Exception ex) 1984 | { 1985 | Console.WriteLine($"Error updating player stats: {ex.Message}"); 1986 | } 1987 | } 1988 | [ConsoleCommand("rp_reloadconfig", "Reloads the configuration file Config.yml")] 1989 | public void ReloadConfigCommand(CCSPlayerController? player, CommandInfo command) 1990 | { 1991 | if (player == null) 1992 | { 1993 | try 1994 | { 1995 | config = LoadOrCreateConfig(); 1996 | Console.WriteLine("[RankPointsPlugin] Configuration reloaded successfully."); 1997 | } 1998 | catch (Exception ex) 1999 | { 2000 | Console.WriteLine($"[RankPointsPlugin] Error reloading configuration: {ex.Message}"); 2001 | } 2002 | } 2003 | else 2004 | { 2005 | player.PrintToChat("This command is only available from the server console."); 2006 | } 2007 | } 2008 | 2009 | [ConsoleCommand("rp_reloadweapons", "Reloads the configuration file Weapons.yml")] 2010 | public void ReloadWeaponsConfigCommand(CCSPlayerController? player, CommandInfo command) 2011 | { 2012 | if (player == null || player.IsBot) 2013 | { 2014 | try 2015 | { 2016 | weaponPointsConfig = LoadWeaponPointsConfig(); 2017 | Console.WriteLine("[RankPointsPlugin] Weapon configuration reloaded successfully."); 2018 | } 2019 | catch (Exception ex) 2020 | { 2021 | Console.WriteLine($"[RankPointsPlugin] Error reloading weapon configuration: {ex.Message}"); 2022 | } 2023 | } 2024 | else 2025 | { 2026 | player.PrintToChat($"{ChatColors.Red}This command is only available from the server console."); 2027 | } 2028 | } 2029 | [ConsoleCommand("rp_reloadranks", "Reloads the configuration file settings_ranks.yaml")] 2030 | public void ReloadRanksCommand(CCSPlayerController? player, CommandInfo command) 2031 | { 2032 | if (player == null) 2033 | { 2034 | try 2035 | { 2036 | LoadRanksConfig(); 2037 | Console.WriteLine("[RankPointsPlugin] Ranks configuration reloaded successfully."); 2038 | } 2039 | catch (Exception ex) 2040 | { 2041 | Console.WriteLine($"[RankPointsPlugin] Error reloading ranks configuration: {ex.Message}"); 2042 | } 2043 | } 2044 | else 2045 | { 2046 | player.PrintToChat("{Red}This command is only available from the server console."); 2047 | } 2048 | } 2049 | [ConsoleCommand("ranks", "Shows the list of all ranks and the experience required to achieve them")] 2050 | public void OnRanksCommand(CCSPlayerController? player, CommandInfo command) 2051 | { 2052 | if (!config.IsRanksCommandEnabled) 2053 | { 2054 | return; 2055 | } 2056 | if (player == null) 2057 | { 2058 | Console.WriteLine("This command can only be used by players."); 2059 | return; 2060 | } 2061 | 2062 | try 2063 | { 2064 | var ranksConfig = LoadRanksConfig(); 2065 | 2066 | if (ranksConfig.Any()) 2067 | { 2068 | string introMessage = ReplaceColorPlaceholders(config.RanksCommandIntroMessage); 2069 | player.PrintToChat(introMessage); 2070 | 2071 | foreach (var rank in ranksConfig) 2072 | { 2073 | string rankMessage = config.RanksCommandRankMessage 2074 | .Replace("{NAME}", rank.Name) 2075 | .Replace("{EXPERIENCE}", rank.MinExperience.ToString()); 2076 | rankMessage = ReplaceColorPlaceholders(rankMessage); 2077 | player.PrintToChat(rankMessage); 2078 | } 2079 | } 2080 | else 2081 | { 2082 | string noDataMessage = ReplaceColorPlaceholders(config.RanksCommandNoDataMessage); 2083 | player.PrintToChat(noDataMessage); 2084 | } 2085 | } 2086 | catch (Exception ex) 2087 | { 2088 | Console.WriteLine("Exception in OnRanksCommand: " + ex.Message); 2089 | string errorMessage = ReplaceColorPlaceholders(config.RanksCommandErrorMessage); 2090 | player.PrintToChat(errorMessage); 2091 | } 2092 | } 2093 | [ConsoleCommand("lvl", "Shows the list of all available commands and their functions")] 2094 | public void OnLvlCommand(CCSPlayerController? player, CommandInfo command) 2095 | { 2096 | if (!config.IsLvlCommandEnabled) 2097 | { 2098 | return; 2099 | } 2100 | if (player == null) 2101 | { 2102 | Console.WriteLine("This command can only be used by players."); 2103 | return; 2104 | } 2105 | 2106 | string introMessage = ReplaceColorPlaceholders(config.LvlCommandIntroMessage); 2107 | player.PrintToChat(introMessage); 2108 | 2109 | string[] commandDescriptions = new string[] 2110 | { 2111 | ReplaceColorPlaceholders(config.RankCommandDescription), 2112 | ReplaceColorPlaceholders(config.TopCommandDescription), 2113 | ReplaceColorPlaceholders(config.TopKillsCommandDescription), 2114 | ReplaceColorPlaceholders(config.TopDeathsCommandDescription), 2115 | ReplaceColorPlaceholders(config.TopKDRCommandDescription), 2116 | ReplaceColorPlaceholders(config.TopTimeCommandDescription), 2117 | ReplaceColorPlaceholders(config.ResetStatsCommandDescription), 2118 | ReplaceColorPlaceholders(config.RanksCommandDescription), 2119 | ReplaceColorPlaceholders(config.TagRankCommandDescription) 2120 | }; 2121 | 2122 | foreach (var description in commandDescriptions) 2123 | { 2124 | player.PrintToChat(description); 2125 | } 2126 | } 2127 | [ConsoleCommand("tagrank", "Enables or disables displaying your clan tag.")] 2128 | public void OnTagRankCommand(CCSPlayerController? player, CommandInfo command) 2129 | { 2130 | if (!config.IsTagRankCommandEnabled) 2131 | { 2132 | return; 2133 | } 2134 | 2135 | if (player == null) return; 2136 | 2137 | var steamId64 = player.SteamID.ToString(); 2138 | var tagSettings = LoadTagSettings(); 2139 | 2140 | if (tagSettings.DisabledTags.Contains(steamId64)) 2141 | { 2142 | tagSettings.DisabledTags.Remove(steamId64); 2143 | string enabledMessage = ReplaceColorPlaceholders(config.TagRankEnabledMessage); 2144 | player.PrintToChat(enabledMessage); 2145 | } 2146 | else 2147 | { 2148 | tagSettings.DisabledTags.Add(steamId64); 2149 | string disabledMessage = ReplaceColorPlaceholders(config.TagRankDisabledMessage); 2150 | player.PrintToChat(disabledMessage); 2151 | } 2152 | 2153 | SaveTagSettings(tagSettings); 2154 | } 2155 | [ConsoleCommand("rp_resetranks", "Clears a player's statistics. Usage: rp_resetranks ")] 2156 | [CommandHelper(minArgs: 2, usage: " ", whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] 2157 | public void ResetRanksCommand(CCSPlayerController? player, CommandInfo command) 2158 | { 2159 | if (player == null || player.IsBot) 2160 | { 2161 | try 2162 | { 2163 | var steamId64 = command.ArgByIndex(1); 2164 | var steamId = ConvertSteamID64ToSteamID(steamId64); 2165 | var dataType = command.ArgByIndex(2).ToLower(); 2166 | 2167 | if (steamId == null) 2168 | { 2169 | Console.WriteLine("Invalid SteamID64."); 2170 | return; 2171 | } 2172 | 2173 | switch (dataType) 2174 | { 2175 | case "exp": 2176 | ResetPlayerExperience(steamId); 2177 | Console.WriteLine($"[RankPointsPlugin] Experience and rank for player {steamId} (SteamID64: {steamId64}) have been reset."); 2178 | break; 2179 | case "stats": 2180 | ResetPlayerStats(steamId); 2181 | Console.WriteLine($"[RankPointsPlugin] Player statistics for {steamId} (SteamID64: {steamId64}) have been reset."); 2182 | break; 2183 | case "time": 2184 | ResetPlayerPlaytime(steamId); 2185 | Console.WriteLine($"[RankPointsPlugin] Player playtime for {steamId} (SteamID64: {steamId64}) has been reset."); 2186 | break; 2187 | default: 2188 | Console.WriteLine("Invalid data type. Use 'exp', 'stats', or 'time'."); 2189 | break; 2190 | } 2191 | } 2192 | catch (Exception ex) 2193 | { 2194 | Console.WriteLine($"[RankPointsPlugin] An error occurred while resetting statistics: {ex.Message}"); 2195 | } 2196 | } 2197 | else 2198 | { 2199 | player.PrintToChat($"{ChatColors.Red}This command is only available from the server console."); 2200 | } 2201 | } 2202 | private void ResetPlayerExperience(string steamId) 2203 | { 2204 | using (var connection = new MySqlConnection(ConnectionString)) 2205 | { 2206 | connection.Open(); 2207 | var resetQuery = $"UPDATE `{dbConfig.Name}` SET `value` = 0, `rank` = 1 WHERE steam = @SteamID;"; 2208 | connection.Execute(resetQuery, new { SteamID = steamId }); 2209 | } 2210 | } 2211 | 2212 | private void ResetPlayerStats2(string steamId) 2213 | { 2214 | using (var connection = new MySqlConnection(ConnectionString)) 2215 | { 2216 | connection.Open(); 2217 | var resetQuery = $"UPDATE `{dbConfig.Name}` SET kills = 0, deaths = 0, shoots = 0, hits = 0, headshots = 0, assists = 0, round_win = 0, round_lose = 0 WHERE steam = @SteamID;"; 2218 | connection.Execute(resetQuery, new { SteamID = steamId }); 2219 | } 2220 | } 2221 | 2222 | private void ResetPlayerPlaytime(string steamId) 2223 | { 2224 | using (var connection = new MySqlConnection(ConnectionString)) 2225 | { 2226 | connection.Open(); 2227 | var resetQuery = $"UPDATE `{dbConfig.Name}` SET playtime = 0 WHERE steam = @SteamID;"; 2228 | connection.Execute(resetQuery, new { SteamID = steamId }); 2229 | } 2230 | } 2231 | private void CreateTable() 2232 | { 2233 | using (var connection = new MySqlConnection(ConnectionString)) 2234 | { 2235 | connection.Open(); 2236 | 2237 | var createTableQuery = string.Format(SQL_CreateTable, $"{dbConfig.Name}", "", ""); 2238 | connection.Execute(createTableQuery); 2239 | } 2240 | } 2241 | 2242 | private string ConnectionString 2243 | { 2244 | get 2245 | { 2246 | if (dbConfig?.DbHost == null || dbConfig?.DbUser == null || dbConfig?.DbPassword == null || dbConfig?.DbName == null || dbConfig?.DbPort == null) 2247 | throw new InvalidOperationException("Database configuration is not properly set."); 2248 | 2249 | return $"Server={dbConfig.DbHost};Port={dbConfig.DbPort};User ID={dbConfig.DbUser};Password={dbConfig.DbPassword};Database={dbConfig.DbName};"; 2250 | } 2251 | } 2252 | 2253 | private const string SQL_CreateTable = "CREATE TABLE IF NOT EXISTS `{0}` ( `steam` varchar(22){1} PRIMARY KEY, `name` varchar(32){2}, `value` int NOT NULL DEFAULT 0, `rank` int NOT NULL DEFAULT 0, `kills` int NOT NULL DEFAULT 0, `deaths` int NOT NULL DEFAULT 0, `shoots` int NOT NULL DEFAULT 0, `hits` int NOT NULL DEFAULT 0, `headshots` int NOT NULL DEFAULT 0, `assists` int NOT NULL DEFAULT 0, `round_win` int NOT NULL DEFAULT 0, `round_lose` int NOT NULL DEFAULT 0, `playtime` int NOT NULL DEFAULT 0, `lastconnect` int NOT NULL DEFAULT 0);"; 2254 | public override string ModuleAuthor => PluginAuthor; 2255 | public override string ModuleName => PluginName; 2256 | public override string ModuleVersion => PluginVersion; 2257 | } 2258 | public class WeaponPoints 2259 | { 2260 | public string? WeaponName { get; set; } 2261 | public int Points { get; set; } 2262 | public string? MessageColor { get; set; } 2263 | public string? KillMessage { get; set; } 2264 | } 2265 | public class DatabaseConfig 2266 | { 2267 | public string? DbHost { get; set; } 2268 | public string? DbUser { get; set; } 2269 | public string? DbPassword { get; set; } 2270 | public string? DbName { get; set; } 2271 | public string? DbPort { get; set; } 2272 | public string? Name { get; set; } = "lvl_base"; 2273 | 2274 | public static DatabaseConfig ReadFromJsonFile(string filePath) 2275 | { 2276 | string jsonConfig = File.ReadAllText(filePath); 2277 | return JsonSerializer.Deserialize(jsonConfig) ?? new DatabaseConfig(); 2278 | } 2279 | } 2280 | } -------------------------------------------------------------------------------- /EN/source/RanksPoints.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | AnyCPU;x86 6 | true 7 | enable 8 | 9 | 10 | 11 | 12 | ..\CounterStrikeSharp.API.dll 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![GitHub Repo stars](https://img.shields.io/github/stars/ABKAM2023/CS2-RanksPoints?style=for-the-badge) 2 | ![GitHub issues](https://img.shields.io/github/issues/ABKAM2023/CS2-RanksPoints?style=for-the-badge) 3 | ![GitHub contributors](https://img.shields.io/github/contributors/ABKAM2023/CS2-RanksPoints?style=for-the-badge) 4 | ![GitHub all releases](https://img.shields.io/github/downloads/ABKAM2023/CS2-RanksPoints/total?style=for-the-badge) 5 | 6 | I'm sorry for my poor English. 7 | 8 | # EN 9 | **To facilitate easier assistance, I have created a dedicated Discord server. You can join it using the following link: [https://discord.gg/XjfUGMdCgn](https://discord.gg/XjfUGMdCgn)** 10 | 11 | The plugin was developed inspired by the [Levels Ranks] plugins and borrows most of its functions as well as the database. This means that the RanksPoints plugin can be integrated with LrWeb or GameCMS similar to [Levels Ranks]. During development, there was no opportunity to fully test the plugin, so there may be errors in its operation. If you find any issues, please report them for correction. 12 | 13 | # RanksPoints 14 | The RanksPoints system is based on a simple principle: players perform various actions in the game, as a result of which they either gain or lose experience points. Achieving or losing a certain amount of these points leads to obtaining a corresponding rank. The number of available ranks can be configured and edited as desired. 15 | 16 | # Installation 17 | 1. Install [Metamod:Source](https://www.sourcemm.net/downloads.php/?branch=master) and [CounterStrikeSharp](https://github.com/roflmuffin/CounterStrikeSharp) 18 | 19 | 2. Download RankPoints. 20 | 21 | 3. Unzip the archive and upload it to your game server. 22 | 23 | 4. Start the server to create the necessary configuration files. 24 | 25 | 5. Connect the plugin to the database by entering the required data in the dbconfig.json file. Make sure the entered data is correct. 26 | 27 | # Main Configuration (Config.yml) 28 | ``` 29 | # Configuration file for RankPoints 30 | 31 | # Number of awarded points 32 | # Points for Kill - the number of points added to a player for killing an opponent. 33 | PointsForKill: 5 34 | # Points for Death - the number of points subtracted from a player for dying. 35 | PointsForDeath: -5 36 | # Points for Assist - the number of points added to a player for assisting in a kill. 37 | PointsForAssist: 1 38 | # Points for Suicide - the number of points subtracted from a player for committing suicide. 39 | PointsForSuicide: -6 40 | # Points for Headshot - additional points for a headshot kill. 41 | PointsForHeadshot: 1 42 | # Points per Round Win - the number of points added to a player for their team winning a round. 43 | PointsPerRoundWin: 2 44 | # Points per Round Loss - the number of points subtracted from a player for their team losing a round. 45 | PointsPerRoundLoss: -2 46 | # Points for MVP - the number of points added to a player for earning the MVP title of a round. 47 | PointsPerMVP: 3 48 | # Points for NoScope AWP Kill - additional points for killing without using the AWP scope. 49 | PointsForNoScopeAWP: 1 50 | # Points for Bomb Defusal 51 | PointsForBombDefusal: 2 52 | # Points for Bomb Exploded 53 | PointsForBombExploded: 2 54 | # Points for Bomb Planting - the number of points added to a player for successfully planting the bomb. 55 | PointsForBombPlanting: 2 56 | # Points for Bomb Dropping - the number of points subtracted from a player for dropping the bomb. 57 | PointsForBombDropping: -2 58 | # Points for Bomb Pickup - the number of points added to a player for picking up the bomb. 59 | PointsForBombPickup: 1 60 | # Points for Wallbang - the number of points added to a player for getting a kill through a wall. 61 | PointsForWallbang: 3 62 | # Points for Hostage Follows 63 | PointsForHostageFollows: 2 64 | # Points for Hostage Stops Following 65 | PointsForHostageStopsFollowing: -2 66 | # Points for Hostage Rescued 67 | PointsForHostageRescued: 4 68 | # Points for Kill Through Smoke - the number of points added to a player for killing an enemy through a smoke screen. 69 | PointsForKillThroughSmoke: 3 70 | # Points for Blind Kill - the number of points added to a player for getting a kill while blinded. 71 | PointsForBlindKill: 5 72 | 73 | # RanksPoints Parameters 74 | # Displaying clan tags for ranks of players. true - enabled, false - disabled. 75 | EnableClanTags: True 76 | # Minimum number of players for experience gain - players only earn experience if this number of players is on the server. 77 | GetActivePlayerCountMsg: "[ {Yellow}RanksPoints {White}] A minimum of {Red}{MIN_PLAYERS} {White}players is required for experience gain." 78 | MinPlayersForExperience: 4 79 | # Enabling or disabling bonus experience for special nicknames. 80 | EnableSpecialNicknameBonus: true 81 | # Experience multiplier for special nicknames. 82 | BonusMultiplierForSpecialNickname: 1.5 83 | # String to search for in the nickname to apply the multiplier. 84 | SpecialNicknameContains: "example.com" 85 | # Enabling or disabling awarding points for bot kills. true - enabled, false - disabled. 86 | GivePointsForBotKills: False 87 | 88 | # All RanksPoints Messages 89 | # Messages for experience gain 90 | PointsChangeMessage: "[ {Yellow}RanksPoints{White} ] Your experience: {COLOR} {POINTS} [{SIGN}{CHANGE_POINTS} for {REASON}]" 91 | # Events 92 | SuicideMessage: "suicide" 93 | SuicideMessageColor: "{Red}" 94 | DeathMessage: "death" 95 | DeathMessageColor: "{Red}" 96 | KillMessage: "kill" 97 | KillMessageColor: "{Green}" 98 | NoScopeAWPMessage: "AWP kill without scope" 99 | NoScopeAWPMessageColor: "{Blue}" 100 | HeadshotMessage: "headshot" 101 | HeadshotMessageColor: "{Yellow}" 102 | AssistMessage: "assist" 103 | AssistMessageColor: "{Blue}" 104 | RoundWinMessage: "round win" 105 | RoundWinMessageColor: "{Green}" 106 | RoundLossMessage: "round loss" 107 | RoundLossMessageColor: "{Red}" 108 | MVPMessage: "MVP" 109 | MVPMessageColor: "{Gold}" 110 | BombDefusalMessage: "bomb defusal" 111 | BombDefusalMessageColor: "{Green}" 112 | BombExplodedMessage: "bomb exploded" 113 | BombExplodedMessageColor: "{Green}" 114 | BombPlantingMessage: "bomb planting" 115 | BombPlantingMessageColor: "{Green}" 116 | BombDroppingMessage: "bomb dropping" 117 | BombDroppingMessageColor: "{Red}" 118 | BombPickupMessage: "bomb pickup" 119 | BombPickupMessageColor: "{Green}" 120 | WallbangMessage: "wallbang" 121 | WallbangMessageColor: "{Purple}" 122 | HostageFollowsMessage: "hostage follows" 123 | HostageFollowsMessageColor: "{Green}" 124 | HostageStopsFollowingMessage: "hostage stops following" 125 | HostageStopsFollowingMessageColor: "{Red}" 126 | HostageRescuedMessage: "hostage rescued" 127 | HostageRescuedMessageColor: "{Blue}" 128 | KillThroughSmokeMessage: "убийство через дым" 129 | KillThroughSmokeMessageColor: "{Green}" 130 | BlindKillMessage: "убийство в состоянии ослепления" 131 | BlindKillMessageColor: "{Yellow}" 132 | 133 | # Rank Up Message. 134 | RankUpMessage: Your rank has been upgraded to {RANK_NAME}! 135 | # Rank Down Message. 136 | RankDownMessage: Your rank has been downgraded to {RANK_NAME}. 137 | 138 | # !rank 139 | RankCommandMessage : "[ {Yellow}RanksPoints {White}] Rank: {Green}{RANK_NAME} {White}| Position: {Blue}{PLACE}/{TOTAL_PLAYERS} {White}| Experience: {Gold}{POINTS} {White}| Kills: {Green}{KILLS} {White}| Deaths: {Red}{DEATHS} {White}| KDR: {Yellow}{KDR} {White}| Server Time: {Gold}{PLAY_TIME}" 140 | TimeFormat: "{0}d {1}h {2}min" 141 | # Enabling or disabling the !rank command 142 | IsRankCommandEnabled: true 143 | 144 | # !top 145 | TopCommandIntroMessage : "[ {Blue}Top Players{White} ]" 146 | TopCommandPlayerMessage: "{INDEX}. {Grey}{NAME} - {White}{RANK} {Grey}- {Blue}{POINTS} points" 147 | TopCommandNoDataMessage: "[ {Red}Error{White} ] No data available for top players." 148 | TopCommandErrorMessage: "[ {Red}Error{White} ] An error occurred while executing the command." 149 | # Enabling or disabling the !top command 150 | IsTopCommandEnabled: true 151 | 152 | # !topkills 153 | TopKillsCommandIntroMessage: "[ {Green}Top Killers{White} ]" 154 | TopKillsCommandPlayerMessage: "{INDEX}. {Grey}{NAME} - {Green}{KILLS} kills{White}" 155 | TopKillsCommandNoDataMessage: "[ {Red}Error{White} ] No data available for top killers." 156 | TopKillsCommandErrorMessage: "[ {Red}Error{White} ] An error occurred while executing the command." 157 | # Enabling or disabling the !topkills command 158 | IsTopkillsCommandEnabled: true 159 | 160 | # !topdeaths 161 | TopDeathsCommandIntroMessage: "[ {Red}Top Deaths{White} ]" 162 | TopDeathsCommandPlayerMessage: "{INDEX}. {Grey}{NAME}{White} - {Red}{DEATHS} deaths{White}" 163 | TopDeathsCommandNoDataMessage: "[ {Red}Error{White} ] No data available for top deaths." 164 | TopDeathsCommandErrorMessage: "[ {Red}Error{White} ] An error occurred while executing the command." 165 | # Enabling or disabling the !topdeaths command 166 | IsTopdeathsCommandEnabled: true 167 | 168 | # !topkdr 169 | TopKDRCommandIntroMessage: "[ {Yellow}Top KDR{White} ]" 170 | TopKDRCommandPlayerMessage: "{INDEX}. {Grey}{NAME}{White} - {Yellow}KDR: {KDR}" 171 | TopKDRCommandNoDataMessage: "[ {Red}Error{White} ] No data available for top KDR." 172 | TopKDRCommandErrorMessage: "[ {Red}Error{White} ] An error occurred while executing the command." 173 | # Enabling or disabling the !topkdr command 174 | IsTopkdrCommandEnabled: true 175 | 176 | # !toptime 177 | TopTimeCommandIntroMessage: "[ {Gold}Top Playtime{White} ]" 178 | TopTimeCommandPlayerMessage: "{INDEX}. {Grey}{NAME} - {Gold}{TIME}{White}" 179 | TopTimeCommandNoDataMessage : "[ {Red}Error{White} ] No data available for top playtime." 180 | TopTimeCommandErrorMessage: "[ {Red}Error{White} ] An error occurred while executing the command." 181 | TopTimeFormat: "{0}d {1}h {2}min" 182 | # Enabling or disabling the !toptime command 183 | IsToptimeCommandEnabled: true 184 | 185 | # !resetstats 186 | ResetStatsCooldownMessage: "[ {Red}RanksPoints {White}] You can only reset your stats once every 3 hours." 187 | ResetStatsSuccessMessage: "[ {Yellow}RanksPoints {White}] Your stats have been reset." 188 | ResetStatsCooldownHours: "3" 189 | # Enabling or disabling the !resetstats command 190 | IsResetstatsCommandEnabled: true 191 | 192 | # !ranks 193 | RanksCommandIntroMessage: "[ {Gold}Rank List{White} ]" 194 | RanksCommandRankMessage: "{NAME} - {Green}{EXPERIENCE} experience{White}" 195 | RanksCommandNoDataMessage: "[ {Red}Error{White} ] No data available for ranks." 196 | RanksCommandErrorMessage: "[ {Red}Error{White} ] An error occurred while executing the command." 197 | # Enabling or disabling the !ranks command 198 | IsRanksCommandEnabled: true 199 | 200 | # !lvl 201 | LvlCommandIntroMessage: "[ {Gold}Available Commands{White} ]" 202 | RankCommandDescription: "- {Green}!rank {White}- Shows your current rank and stats" 203 | TopCommandDescription: "- {Green}!top {White}- Shows the top 10 players by points" 204 | TopKillsCommandDescription: "- {Green}!topkills {White}- Shows the top 10 players by kills" 205 | TopDeathsCommandDescription: "- {Green}!topdeaths {White}- Shows the top 10 players by deaths" 206 | TopKDRCommandDescription: "- {Green}!topkdr {White}- Shows the top 10 players by KDR" 207 | TopTimeCommandDescription: "- {Green}!toptime {White}- Shows the top 10 players by playtime" 208 | ResetStatsCommandDescription: "- {Green}!resetstats {White}- Reset your stats (can be used once every 3 hours)" 209 | RanksCommandDescription: "- {Green}!ranks {White}- Shows a list of all ranks and the required experience to achieve them" 210 | TagRankCommandDescription: "- {Green}!tagrank {White}- Enables or disables displaying your clan tag" 211 | # Enabling or disabling the !lvl command 212 | IsLvlCommandEnabled: true 213 | 214 | # !tagrank 215 | TagRankEnabledMessage: "[ {Yellow}RanksPoints {White}] Your clan tag will be displayed again starting from the next round." 216 | TagRankDisabledMessage: "[ {Yellow}RanksPoints {White}] Your clan tag will no longer be displayed starting from the next round." 217 | # Enabling or disabling the !tagrank command 218 | IsTagRankCommandEnabled: true 219 | ``` 220 | # Rank Configuration (settings_ranks.yml) 221 | ``` 222 | - id: 0 223 | name: Silver - I 224 | minExperience: 0 225 | clanTag: '[Silver - I]' 226 | - id: 1 227 | name: Silver - II 228 | minExperience: 10 229 | clanTag: '[Silver - II]' 230 | - id: 2 231 | name: Silver - III 232 | minExperience: 25 233 | clanTag: '[Silver - III]' 234 | - id: 3 235 | name: Silver - IV 236 | minExperience: 50 237 | clanTag: '[Silver - IV]' 238 | - id: 4 239 | name: Silver Elite 240 | minExperience: 75 241 | clanTag: '[Silver Elite]' 242 | - id: 5 243 | name: Silver - Master Guardian 244 | minExperience: 100 245 | clanTag: '[Silver - MG]' 246 | - id: 6 247 | name: Gold Star - I 248 | minExperience: 150 249 | clanTag: '[Gold Star - I]' 250 | - id: 7 251 | name: Gold Star - II 252 | minExperience: 200 253 | clanTag: '[Gold Star - II]' 254 | - id: 8 255 | name: Gold Star - III 256 | minExperience: 300 257 | clanTag: '[Gold Star - III]' 258 | - id: 9 259 | name: Gold Star - Master 260 | minExperience: 500 261 | clanTag: '[Gold Star - M]' 262 | - id: 10 263 | name: Master Guardian - I 264 | minExperience: 750 265 | clanTag: '[Master Guardian - I]' 266 | - id: 11 267 | name: Master Guardian - II 268 | minExperience: 1000 269 | clanTag: '[Master Guardian - II]' 270 | - id: 12 271 | name: Master Guardian Elite 272 | minExperience: 1500 273 | clanTag: '[Master Guardian Elite]' 274 | - id: 13 275 | name: Distinguished Master Guardian 276 | minExperience: 2000 277 | clanTag: '[Distinguished MG]' 278 | - id: 14 279 | name: Legendary Eagle 280 | minExperience: 3000 281 | clanTag: '[Legendary Eagle]' 282 | - id: 15 283 | name: Legendary Eagle Master 284 | minExperience: 5000 285 | clanTag: '[Legendary Eagle M]' 286 | - id: 16 287 | name: Supreme Master First Class 288 | minExperience: 7500 289 | clanTag: '[Supreme M-FC]' 290 | - id: 17 291 | name: Global Elite 292 | minExperience: 10000 293 | clanTag: '[Global Elite]' 294 | ``` 295 | 296 | # Configuration file for setting up experience points awarded for kills with specific types of weapons (Weapons.yml). 297 | Additional weapon types can also be added, for example, 'weapon_knife' corresponds to 'knife'. 298 | ``` 299 | - WeaponName: knife 300 | Points: 10 301 | MessageColor: '{LightYellow}' 302 | KillMessage: knife kill 303 | - WeaponName: awp 304 | Points: 5 305 | MessageColor: '{Blue}' 306 | KillMessage: precise AWP shot 307 | ``` 308 | 309 | # Database Connection Configuration (dbconfig.json) 310 | ``` 311 | { 312 | "DbHost": "YourHost", 313 | "DbUser": "YourUser", 314 | "DbPassword": "YourPassword", 315 | "DbName": "YourDatabase", 316 | "DbPort": "3306" 317 | "Name": "lvl_base" 318 | } 319 | ``` 320 | # Chat Commands 321 | - `!rank` shows statistics: current rank, required number of points to the next rank, your experience, number of kills, deaths, and kill-to-death ratio (KDR). 322 | - `!top` displays a list of the top ten players by experience points. 323 | - `!topkills` displays a list of the top ten players by kills. 324 | - `!topdeaths` displays a list of the top ten players by deaths. 325 | - `!topkdr` displays a list of the top ten players by KDR. 326 | - `!toptime` shows the top ten players by time on the server. 327 | - `!resetstats` resets your statistics (can be used once every 3 hours). 328 | - `!ranks` shows a list of all ranks and the experience required to achieve them. 329 | - `!lvl` shows a list of all available commands and their functions. 330 | - `!tagrank` enables or disables the clan tag 331 | 332 | # Console Commands 333 | - `rp_reloadconfig` reloads the configuration file Config.yml. 334 | - `rp_reloadranks` reloads the configuration file settings_ranks.yaml. 335 | - `rp_reloadweapons` reloads the configuration file Weapons.yaml. 336 | - `rp_resetranks` Clears a player's statistics. Usage: rp_resetranks (`data-type`: `exp` clears values, rank; `stats` clears kills, deaths, shoots, hits, headshots, assists, round_win, round_lose; `time` clears playtime). 337 | 338 | # RU 339 | **Для удобства и лучшей организации помощи я создал специальный сервер в Discord. Вы можете присоединиться к нему по следующей ссылке: [https://discord.gg/XjfUGMdCgn](https://discord.gg/XjfUGMdCgn)** 340 | 341 | Плагин был разработан, вдохновляясь плагином [Levels Ranks], и заимствует большую часть своих функций, а также базу данных. Это означает, что плагин RanksPoints может быть интегрирован с LrWeb или GameCMS аналогично [Levels Ranks]. В процессе разработки не было возможности полностью проверить плагин, поэтому в его работе могут проявляться ошибки. Если вы обнаружите какие-либо проблемы, сообщите об этом для их исправления. 342 | 343 | # RanksPoints 344 | RanksPoints система базируется на простом принципе: игроки совершают разнообразные действия в игре, в результате которых они либо приобретают, либо теряют очки опыта. Достижение или потеря определенного объема этих очков ведет к получению соответствующего ранга. Количество доступных рангов может быть настроено и отредактировано по усмотрению. 345 | 346 | # Установка 347 | 1. Установите [Metamod:Source](https://www.sourcemm.net/downloads.php/?branch=master) и [CounterStrikeSharp](https://github.com/roflmuffin/CounterStrikeSharp) 348 | 349 | 2. Скачайте RankPoints 350 | 351 | 3. Распакуйте архив и загрузите его на игровой сервер 352 | 353 | 4. Запустите сервер, чтобы создать необходимые конфигурационные файлы. 354 | 355 | 5. Подключите плагин к базе данных, введя необходимые данные в файл dbconfig.json. Убедитесь в корректности введенных данных. 356 | 357 | # Основной конфиг (Config.yml) 358 | ``` 359 | # Конфигурационный файл для RankPoints 360 | 361 | # Количество выдаваемых очков 362 | # Очки за убийство - количество очков, добавляемое игроку за убийство противника. 363 | PointsForKill: 5 364 | # Очки отнимаемые за смерть - количество очков, вычитаемое у игрока за смерть. 365 | PointsForDeath: -5 366 | # Очки за помощь - количество очков, добавляемое игроку за помощь в убийстве. 367 | PointsForAssist: 1 368 | # Очки за самоубийство - количество очков, вычитаемое у игрока за самоубийство. 369 | PointsForSuicide: -6 370 | # Очки за выстрел в голову - дополнительные очки за убийство с выстрелом в голову. 371 | PointsForHeadshot: 1 372 | # Очки за победу в раунде - количество очков, добавляемое игроку за победу его команды в раунде. 373 | PointsPerRoundWin: 2 374 | # Очки за проигрыш в раунде - количество очков, вычитаемое у игрока за проигрыш его команды в раунде. 375 | PointsPerRoundLoss: -2 376 | # Очки за MVP - количество очков, добавляемое игроку за получение звания MVP раунда. 377 | PointsPerMVP: 3 378 | # Очки за убийство с AWP без прицела - дополнительные очки за убийство без использования прицела. 379 | PointsForNoScopeAWP: 1 380 | # Очки за обезвреживание бомбы 381 | PointsForBombDefusal: 2 382 | # Очки за взрыв бомбы 383 | PointsForBombExploded: 2 384 | # Очки за установку бомбы - количество очков, добавляемое игроку за успешную установку бомбы. 385 | PointsForBombPlanting: 2 386 | # Очки за выброс бомбы - количество очков, вычитаемое у игрока за выброс бомбы. 387 | PointsForBombDropping: -2 388 | # Очки за поднятие бомбы - количество очков, добавляемое игроку за поднятие бомбы. 389 | PointsForBombPickup: 1 390 | # Очки за убийство через прострел. 391 | PointsForWallbang: 3 392 | # Очки за поднятие заложника 393 | PointsForHostageFollows: 2 394 | # Очки за потерю заложника 395 | PointsForHostageStopsFollowing: -2 396 | # Очки за спасение заложника 397 | PointsForHostageRescued: 4 398 | # Очки за убийство через дым - количество очков, добавляемое игроку за убийство врага через дымовую завесу. 399 | PointsForKillThroughSmoke: 3 400 | # Очки за убийство в состоянии ослепления - количество очков, добавляемое игроку за убийство, когда он ослеплен. 401 | PointsForBlindKill: 5 402 | 403 | # Параметры RanksPoints 404 | # Отображение клан-тегов званий для игроков. true - включено, false - отключено. 405 | EnableClanTags: True 406 | # Минимальное количество игроков для начисления опыта - игрокам начисляется опыт только если на сервере играет минимум это количество игроков. 407 | GetActivePlayerCountMsg: "[ {Yellow}RanksPoints {White}] Необходимо минимум {Red}{MIN_PLAYERS} {White}игроков для начисления опыта." 408 | MinPlayersForExperience: 4 409 | # Включение или выключение дополнительного опыта для специальных никнеймов 410 | EnableSpecialNicknameBonus: true 411 | # Множитель опыта для специальных никнеймов 412 | BonusMultiplierForSpecialNickname: 1.5 413 | # Строка, которую нужно искать в никнейме для применения множителя 414 | SpecialNicknameContains: "example.com" 415 | # Включение или выключение начисления очков за убийство ботов. true - включено, false - выключено. 416 | GivePointsForBotKills: false 417 | 418 | # Все сообщения RanksPoints 419 | # Сообщения при получении опыта 420 | PointsChangeMessage: "[ {Yellow}RanksPoints{White} ] Ваш опыт:{COLOR} {POINTS} [{SIGN}{CHANGE_POINTS} за {REASON}]" 421 | # События 422 | SuicideMessage: "самоубийство" 423 | SuicideMessageColor: "{Red}" 424 | DeathMessage: "смерть" 425 | DeathMessageColor: "{Red}" 426 | KillMessage: "убийство" 427 | KillMessageColor: "{Green}" 428 | NoScopeAWPMessage: "убийство с AWP без прицела" 429 | NoScopeAWPMessageColor: "{Blue}" 430 | HeadshotMessage: "выстрел в голову" 431 | HeadshotMessageColor: "{Yellow}" 432 | AssistMessage: "ассист" 433 | AssistMessageColor: "{Blue}" 434 | RoundWinMessage: "победа в раунде" 435 | RoundWinMessageColor: "{Green}" 436 | RoundLossMessage: "проигрыш в раунде" 437 | RoundLossMessageColor: "{Red}" 438 | MVPMessage: "MVP" 439 | MVPMessageColor: "{Gold}" 440 | BombDefusalMessage: "обезвреживание бомбы" 441 | BombDefusalMessageColor: "{Green}" 442 | BombExplodedMessage: "взрыв бомбы" 443 | BombExplodedMessageColor: "{Green}" 444 | BombPlantingMessage: "установку бомбы" 445 | BombPlantingMessageColor: "{Green}" 446 | BombDroppingMessage: "выброс бомбы" 447 | BombDroppingMessageColor: "{Red}" 448 | BombPickupMessage: "поднятие бомбы" 449 | BombPickupMessageColor: "{Green}" 450 | WallbangMessage: "прострел" 451 | WallbangMessageColor: "{Purple}" 452 | HostageFollowsMessage: "заложник следует" 453 | HostageFollowsMessageColor: "{Green}" 454 | HostageStopsFollowingMessage: "заложник перестал следовать" 455 | HostageStopsFollowingMessageColor: "{Red}" 456 | HostageRescuedMessage: "заложник спасен" 457 | HostageRescuedMessageColor: "{Blue}" 458 | KillThroughSmokeMessage: "убийство через дым" 459 | KillThroughSmokeMessageColor: "{Green}" 460 | BlindKillMessage: "убийство в состоянии ослепления" 461 | BlindKillMessageColor: "{Yellow}" 462 | 463 | # Сообщение о повышении звания. 464 | RankUpMessage: Ваше звание было повышено до {RANK_NAME}! 465 | # Сообщение о понижении звания. 466 | RankDownMessage: Ваше звание было понижено до {RANK_NAME}. 467 | 468 | # !rank 469 | RankCommandMessage : "[ {Yellow}RanksPoints {White}] Звание: {Green}{RANK_NAME} {White}| Место: {Blue}{PLACE}/{TOTAL_PLAYERS} {White}| Опыт: {Gold}{POINTS} {White}| Убийства: {Green}{KILLS} {White}| Смерти: {Red}{DEATHS} {White}| KDR: {Yellow}{KDR} {White}| Время на сервере: {Gold}{PLAY_TIME}" 470 | TimeFormat: "{0}д {1}ч {2}мин" 471 | # Включение или выключение команды !rank 472 | IsRankCommandEnabled: true 473 | 474 | # !top 475 | TopCommandIntroMessage : "[ {Blue}Топ игроков{White} ]" 476 | TopCommandPlayerMessage: "{INDEX}. {Grey}{NAME} - {White}{RANK} {Grey}- {Blue}{POINTS} очков" 477 | TopCommandNoDataMessage: "[ {Red}Ошибка{White} ] Нет данных о топ игроках." 478 | TopCommandErrorMessage: "[ {Red}Ошибка{White} ] Произошла ошибка при выполнении команды." 479 | # Включение или выключение команды !top 480 | IsTopCommandEnabled: true 481 | 482 | # !topkills 483 | TopKillsCommandIntroMessage: "[ {Green}Топ игроков по убийствам{White} ]" 484 | TopKillsCommandPlayerMessage: "{INDEX}. {Grey}{NAME} - {Green}{KILLS} убийств{White}" 485 | TopKillsCommandNoDataMessage: "[ {Red}Ошибка{White} ] Нет данных о топ игроках по убийствам." 486 | TopKillsCommandErrorMessage: "[ {Red}Ошибка{White} ] Произошла ошибка при выполнении команды." 487 | # Включение или выключение команды !topkills 488 | IsTopkillsCommandEnabled: true 489 | 490 | # !topdeaths 491 | TopDeathsCommandIntroMessage: "[ {Red}Топ игроков по смертям{White} ]" 492 | TopDeathsCommandPlayerMessage: "{INDEX}. {Grey}{NAME}{White} - {Red}{DEATHS} смертей{White}" 493 | TopDeathsCommandNoDataMessage: "[ {Red}Ошибка{White} ] Нет данных о топ игроках по смертям." 494 | TopDeathsCommandErrorMessage: "[ {Red}Ошибка{White} ] Произошла ошибка при выполнении команды." 495 | # Включение или выключение команды !topdeaths 496 | IsTopdeathsCommandEnabled: true 497 | 498 | # !topkdr 499 | TopKDRCommandIntroMessage: "[ {Yellow}Топ игроков по KDR{White} ]" 500 | TopKDRCommandPlayerMessage: "{INDEX}. {Grey}{NAME}{White} - {Yellow}KDR: {KDR}" 501 | TopKDRCommandNoDataMessage: "[ {Red}Ошибка{White} ] Нет данных о топ игроках по KDR." 502 | TopKDRCommandErrorMessage: "[ {Red}Ошибка{White} ] Произошла ошибка при выполнении команды." 503 | # Включение или выключение команды !topkdr 504 | IsTopkdrCommandEnabled: true 505 | 506 | # !toptime 507 | TopTimeCommandIntroMessage: "[ {Gold}Топ игроков по времени на сервере{White} ]" 508 | TopTimeCommandPlayerMessage: "{INDEX}. {Grey}{NAME} - {Gold}{TIME}{White}" 509 | TopTimeCommandNoDataMessage : "[ {Red}Ошибка{White} ] Нет данных о топ игроках по времени на сервере." 510 | TopTimeCommandErrorMessage: "[ {Red}Ошибка{White} ] Произошла ошибка при выполнении команды." 511 | TopTimeFormat: "{0}д {1}ч {2}мин" 512 | # Включение или выключение команды !toptime 513 | IsToptimeCommandEnabled: true 514 | 515 | # !resetstats 516 | ResetStatsCooldownMessage: "[ {Red}RanksPoints {White}] Сбросить статистику можно только раз в 3 часа." 517 | ResetStatsSuccessMessage: "[ {Yellow}RanksPoints {White}] Ваша статистика сброшена." 518 | ResetStatsCooldownHours: "3" 519 | # Включение или выключение команды !resetstats 520 | IsResetstatsCommandEnabled: true 521 | 522 | # !ranks 523 | RanksCommandIntroMessage: "[ {Gold}Список званий{White} ]" 524 | RanksCommandRankMessage: "{NAME} - {Green}{EXPERIENCE} опыта{White}" 525 | RanksCommandNoDataMessage: "[ {Red}Ошибка{White} ] Нет данных о званиях." 526 | RanksCommandErrorMessage: "[ {Red}Ошибка{White} ] Произошла ошибка при выполнении команды." 527 | # Включение или выключение команды !ranks 528 | IsRanksCommandEnabled: true 529 | 530 | # !lvl 531 | LvlCommandIntroMessage: "[ {Gold}Список доступных команд{White} ]" 532 | RankCommandDescription: "- {Green}!rank {White}- Показывает ваше текущее звание и статистику" 533 | TopCommandDescription: "- {Green}!top {White}- Показывает топ-10 игроков по очкам" 534 | TopKillsCommandDescription: "- {Green}!topkills {White}- Показывает топ-10 игроков по убийствам" 535 | TopDeathsCommandDescription: "- {Green}!topdeaths {White}- Показывает топ-10 игроков по смертям" 536 | TopKDRCommandDescription: "- {Green}!topkdr {White}- Показывает топ-10 игроков по KDR" 537 | TopTimeCommandDescription: "- {Green}!toptime {White}- Показывает топ-10 игроков по времени на сервере" 538 | ResetStatsCommandDescription: "- {Green}!resetstats {White}- Сбросить свою статистику (можно использовать раз в 3 часа)" 539 | RanksCommandDescription: "- {Green}!ranks {White}- Показывает список всех званий и опыта, необходимого для их получения" 540 | TagRankCommandDescription: "- {Green}!tagrank {White}- Включает или выключает отображение вашего клан-тега" 541 | # Включение или выключение команды !lvl 542 | IsLvlCommandEnabled: true 543 | 544 | # !tagrank 545 | TagRankEnabledMessage: "[ {Yellow}RanksPoints {White}] Клан-тег будет вновь отображаться, начиная с следующего раунда." 546 | TagRankDisabledMessage: "[ {Yellow}RanksPoints {White}] Клан-тег больше не будет отображаться, начиная с следующего раунда." 547 | # Включение или выключение команды !tagrank 548 | IsTagRankCommandEnabled: true 549 | ``` 550 | 551 | # Конфиг настройки званий (settings_ranks.yml) 552 | ``` 553 | - id: 1 554 | name: Серебро - I 555 | minExperience: 0 556 | - id: 2 557 | name: Серебро - II 558 | minExperience: 10 559 | - id: 3 560 | name: Серебро - III 561 | minExperience: 25 562 | - id: 4 563 | name: Серебро - IV 564 | minExperience: 50 565 | - id: 5 566 | name: Серебро Элита 567 | minExperience: 75 568 | - id: 6 569 | name: Серебро - Великий Магистр 570 | minExperience: 100 571 | - id: 7 572 | name: Золотая Звезда - I 573 | minExperience: 150 574 | - id: 8 575 | name: Золотая Звезда - II 576 | minExperience: 200 577 | - id: 9 578 | name: Золотая Звезда - III 579 | minExperience: 300 580 | - id: 10 581 | name: Золотая Звезда - Магистр 582 | minExperience: 500 583 | - id: 11 584 | name: Магистр-хранитель - I 585 | minExperience: 750 586 | - id: 12 587 | name: Магистр-хранитель - II 588 | minExperience: 1000 589 | - id: 13 590 | name: Магистр-хранитель - Элита 591 | minExperience: 1500 592 | - id: 14 593 | name: Заслуженный Магистр-хранитель 594 | minExperience: 2000 595 | - id: 15 596 | name: Легендарный Беркут 597 | minExperience: 3000 598 | - id: 16 599 | name: Легендарный Беркут-магистр 600 | minExperience: 5000 601 | - id: 17 602 | name: Великий Магистр - Высшего Ранга 603 | minExperience: 7500 604 | - id: 18 605 | name: Всемирная Элита 606 | minExperience: 10000 607 | ``` 608 | 609 | # Конфигурационный файл для настройки начисления опыта за убийства с использованием определенных видов оружия (Weapons.yml). 610 | Также можно добавить другие виды оружия, например, 'weapon_knife', это 'knife' 611 | ``` 612 | - WeaponName: knife 613 | Points: 10 614 | MessageColor: '{Red}' 615 | KillMessage: убийство ножом 616 | - WeaponName: awp 617 | Points: 5 618 | MessageColor: '{Blue}' 619 | KillMessage: точный выстрел из AWP 620 | ``` 621 | 622 | # Конфиг подключения базы данных (dbconfig.json) 623 | ``` 624 | { 625 | "DbHost": "YourHost", 626 | "DbUser": "YourUser", 627 | "DbPassword": "YourPassword", 628 | "DbName": "YourDatabase" 629 | "Name": "lvl_base" 630 | } 631 | ``` 632 | 633 | # Команды для чата 634 | - `!rank` показывает статистику: текущее звание, необходимое количество очков до следующего звания, ваш опыт, количество убийств, смертей и коэффициент убийств к смертям (KDR). 635 | - `!top` выводит список десяти лучших игроков по очкам опыта. 636 | - `!topkills` выводит список десяти лучших игроков по убийствам. 637 | - `!topdeaths` выводит список десяти лучших игроков по смертям. 638 | - `!topkdr` выводит список десяти лучших игроков по KDR. 639 | - `toptime` показывает топ-10 игроков по времени на сервере 640 | - `!resetstats` Cбросить свою статистику (можно использовать раз в 3 часа) 641 | - `!ranks` показывает список всех званий и опыта, необходимого для их получения 642 | - `!lvl` показывает список всех доступных команд и их функций 643 | - `!tagrank` включает или выключает отображение клан-тега 644 | 645 | # Команды для консоли 646 | - `rp_reloadconfig` перезагружает конфигурационный файл Config.yml 647 | - `rp_reloadranks` перезагружает конфигурационный файл settings_ranks.yaml 648 | - `rp_reloadweapons` перезагружает конфигурационный файл Weapons.yaml 649 | - `rp_resetranks` очищает статистику игрока. Использование: rp_resetranks (`data-type`: `exp` очистка values, rank; `stats` очистка kills, deaths, shoots, hits, headshots, assists, round_win, round_lose; `time` очистка playtime) 650 | -------------------------------------------------------------------------------- /RU/source/CounterStrikeSharp.API.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ABKAM2023/CS2-RanksPoints/1d36ea4750df91fe591b0c6a0b5c36cce1f15f21/RU/source/CounterStrikeSharp.API.dll -------------------------------------------------------------------------------- /RU/source/RanksPoints.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | AnyCPU;x86 6 | true 7 | enable 8 | 9 | 10 | 11 | 12 | ..\CounterStrikeSharp.API.dll 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | --------------------------------------------------------------------------------