From 04a4fdce0d9ad27f0237e51074a9055c41279f12 Mon Sep 17 00:00:00 2001 From: Juzlus Date: Tue, 23 Sep 2025 04:53:25 +0200 Subject: [PATCH] v.1.1.7 --- .../Classes/WasdManager.cs | 4 +- WASDMenuAPI - SRC Files/Classes/WasdMenu.cs | 2 + .../Classes/WasdMenuPlayer.cs | 17 +- .../Interfaces/IWasdMenu.cs | 2 + .../Interfaces/IWasdMenuManager.cs | 2 +- .../!jRandomSkills.csproj | 4 +- .../src/command/Command.cs | 157 +-- .../src/command/VoteSystem.cs | 39 +- .../src/jRandomSkills.cs | 13 +- jRandomSkills - SRC Files/src/lang/en.json | 7 +- jRandomSkills - SRC Files/src/lang/fr.json | 9 +- jRandomSkills - SRC Files/src/lang/pl.json | 7 +- jRandomSkills - SRC Files/src/lang/pt-br.json | 7 +- jRandomSkills - SRC Files/src/lang/zh.json | 7 +- jRandomSkills - SRC Files/src/player/Debug.cs | 2 +- .../src/player/PlayerEvents.cs | 180 +-- .../src/player/PlayerOnTick.cs | 44 +- .../src/player/skills/Aimbot.cs | 7 +- .../src/player/skills/Anomaly.cs | 38 +- .../src/player/skills/AntyFlash.cs | 7 +- .../src/player/skills/AntyHead.cs | 4 +- .../src/player/skills/AreaReaper.cs | 7 +- .../src/player/skills/Armored.cs | 8 +- .../src/player/skills/Assassin.cs | 9 +- .../src/player/skills/Astronaut.cs | 8 +- .../src/player/skills/Bankrupt.cs | 9 +- .../src/player/skills/Baseball.cs | 22 +- .../src/player/skills/Behind.cs | 8 +- .../src/player/skills/BladeMaster.cs | 32 +- .../src/player/skills/BunnyHop.cs | 12 +- .../src/player/skills/C4Camouflage.cs | 25 +- .../src/player/skills/Catapult.cs | 8 +- .../src/player/skills/Chicken.cs | 62 +- .../src/player/skills/ChillOut.cs | 7 +- .../src/player/skills/Cutter.cs | 4 +- .../src/player/skills/Darkness.cs | 55 +- .../src/player/skills/Dash.cs | 15 +- .../src/player/skills/Deactivator.cs | 9 +- .../src/player/skills/Deaf.cs | 19 +- .../src/player/skills/Disarmament.cs | 11 +- .../src/player/skills/Distancer.cs | 40 +- .../src/player/skills/Dracula.cs | 7 +- .../src/player/skills/Duplicator.cs | 18 +- .../src/player/skills/Dwarf.cs | 8 +- .../src/player/skills/Earthquake.cs | 47 +- .../src/player/skills/EnemySpawn.cs | 42 +- .../src/player/skills/ExplosiveShot.cs | 53 +- .../src/player/skills/FalconEye.cs | 14 +- .../src/player/skills/FastReload.cs | 4 +- .../src/player/skills/Flash.cs | 18 +- .../src/player/skills/Fortnite.cs | 52 +- .../src/player/skills/FragileBomb.cs | 11 +- .../src/player/skills/FriendlyFire.cs | 7 +- .../src/player/skills/FrozenDecoy.cs | 9 +- .../src/player/skills/Gambler.cs | 26 +- .../src/player/skills/Ghost.cs | 45 +- .../src/player/skills/Glaz.cs | 17 +- .../src/player/skills/Glitch.cs | 19 +- .../src/player/skills/Glue.cs | 4 +- .../src/player/skills/GodMode.cs | 38 +- .../src/player/skills/Grenadier.cs | 4 +- .../src/player/skills/HealingSmoke.cs | 10 +- .../src/player/skills/Hermit.cs | 35 +- .../src/player/skills/HolyHandGrenade.cs | 10 +- .../src/player/skills/Impostor.cs | 4 +- .../src/player/skills/InfiniteAmmo.cs | 4 +- .../src/player/skills/Jackal.cs | 16 +- .../src/player/skills/Jammer.cs | 25 +- .../src/player/skills/Jester.cs | 28 +- .../src/player/skills/JumpBan.cs | 17 +- .../src/player/skills/JumpingJack.cs | 7 +- .../src/player/skills/KillerFlash.cs | 11 +- .../src/player/skills/LifeSwap.cs | 9 +- .../src/player/skills/LongKnife.cs | 74 +- .../src/player/skills/LongZeus.cs | 9 +- .../src/player/skills/Magnifier.cs | 18 +- .../src/player/skills/Medic.cs | 42 +- .../src/player/skills/MoneySwap.cs | 9 +- .../src/player/skills/Muhammed.cs | 51 +- .../src/player/skills/Ninja.cs | 33 +- .../src/player/skills/NoNades.cs | 4 +- .../src/player/skills/NoRecoil.cs | 4 +- .../src/player/skills/Noclip.cs | 83 +- .../src/player/skills/None.cs | 4 +- .../src/player/skills/OneShot.cs | 4 +- .../src/player/skills/OnlyHead.cs | 4 +- .../src/player/skills/PawelJumper.cs | 13 +- .../src/player/skills/Phoenix.cs | 23 +- .../src/player/skills/Pilot.cs | 44 +- .../src/player/skills/Planter.cs | 9 +- .../src/player/skills/Poison.cs | 27 +- .../src/player/skills/PrimaryBan.cs | 29 +- .../src/player/skills/Prosthesis.cs | 4 +- .../src/player/skills/PsychicDefusing.cs | 65 +- .../src/player/skills/Push.cs | 14 +- .../src/player/skills/Pyro.cs | 7 +- .../src/player/skills/QuickShot.cs | 4 +- .../src/player/skills/RadarHack.cs | 6 +- .../src/player/skills/Rambo.cs | 8 +- .../src/player/skills/RandomWeapon.cs | 35 +- .../src/player/skills/ReZombie.cs | 37 +- .../src/player/skills/ReactiveArmor.cs | 35 +- .../src/player/skills/Regeneration.cs | 12 +- .../src/player/skills/Replicator.cs | 35 +- .../src/player/skills/Retreat.cs | 35 +- .../src/player/skills/ReturnToSender.cs | 13 +- .../src/player/skills/RichBoy.cs | 8 +- .../src/player/skills/RobinHood.cs | 7 +- .../src/player/skills/Rubber.cs | 20 +- .../src/player/skills/Saper.cs | 4 +- .../src/player/skills/SecondLife.cs | 30 +- .../src/player/skills/Shade.cs | 26 +- .../src/player/skills/ShortBomb.cs | 7 +- .../src/player/skills/Silent.cs | 4 +- .../src/player/skills/SniperElite.cs | 26 +- .../src/player/skills/Soldier.cs | 8 +- .../src/player/skills/SoundMaker.cs | 37 +- .../src/player/skills/Spectator.cs | 28 +- .../src/player/skills/SwapPosition.cs | 54 +- .../src/player/skills/Teleporter.cs | 4 +- .../src/player/skills/Thief.cs | 17 +- .../src/player/skills/ThirdEye.cs | 23 +- .../src/player/skills/Thorns.cs | 7 +- .../src/player/skills/ToxicSmoke.cs | 10 +- .../src/player/skills/Wallhack.cs | 24 +- .../src/player/skills/Watchmaker.cs | 23 +- .../src/player/skills/WeaponsSwap.cs | 50 +- .../src/player/skills/Zeus.cs | 4 +- jRandomSkills - SRC Files/src/utils/Config.cs | 299 ++--- .../src/utils/Localization.cs | 100 +- .../src/utils/SkillUtils.cs | 72 +- .../src/utils/SkillsInfo.cs | 139 +++ .../gamedata/jRandomSkills.gamedata.json | 16 + .../plugins/!jRandomSkills/!jRandomSkills.dll | Bin 373760 -> 401920 bytes .../plugins/!jRandomSkills/WASDMenuAPI.dll | Bin 16384 -> 16384 bytes .../!jRandomSkills/configs/config.json | 1011 ++-------------- .../!jRandomSkills/configs/skillsInfo.json | 1014 +++++++++++++++++ .../plugins/!jRandomSkills/languages/en.json | 7 +- .../plugins/!jRandomSkills/languages/fr.json | 9 +- .../plugins/!jRandomSkills/languages/pl.json | 7 +- .../!jRandomSkills/languages/pt-br.json | 7 +- .../!jRandomSkills/languages/test.json | 458 ++++++++ .../plugins/!jRandomSkills/languages/zh.json | 7 +- 143 files changed, 3565 insertions(+), 2344 deletions(-) create mode 100644 jRandomSkills - SRC Files/src/utils/SkillsInfo.cs create mode 100644 jRandomSkills - Server Files/gamedata/jRandomSkills.gamedata.json create mode 100644 jRandomSkills - Server Files/plugins/!jRandomSkills/configs/skillsInfo.json create mode 100644 jRandomSkills - Server Files/plugins/!jRandomSkills/languages/test.json diff --git a/WASDMenuAPI - SRC Files/Classes/WasdManager.cs b/WASDMenuAPI - SRC Files/Classes/WasdManager.cs index 9f62692..0055097 100644 --- a/WASDMenuAPI - SRC Files/Classes/WasdManager.cs +++ b/WASDMenuAPI - SRC Files/Classes/WasdManager.cs @@ -35,11 +35,13 @@ public class WasdManager : IWasdMenuManager menuPlayer?.OpenSubMenu(menu); } - public IWasdMenu CreateMenu(string title = "", string controlText = "") + public IWasdMenu CreateMenu(string title, string itemText, string itemHoverText, string controlText) { WasdMenu menu = new() { Title = title, + ItemText = itemText, + ItemHoverText = itemHoverText, ControlText = controlText }; return menu; diff --git a/WASDMenuAPI - SRC Files/Classes/WasdMenu.cs b/WASDMenuAPI - SRC Files/Classes/WasdMenu.cs index 8a3f49e..de2aa0a 100644 --- a/WASDMenuAPI - SRC Files/Classes/WasdMenu.cs +++ b/WASDMenuAPI - SRC Files/Classes/WasdMenu.cs @@ -6,6 +6,8 @@ namespace WASDMenuAPI.Classes; public class WasdMenu : IWasdMenu { public string Title { get; set; } = ""; + public string ItemText { get; set; } = ""; + public string ItemHoverText { get; set; } = ""; public string ControlText { get; set; } = ""; public LinkedList? Options { get; set; } = new(); public LinkedListNode? Prev { get; set; } = null; diff --git a/WASDMenuAPI - SRC Files/Classes/WasdMenuPlayer.cs b/WASDMenuAPI - SRC Files/Classes/WasdMenuPlayer.cs index b559be8..554397a 100644 --- a/WASDMenuAPI - SRC Files/Classes/WasdMenuPlayer.cs +++ b/WASDMenuAPI - SRC Files/Classes/WasdMenuPlayer.cs @@ -123,14 +123,22 @@ public class WasdMenuPlayer return; StringBuilder builder = new(); + string itemText = ""; + string itemHoverText = ""; string endControl = ""; LinkedListNode? option = MenuStart; if (!string.IsNullOrEmpty(option?.Value?.Parent?.Title)) - builder.AppendLine($"{option.Value.Parent?.Title}
"); + builder.AppendLine(option.Value.Parent?.Title ?? ""); + + if (!string.IsNullOrEmpty(option?.Value?.Parent?.ItemText)) + itemText = option?.Value?.Parent?.ItemText ?? ""; + + if (!string.IsNullOrEmpty(option?.Value?.Parent?.ItemHoverText)) + itemHoverText = option?.Value?.Parent?.ItemHoverText ?? ""; if (!string.IsNullOrEmpty(option?.Value?.Parent?.ControlText)) - endControl = option?.Value?.Parent?.ControlText ?? ""; + endControl = option?.Value?.Parent?.ControlText ?? ""; int shown = 0; int maxOptions = VisibleOptions; @@ -176,10 +184,10 @@ public class WasdMenuPlayer else text = SafeSubstring(text, 0, maxLength); - builder.AppendLine($"
[ {text} ]
"); + builder.AppendLine(string.Format(itemHoverText, text)); } else - builder.AppendLine($"{SafeSubstring(text, 0, maxLength)}
"); + builder.AppendLine(string.Format(itemText, $"{SafeSubstring(text, 0, maxLength)}")); option = option.Next; shown++; @@ -188,7 +196,6 @@ public class WasdMenuPlayer if (!string.IsNullOrEmpty(endControl)) builder.AppendLine(endControl); - builder.AppendLine(""); CenterHtml = builder.ToString(); } diff --git a/WASDMenuAPI - SRC Files/Interfaces/IWasdMenu.cs b/WASDMenuAPI - SRC Files/Interfaces/IWasdMenu.cs index b884854..ce04798 100644 --- a/WASDMenuAPI - SRC Files/Interfaces/IWasdMenu.cs +++ b/WASDMenuAPI - SRC Files/Interfaces/IWasdMenu.cs @@ -5,6 +5,8 @@ namespace WASDSharedAPI; public interface IWasdMenu { public string? Title { get; set; } + public string? ItemText { get; set; } + public string? ItemHoverText { get; set; } public string? ControlText { get; set; } public LinkedList? Options { get; set; } // previous option node diff --git a/WASDMenuAPI - SRC Files/Interfaces/IWasdMenuManager.cs b/WASDMenuAPI - SRC Files/Interfaces/IWasdMenuManager.cs index b895851..911a9a4 100644 --- a/WASDMenuAPI - SRC Files/Interfaces/IWasdMenuManager.cs +++ b/WASDMenuAPI - SRC Files/Interfaces/IWasdMenuManager.cs @@ -15,7 +15,7 @@ public interface IWasdMenuManager //open menu as submenu public void OpenSubMenu(CCSPlayerController? player, IWasdMenu? menu); //create new menu object - public IWasdMenu CreateMenu(string title = "", string ControlText = ""); + public IWasdMenu CreateMenu(string title, string itemText, string itemHoverText, string ControlText); public bool HasMenu(CCSPlayerController? player); public void UpdateActiveMenu(CCSPlayerController? player, Dictionary> list); } \ No newline at end of file diff --git a/jRandomSkills - SRC Files/!jRandomSkills.csproj b/jRandomSkills - SRC Files/!jRandomSkills.csproj index 0683baa..f46d9e5 100644 --- a/jRandomSkills - SRC Files/!jRandomSkills.csproj +++ b/jRandomSkills - SRC Files/!jRandomSkills.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/jRandomSkills - SRC Files/src/command/Command.cs b/jRandomSkills - SRC Files/src/command/Command.cs index 97e70a1..709aca4 100644 --- a/jRandomSkills - SRC Files/src/command/Command.cs +++ b/jRandomSkills - SRC Files/src/command/Command.cs @@ -5,7 +5,7 @@ using CounterStrikeSharp.API.Modules.Commands; using CounterStrikeSharp.API.Modules.Entities.Constants; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.utils; -using System.Reflection.Metadata; +using System.Collections.Concurrent; using static jRandomSkills.jRandomSkills; namespace jRandomSkills @@ -13,32 +13,50 @@ namespace jRandomSkills public static class Command { private static bool gamePaused = false; - private static readonly Config.Settings config = Config.LoadedConfig.Settings; + private static Config.SettingsModel config = Config.LoadedConfig; + private static readonly ConcurrentDictionary oldCommands = []; + private static readonly object setLock = new(); public static void Load() { + config = Config.LoadedConfig; if (config == null || config == null) return; - var commands = new Dictionary, (string description, CommandInfo.CommandCallback handler)> - { - { SplitCommands(config.SetSkillCommands.Alias), ("Set skill", Command_SetSkill) }, - { SplitCommands(config.SkillsListCommands.Alias), ("Delete all records", Command_SkillsListMenu) }, - { SplitCommands(config.UseSkillCommands.Alias), ("Use/Type skill", Command_UseTypeSkill) }, - { SplitCommands(config.ChangeMapCommands.Alias), ("Change map", Command_ChangeMap) }, - { SplitCommands(config.ConsoleCommands.Alias), ("Console command", Command_CustomCommand) }, - { SplitCommands(config.StartGameCommands.Alias), ("Start game", Command_StartGame) }, - { SplitCommands(config.SwapCommands.Alias), ("Swap team", Command_Swap) }, - { SplitCommands(config.ShuffleCommands.Alias), ("Shuffle team", Command_Shuffle) }, - { SplitCommands(config.PauseCommands.Alias), ("Pause game", Command_Pause) }, - { SplitCommands(config.HealCommands.Alias), ("Heal", Command_Heal) }, - { SplitCommands(config.SetScoreCommands.Alias), ("Set teams score", Command_SetScore) }, - { SplitCommands(config.SetStaticSkillCommands.Alias), ("Set static skill", Command_SetStaticSkill) }, - { SplitCommands(config.ChangeLanguageCommands.Alias), ("Change language", Command_ChangeLanguage) }, - { SplitCommands(config.ReloadCommands.Alias), ("Reaload configs", Command_Reload) }, - }; - foreach (var commandPair in commands) - foreach (var command in commandPair.Key) - Instance.AddCommand($"css_{command}", commandPair.Value.description, commandPair.Value.handler); + lock (setLock) + { + if (oldCommands.Count != 0) + { + foreach (var command in oldCommands) + Instance.RemoveCommand(command.Key, command.Value); + oldCommands.Clear(); + } + + var commands = new Dictionary, (string description, CommandInfo.CommandCallback handler)> + { + { SplitCommands(config.NormalCommands.SetSkillCommand.Alias), ("Set skill", Command_SetSkill) }, + { SplitCommands(config.NormalCommands.SkillsListCommand.Alias), ("Delete all records", Command_SkillsListMenu) }, + { SplitCommands(config.NormalCommands.UseSkillCommand.Alias), ("Use/Type skill", Command_UseTypeSkill) }, + { SplitCommands(config.NormalCommands.ConsoleCommand.Alias), ("Console command", Command_CustomCommand) }, + { SplitCommands(config.NormalCommands.HealCommand.Alias), ("Heal", Command_Heal) }, + { SplitCommands(config.NormalCommands.SetStaticSkillCommand.Alias), ("Set static skill", Command_SetStaticSkill) }, + { SplitCommands(config.NormalCommands.ChangeLanguageCommand.Alias), ("Change language", Command_ChangeLanguage) }, + { SplitCommands(config.NormalCommands.ReloadCommand.Alias), ("Reaload configs", Command_Reload) }, + + { SplitCommands(config.VotingCommands.ChangeMapCommand.Alias), ("Change map", Command_ChangeMap) }, + { SplitCommands(config.VotingCommands.StartGameCommand.Alias), ("Start game", Command_StartGame) }, + { SplitCommands(config.VotingCommands.SwapCommand.Alias), ("Swap team", Command_Swap) }, + { SplitCommands(config.VotingCommands.ShuffleCommand.Alias), ("Shuffle team", Command_Shuffle) }, + { SplitCommands(config.VotingCommands.PauseCommand.Alias), ("Pause game", Command_Pause) }, + { SplitCommands(config.VotingCommands.SetScoreCommand.Alias), ("Set teams score", Command_SetScore) }, + }; + + foreach (var commandPair in commands) + foreach (var command in commandPair.Key) + { + Instance.AddCommand($"css_{command}", commandPair.Value.description, commandPair.Value.handler); + oldCommands.TryAdd($"css_{command}", commandPair.Value.handler); + } + } } private static IEnumerable SplitCommands(string commands) @@ -69,7 +87,7 @@ namespace jRandomSkills private static void Command_SetSkill(CCSPlayerController? player, CommandInfo command) { Debug.WriteToDebug($"Player {player?.PlayerPawn} used the css_setskill {command.ArgString} command."); - if (!AdminManager.PlayerHasPermissions(player, config.SetSkillCommands.Permissions)) return; + if (!AdminManager.PlayerHasPermissions(player, config.NormalCommands.SetSkillCommand.Permissions)) return; var targetPlayer = Utilities.GetPlayers().FirstOrDefault(p => !p.IsBot && (p.SteamID.ToString().Equals(command.GetArg(1), StringComparison.CurrentCultureIgnoreCase) || p.PlayerName.Equals(command.GetArg(1), StringComparison.CurrentCultureIgnoreCase))); @@ -123,7 +141,7 @@ namespace jRandomSkills skillPlayer.Skill = skill.Skill; skillPlayer.SpecialSkill = src.player.Skills.None; Instance.SkillAction(skill.Skill.ToString(), "EnableSkill", [targetPlayer]); - skillPlayer.SkillDescriptionHudExpired = DateTime.Now.AddSeconds(Config.LoadedConfig.Settings.SkillDescriptionDuration); + skillPlayer.SkillDescriptionHudExpired = DateTime.Now.AddSeconds(Config.LoadedConfig.SkillDescriptionDuration); if (player == null) { @@ -156,7 +174,7 @@ namespace jRandomSkills private static void Command_SkillsListMenu(CCSPlayerController? player, CommandInfo command) { Debug.WriteToDebug($"Player {player?.PlayerPawn} used the css_skills {command.ArgString} command."); - if (player == null || !AdminManager.PlayerHasPermissions(player, config.SkillsListCommands.Permissions)) return; + if (player == null || !AdminManager.PlayerHasPermissions(player, config.NormalCommands.SkillsListCommand.Permissions)) return; Menu.DisplaySkillsList(player); } @@ -164,9 +182,9 @@ namespace jRandomSkills private static void Command_ChangeMap(CCSPlayerController? player, CommandInfo command) { Debug.WriteToDebug($"Player {player?.PlayerPawn} used the css_map {command.ArgString} command."); - if (player != null && player.IsValid && !AdminManager.PlayerHasPermissions(player, config.ChangeMapCommands.Permissions)) + if (player != null && player.IsValid && !AdminManager.PlayerHasPermissions(player, config.VotingCommands.ChangeMapCommand.Permissions)) { - if (!config.ChangeMapCommands.EnableVoting) return; + if (!config.VotingCommands.ChangeMapCommand.EnableVoting) return; player.Vote(VoteType.ChangeMap, command.ArgString); return; } @@ -197,9 +215,9 @@ namespace jRandomSkills private static void Command_StartGame(CCSPlayerController? player, CommandInfo command) { Debug.WriteToDebug($"Player {player?.PlayerPawn} used the css_start {command.ArgString} command."); - if (player != null && player.IsValid && !AdminManager.PlayerHasPermissions(player, config.StartGameCommands.Permissions)) + if (player != null && player.IsValid && !AdminManager.PlayerHasPermissions(player, config.VotingCommands.StartGameCommand.Permissions)) { - if (!config.StartGameCommands.EnableVoting) return; + if (!config.VotingCommands.StartGameCommand.EnableVoting) return; player.Vote(VoteType.StartGame); return; } @@ -209,10 +227,11 @@ namespace jRandomSkills private static void StartGame(CommandInfo command) { int cheats = command.GetArg(1) == "sv" ? 1 : 0; - Server.ExecuteCommand($"mp_freezetime {(cheats == 1 ? 0 : 15)}"); - Server.ExecuteCommand("mp_overtime_enable 1"); - Server.ExecuteCommand($"sv_cheats {cheats}"); - Server.ExecuteCommand("mp_forcecamera 0"); + + foreach (string consoleCommand in cheats == 1 + ? Config.LoadedConfig.VotingCommands.StartGameCommand.SVStartParams.Split(";") + : Config.LoadedConfig.VotingCommands.StartGameCommand.StartParams.Split(";")) + Server.ExecuteCommand(consoleCommand); if (Instance?.GameRules?.WarmupPeriod == true) { @@ -230,9 +249,9 @@ namespace jRandomSkills private static void Command_Swap(CCSPlayerController? player, CommandInfo command) { Debug.WriteToDebug($"Player {player?.PlayerPawn} used the css_swap {command.ArgString} command."); - if (player != null && player.IsValid && !AdminManager.PlayerHasPermissions(player, config.SwapCommands.Permissions)) + if (player != null && player.IsValid && !AdminManager.PlayerHasPermissions(player, config.VotingCommands.SwapCommand.Permissions)) { - if (!config.SwapCommands.EnableVoting) return; + if (!config.VotingCommands.SwapCommand.EnableVoting) return; player.Vote(VoteType.SwapTeam); return; } @@ -251,9 +270,9 @@ namespace jRandomSkills private static void Command_Shuffle(CCSPlayerController? player, CommandInfo command) { Debug.WriteToDebug($"Player {player?.PlayerPawn} used the css_shuffle {command.ArgString} command."); - if (player != null && player.IsValid && !AdminManager.PlayerHasPermissions(player, config.ShuffleCommands.Permissions)) + if (player != null && player.IsValid && !AdminManager.PlayerHasPermissions(player, config.VotingCommands.ShuffleCommand.Permissions)) { - if (!config.ShuffleCommands.EnableVoting) return; + if (!config.VotingCommands.ShuffleCommand.EnableVoting) return; player.Vote(VoteType.ShuffleTeam); return; } @@ -277,9 +296,9 @@ namespace jRandomSkills private static void Command_Pause(CCSPlayerController? player, CommandInfo command) { Debug.WriteToDebug($"Player {player?.PlayerPawn} used the css_pause {command.ArgString} command."); - if (player != null && player.IsValid && !AdminManager.PlayerHasPermissions(player, config.PauseCommands.Permissions)) + if (player != null && player.IsValid && !AdminManager.PlayerHasPermissions(player, config.VotingCommands.PauseCommand.Permissions)) { - if (!config.PauseCommands.EnableVoting) return; + if (!config.VotingCommands.PauseCommand.EnableVoting) return; player.Vote(VoteType.PauseGame); return; } @@ -298,7 +317,7 @@ namespace jRandomSkills { Debug.WriteToDebug($"Player {player?.PlayerPawn} used the css_heal {command.ArgString} command."); if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid || player.LifeState != (byte)LifeState_t.LIFE_ALIVE) return; - if (!AdminManager.PlayerHasPermissions(player, config.HealCommands.Permissions)) return; + if (!AdminManager.PlayerHasPermissions(player, config.NormalCommands.HealCommand.Permissions)) return; SkillUtils.AddHealth(player.PlayerPawn.Value, 100); player.PrintToChat($" {ChatColors.Green}{player.GetTranslation("healed")}"); } @@ -307,9 +326,9 @@ namespace jRandomSkills private static void Command_SetScore(CCSPlayerController? player, CommandInfo command) { Debug.WriteToDebug($"Player {player?.PlayerPawn} used the css_setscore {command.ArgString} command."); - if (player != null && player.IsValid && !AdminManager.PlayerHasPermissions(player, config.SetScoreCommands.Permissions)) + if (player != null && player.IsValid && !AdminManager.PlayerHasPermissions(player, config.VotingCommands.SetScoreCommand.Permissions)) { - if (!config.SetScoreCommands.EnableVoting) return; + if (!config.VotingCommands.SetScoreCommand.EnableVoting) return; player.Vote(VoteType.SetScore, command.ArgString); return; } @@ -332,7 +351,7 @@ namespace jRandomSkills private static void Command_CustomCommand(CCSPlayerController? player, CommandInfo command) { Debug.WriteToDebug($"Player {player?.PlayerPawn} used the css_console {command.ArgString} command."); - if (player == null || !AdminManager.PlayerHasPermissions(player, config.ConsoleCommands.Permissions)) return; + if (player == null || !AdminManager.PlayerHasPermissions(player, config.NormalCommands.ConsoleCommand.Permissions)) return; string param = command.ArgString; Server.ExecuteCommand(param); } @@ -341,7 +360,7 @@ namespace jRandomSkills private static void Command_SetStaticSkill(CCSPlayerController? player, CommandInfo command) { Debug.WriteToDebug($"Player {player?.PlayerPawn} used the css_setstaticskill {command.ArgString} command."); - if (!AdminManager.PlayerHasPermissions(player, config.SetStaticSkillCommands.Permissions)) return; + if (!AdminManager.PlayerHasPermissions(player, config.NormalCommands.SetStaticSkillCommand.Permissions)) return; var targetPlayer = Utilities.GetPlayers().FirstOrDefault(p => !p.IsBot && (p.SteamID.ToString().Equals(command.GetArg(1), StringComparison.CurrentCultureIgnoreCase) || p.PlayerName.Equals(command.GetArg(1), StringComparison.CurrentCultureIgnoreCase))); @@ -397,12 +416,12 @@ namespace jRandomSkills Instance.SkillAction(skillPlayer.Skill.ToString(), "DisableSkill", [targetPlayer]); skillPlayer.Skill = skill.Skill; skillPlayer.SpecialSkill = src.player.Skills.None; - skillPlayer.SkillDescriptionHudExpired = DateTime.Now.AddSeconds(Config.LoadedConfig.Settings.SkillDescriptionDuration); + skillPlayer.SkillDescriptionHudExpired = DateTime.Now.AddSeconds(Config.LoadedConfig.SkillDescriptionDuration); if (skill.Skill == src.player.Skills.None) - Event.staticSkills.Remove(targetPlayer.SteamID); + Event.staticSkills.TryRemove(targetPlayer.SteamID, out _); else - Event.staticSkills.Add(targetPlayer.SteamID, skill); + Event.staticSkills.TryAdd(targetPlayer.SteamID, skill); Instance.SkillAction(skill.Skill.ToString(), "EnableSkill", [targetPlayer]); if (player == null) @@ -437,33 +456,41 @@ namespace jRandomSkills { Debug.WriteToDebug($"Player {player?.PlayerPawn} used the css_lang {command.ArgString} command."); if (player == null || !player.IsValid) return; - if (!AdminManager.PlayerHasPermissions(player, config.ChangeLanguageCommands.Permissions)) return; + if (!AdminManager.PlayerHasPermissions(player, config.NormalCommands.ChangeLanguageCommand.Permissions)) return; - string newLangCode = command.GetArg(1); - if (Localization.portugalIsoCodes.Contains(newLangCode.ToUpper())) - newLangCode = "pt-br"; - if (Localization.chinaIsoCodes.Contains(newLangCode.ToUpper())) - newLangCode = "zh"; - if (Localization.frenchIsoCodes.Contains(newLangCode.ToUpper())) - newLangCode = "fr"; - newLangCode = newLangCode.ToLower(); + string fileName = Config.LoadedConfig.LanguageSystem.DefaultLangCode; + string newLangCode = command.GetArg(1).ToUpper(); + foreach (var langInfo in Config.LoadedConfig.LanguageSystem.LanguageInfos) + if (langInfo.IsoCodes.Contains(newLangCode)) + fileName = langInfo.FileName; if (!Localization.HasTranslation(newLangCode)) - newLangCode = "en"; - Localization.ChangePlayerLanguage(player, newLangCode); + fileName = Config.LoadedConfig.LanguageSystem.DefaultLangCode; + else + fileName = newLangCode.ToLower(); + Localization.ChangePlayerLanguage(player, fileName); } [CommandHelper(minArgs: 0, whoCanExecute: CommandUsage.CLIENT_AND_SERVER)] private static void Command_Reload(CCSPlayerController? player, CommandInfo command) { Debug.WriteToDebug($"Player {player?.PlayerPawn} used the css_lang {command.ArgString} command."); - if (!AdminManager.PlayerHasPermissions(player, config.ReloadCommands.Permissions)) return; + if (!AdminManager.PlayerHasPermissions(player, config.NormalCommands.ReloadCommand.Permissions)) return; - Config.LoadConfig(); - Localization.Load(); - if (player != null && player.IsValid) - player.PrintToChat($" {ChatColors.Green}{player.GetTranslation("reload")}"); - else - Server.PrintToConsole($" {ChatColors.Green}{Localization.GetTranslation("reload")}"); + lock (setLock) + { + Config.LoadConfig(); + SkillsInfo.LoadSkillsInfo(); + Localization.Load(); + Command.Load(); + + foreach (var skill in SkillData.Skills) + skill.Color = SkillsInfo.GetValue(skill.Skill, "color"); + + if (player != null && player.IsValid) + player.PrintToChat($" {ChatColors.Green}{player.GetTranslation("reload")}"); + else + Server.PrintToConsole($" {ChatColors.Green}{Localization.GetTranslation("reload")}"); + } } } } \ No newline at end of file diff --git a/jRandomSkills - SRC Files/src/command/VoteSystem.cs b/jRandomSkills - SRC Files/src/command/VoteSystem.cs index 8dac8c0..c4aa232 100644 --- a/jRandomSkills - SRC Files/src/command/VoteSystem.cs +++ b/jRandomSkills - SRC Files/src/command/VoteSystem.cs @@ -2,12 +2,13 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.utils; +using System.Collections.Concurrent; namespace jRandomSkills { public static class VoteSystem { - private static readonly HashSet votes = []; + private static readonly ConcurrentDictionary votes = []; private static VoteData? CreateVote(VoteType voteType, string? args = null) { @@ -19,7 +20,7 @@ namespace jRandomSkills if (vote.MinimumPlayersToStartVoting > Utilities.GetPlayers().Count(p => !p.IsBot)) return null; - votes.Add(vote); + votes.TryAdd(vote, 0); string commandName = $"!{VoteTypeCommands.GetCommand(vote.Type)?.Replace("css_", "")}{(!string.IsNullOrEmpty(vote?.Args) ? $" {vote?.Args}" : "")}"; Localization.PrintTranslationToChatAll($" {ChatColors.Lime}{{0}}", ["vote_started"], [commandName]); @@ -29,7 +30,7 @@ namespace jRandomSkills if (vote == null) return vote; jRandomSkills.Instance.AddTimer(vote.TimeToVote, () => { - if (!votes.Contains(vote) || !vote.GetActive()) return; + if (!votes.ContainsKey(vote) || !vote.GetActive()) return; vote.SetActive(false); vote.TimeToNextSameVoting = vote.TimeToNextVoting; Localization.PrintTranslationToChatAll($" {ChatColors.Red}{{0}}", ["vote_timeout"], [commandName]); @@ -38,23 +39,23 @@ namespace jRandomSkills float[] times = [vote.TimeToVote, vote.TimeToVote + vote.TimeToNextVoting, vote.TimeToVote + vote.TimeToNextSameVoting]; jRandomSkills.Instance.AddTimer(times.Max(), () => { - if (!votes.Contains(vote)) return; - votes.Remove(vote); + if (!votes.ContainsKey(vote)) return; + votes.TryRemove(vote, out _); }); return vote; } public static void Vote(this CCSPlayerController player, VoteType voteType, string? args = null) { - var vote = votes.FirstOrDefault(v => v.Type == voteType && v.Args == args && v.GetActive()); + var vote = votes.Keys.FirstOrDefault(v => v.Type == voteType && v.Args == args && v.GetActive()); if (vote == null) { - if (votes.Any(v => v.NextVoting() > DateTime.Now)) + if (votes.Keys.Any(v => v.NextVoting() > DateTime.Now)) { player.PrintToChat($" {ChatColors.Red}{player.GetTranslation("vote_wait")}"); return; } - else if (votes.Any(v => v.Type == voteType && v.NextSameVoting() > DateTime.Now)) + else if (votes.Keys.Any(v => v.Type == voteType && v.NextSameVoting() > DateTime.Now)) { player.PrintToChat($" {ChatColors.Red}{player.GetTranslation("vote_same_wait")}"); return; @@ -69,7 +70,7 @@ namespace jRandomSkills return; } - if (!vote.PlayersVoted.Add(player.SteamID)) + if (!vote.PlayersVoted.TryAdd(player.SteamID, 0)) player.PrintToChat($" {ChatColors.Red}{player.GetTranslation("vote_alredy_voted")}"); else CheckVote(vote); } @@ -101,7 +102,7 @@ namespace jRandomSkills public int MinimumPlayersToStartVoting { get; set; } = minimumPlayersToStartVoting; public VoteType Type { get; set; } = type; public string? Args { get; set; } = args; - public HashSet PlayersVoted { get; set; } = []; + public ConcurrentDictionary PlayersVoted { get; set; } = []; private DateTime CreatedTime { get; set; } = DateTime.Now; @@ -138,15 +139,15 @@ namespace jRandomSkills public static class VoteTypeCommands { - private static readonly Dictionary names = new() - { - { VoteType.StartGame, "css_start" }, - { VoteType.PauseGame, "css_pause" }, - { VoteType.ShuffleTeam, "css_shuffle" }, - { VoteType.SwapTeam, "css_swap" }, - { VoteType.ChangeMap, "css_map" }, - { VoteType.SetScore, "css_setscore" }, - }; + private static readonly ConcurrentDictionary names = new( + [ + new KeyValuePair(VoteType.StartGame, "css_start"), + new KeyValuePair(VoteType.PauseGame, "css_pause"), + new KeyValuePair(VoteType.ShuffleTeam, "css_shuffle"), + new KeyValuePair(VoteType.SwapTeam, "css_swap"), + new KeyValuePair(VoteType.ChangeMap, "css_map"), + new KeyValuePair(VoteType.SetScore, "css_setscore"), + ]); public static string GetCommand(VoteType type) => names[type]; } diff --git a/jRandomSkills - SRC Files/src/jRandomSkills.cs b/jRandomSkills - SRC Files/src/jRandomSkills.cs index 0101ca6..feb2be4 100644 --- a/jRandomSkills - SRC Files/src/jRandomSkills.cs +++ b/jRandomSkills - SRC Files/src/jRandomSkills.cs @@ -19,17 +19,19 @@ namespace jRandomSkills public Random Random { get; } = new Random(); public CCSGameRules? GameRules { get; set; } public IWasdMenuManager? MenuManager; + public const string Tag = "jRandomSkills"; public override string ModuleName => "[CS2] [ jRandomSkills ]"; public override string ModuleAuthor => "D3X, Juzlus"; public override string ModuleDescription => "Plugin adds random skills every round for CS2 by D3X. Modified by Juzlus."; - public override string ModuleVersion => "1.1.5"; + public override string ModuleVersion => "1.1.7"; public override void Load(bool hotReload) { Instance = this; Config.LoadConfig(); + SkillsInfo.LoadSkillsInfo(); Localization.Load(); Debug.Load(); PlayerOnTick.Load(); @@ -42,11 +44,11 @@ namespace jRandomSkills internal void LoadAllSkills() { foreach (var skill in Enum.GetValues(typeof(Skills))) - if (Config.GetValue(skill, "active")) + if (SkillsInfo.GetValue(skill, "active")) SkillAction(skill.ToString()!, "LoadSkill"); - Debug.WriteToDebug($"jRandomSkills v{Instance.ModuleVersion} ({SkillData.Skills.Count - 1}/{Config.LoadedConfig.SkillsInfo.Length - 1} Skills) loaded!"); - Debug.WriteToDebug($"GameModes: {(Config.GameModes)Config.LoadedConfig.Settings.GameMode}, Lang: {Config.LoadedConfig.Settings.LangCode}"); + Debug.WriteToDebug($"jRandomSkills v{Instance.ModuleVersion} ({SkillData.Skills.Count - 1}/{SkillsInfo.LoadedConfig.Count - 1} Skills) loaded!"); + Debug.WriteToDebug($"GameModes: {(Config.GameModes)Config.LoadedConfig.GameMode}, Lang: {Config.LoadedConfig.LanguageSystem.DefaultLangCode}"); foreach (var skill in SkillData.Skills) Debug.WriteToDebug($"Loaded: {skill.Skill}"); } @@ -106,6 +108,7 @@ namespace jRandomSkills public float? SkillChance { get; set; } public bool IsDrawing { get; set; } public DateTime SkillDescriptionHudExpired { get; set; } + public string? PrintHTML { get; set; } } #pragma warning disable IDE1006 @@ -113,7 +116,7 @@ namespace jRandomSkills #pragma warning restore IDE1006 { public Skills Skill { get; } = skill; - public string Color { get; } = color; + public string Color { get; set; } = color; public bool Display { get; } = display; public static implicit operator Skills(jSkill_SkillInfo v) diff --git a/jRandomSkills - SRC Files/src/lang/en.json b/jRandomSkills - SRC Files/src/lang/en.json index 1cb54d3..d3b93e4 100644 --- a/jRandomSkills - SRC Files/src/lang/en.json +++ b/jRandomSkills - SRC Files/src/lang/en.json @@ -215,7 +215,7 @@ "lifeswap_select_info": "Swap health with player:", "longknife": "Long Knife", - "longknife_desc": "A primary knife attack deals damage regardless of distance", + "longknife_desc": "A knife attack deals damage regardless of distance", "longzeus": "Long Zeus", "longzeus_desc": "Zeus deals damage regardless of distance", @@ -237,6 +237,7 @@ "muhammed": "Muhammed", "muhammed_desc": "You explode upon death, killing nearby players", + "muhammed_explosion": "ALLAHU AKBAR!!!", "ninja": "Ninja", "ninja_desc": "Standing still increases your invisibility by 33%, crouching by 33%, and holding a knife by 33%", @@ -452,5 +453,7 @@ "summary_start": "======SUMMARY=OF=THE=LAST=ROUND======", "summary_end": "=======================================", - "menu_controlls": "W/S - Scroll | E - Select
" + "menu_controlls_scroll": "W/S - Scroll", + "menu_controlls_padding": " | ", + "menu_controlls_select": "E - Select" } \ No newline at end of file diff --git a/jRandomSkills - SRC Files/src/lang/fr.json b/jRandomSkills - SRC Files/src/lang/fr.json index 8131605..d5548be 100644 --- a/jRandomSkills - SRC Files/src/lang/fr.json +++ b/jRandomSkills - SRC Files/src/lang/fr.json @@ -215,7 +215,7 @@ "lifeswap_select_info": "Échanger la santé avec le joueur :", "longknife": "Couteau Long", - "longknife_desc": "Une attaque primaire au couteau inflige des dégâts quelle que soit la distance", + "longknife_desc": "Une attaque au couteau inflige des dégâts quelle que soit la distance", "longzeus": "Zeus Longue Portée", "longzeus_desc": "Le Zeus inflige des dégâts quelle que soit la distance", @@ -237,6 +237,7 @@ "muhammed": "Bombe", "muhammed_desc": "Vous explosez à votre mort, tuant les joueurs proches", + "muhammed_explosion": "ALLAHU AKBAR!!!", "ninja": "Ninja", "ninja_desc": "Rester immobile augmente votre invisibilité de 33%, s’accroupir de 33%, et tenir un couteau de 33%", @@ -452,5 +453,7 @@ "summary_start": "======RÉSUMÉ=DU=DERNIER=ROUND======", "summary_end": "===================================", - "menu_controlls": "W/S - Défilement | E - Sélectionner
" -} \ No newline at end of file + "menu_controlls_scroll": "W/S - Défilement", + "menu_controlls_padding": " | ", + "menu_controlls_select": "E - Sélectionner" +} diff --git a/jRandomSkills - SRC Files/src/lang/pl.json b/jRandomSkills - SRC Files/src/lang/pl.json index b84c5ac..60a9b5f 100644 --- a/jRandomSkills - SRC Files/src/lang/pl.json +++ b/jRandomSkills - SRC Files/src/lang/pl.json @@ -215,7 +215,7 @@ "lifeswap_select_info": "Zamień się zdrowiem z graczem:", "longknife": "Długi Nóż", - "longknife_desc": "Podstawowy atak nożem zadaje obrażenia niezależnie od odległości", + "longknife_desc": "Atak nożem zadaje obrażenia niezależnie od odległości", "longzeus": "Długi Zeus", "longzeus_desc": "Zeus zadaje obrażenia niezależnie od odległości", @@ -237,6 +237,7 @@ "muhammed": "Muhammed", "muhammed_desc": "Po śmierci eksplodujesz i zabijasz graczy znajdujących się w zasięgu", + "muhammed_explosion": "ALLAHU AKBAR!!!", "ninja": "Ninja", "ninja_desc": "Stojąc nieruchomo zwiększasz swoją niewidzialność o 33%, kucając o 33%, a trzymając nóż o 33%", @@ -452,5 +453,7 @@ "summary_start": "======PODSUMOWANIE=Z=OSTATNIEJ=RUNDY======", "summary_end": "============================================", - "menu_controlls": "W/S – Przewijanie | E - Wybierz
" + "menu_controlls_scroll": "W/S – Przewijanie", + "menu_controlls_padding": " | ", + "menu_controlls_select": "E - Wybierz" } \ No newline at end of file diff --git a/jRandomSkills - SRC Files/src/lang/pt-br.json b/jRandomSkills - SRC Files/src/lang/pt-br.json index fb05ea3..180a374 100644 --- a/jRandomSkills - SRC Files/src/lang/pt-br.json +++ b/jRandomSkills - SRC Files/src/lang/pt-br.json @@ -215,7 +215,7 @@ "lifeswap_select_info": "Trocar vida com jogador:", "longknife": "Faca Longa", - "longknife_desc": "Um ataque com faca primária causa dano independentemente da distância", + "longknife_desc": "Um ataque com faca causa danos independentemente da distância", "longzeus": "Zeus Longo", "longzeus_desc": "Zeus causa dano independentemente da distância", @@ -237,6 +237,7 @@ "muhammed": "Muhammed", "muhammed_desc": "Você explode ao morrer, matando jogadores próximos", + "muhammed_explosion": "ALLAHU AKBAR!!!", "ninja": "Ninja", "ninja_desc": "Ficar parado aumenta sua invisibilidade em 33%, agachar em 33%, e segurar uma faca em 33%", @@ -452,5 +453,7 @@ "summary_start": "=======RESUMO=DA=ÚLTIMA=RODADA=======", "summary_end": "=======================================", - "menu_controlls": "W/S - Rolar | E - Selecionar
" + "menu_controlls_scroll": "W/S - Rolar", + "menu_controlls_padding": " | ", + "menu_controlls_select": "E - Selecionar" } \ No newline at end of file diff --git a/jRandomSkills - SRC Files/src/lang/zh.json b/jRandomSkills - SRC Files/src/lang/zh.json index b6263c9..7b51cba 100644 --- a/jRandomSkills - SRC Files/src/lang/zh.json +++ b/jRandomSkills - SRC Files/src/lang/zh.json @@ -215,7 +215,7 @@ "lifeswap_select_info": "选择你想与之交换生命值的玩家:", "longknife": "长刀", - "longknife_desc": "近战刀具攻击无论距离远近均可造成伤害", + "longknife_desc": "刀具攻击造成的伤害与距离无关", "longzeus": "长宙斯", "longzeus_desc": "宙斯电击枪无论距离多远都能造成伤害", @@ -237,6 +237,7 @@ "muhammed": "穆罕默德", "muhammed_desc": "你死亡时会爆炸,杀死附近的玩家", + "muhammed_explosion": "真主至大!!!", "ninja": "忍者", "ninja_desc": "站立不动增加33%隐形,蹲下增加33%,持刀增加33%", @@ -452,5 +453,7 @@ "summary_start": "=============上一回合总结==============", "summary_end": "=======================================", - "menu_controlls": "W/S - 滚动 | E - 选择
" + "menu_controlls_scroll": "W/S - 滚动", + "menu_controlls_padding": " | ", + "menu_controlls_select": "E - 选择" } \ No newline at end of file diff --git a/jRandomSkills - SRC Files/src/player/Debug.cs b/jRandomSkills - SRC Files/src/player/Debug.cs index ac762c6..1f84ebb 100644 --- a/jRandomSkills - SRC Files/src/player/Debug.cs +++ b/jRandomSkills - SRC Files/src/player/Debug.cs @@ -127,7 +127,7 @@ namespace jRandomSkills public static void WriteToDebug(string message) { - if (Config.LoadedConfig.Settings.DebugMode != true) + if (Config.LoadedConfig.DebugMode != true) return; string filename = $"debug_{sessionId}.txt"; diff --git a/jRandomSkills - SRC Files/src/player/PlayerEvents.cs b/jRandomSkills - SRC Files/src/player/PlayerEvents.cs index 684e73c..3a36b78 100644 --- a/jRandomSkills - SRC Files/src/player/PlayerEvents.cs +++ b/jRandomSkills - SRC Files/src/player/PlayerEvents.cs @@ -10,25 +10,28 @@ using jRandomSkills.src.player; using jRandomSkills.src.utils; using static CounterStrikeSharp.API.Core.Listeners; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public static partial class Event { + public static DateTime freezeTimeEnd = DateTime.MinValue; public static bool isTransmitRegistered = false; - public static readonly jSkill_SkillInfo noneSkill = new(Skills.None, Config.GetValue(Skills.None, "color"), false); + public static readonly jSkill_SkillInfo noneSkill = new(Skills.None, SkillsInfo.GetValue(Skills.None, "color"), false); private static jSkill_SkillInfo ctSkill = noneSkill; private static jSkill_SkillInfo tSkill = noneSkill; private static jSkill_SkillInfo allSkill = noneSkill; private static List debugSkills = new(SkillData.Skills); - public static readonly Config.DefaultSkillInfo[] terroristSkills = Config.LoadedConfig.SkillsInfo.Where(s => s.OnlyTeam == (int)CsTeam.Terrorist).ToArray(); - public static readonly Config.DefaultSkillInfo[] counterterroristSkills = Config.LoadedConfig.SkillsInfo.Where(s => s.OnlyTeam == (int)CsTeam.CounterTerrorist).ToArray(); - private static readonly Config.DefaultSkillInfo[] allTeamsSkills = Config.LoadedConfig.SkillsInfo.Where(s => s.OnlyTeam == 0).ToArray(); + public static readonly SkillsInfo.DefaultSkillInfo[] terroristSkills = SkillsInfo.LoadedConfig.Where(s => s.OnlyTeam == (int)CsTeam.Terrorist).ToArray(); + public static readonly SkillsInfo.DefaultSkillInfo[] counterterroristSkills = SkillsInfo.LoadedConfig.Where(s => s.OnlyTeam == (int)CsTeam.CounterTerrorist).ToArray(); + private static readonly SkillsInfo.DefaultSkillInfo[] allTeamsSkills = SkillsInfo.LoadedConfig.Where(s => s.OnlyTeam == 0).ToArray(); - private static readonly Dictionary> playersSkills = []; - public static readonly Dictionary staticSkills = []; + private static readonly ConcurrentDictionary> playersSkills = []; + public static readonly ConcurrentDictionary staticSkills = []; + private static readonly object setLock = new object(); public static void Load() { @@ -172,10 +175,13 @@ namespace jRandomSkills private static HookResult PlayerHurt(EventPlayerHurt @event, GameEventInfo info) { - foreach (var playerSkill in Instance.SkillPlayer) + lock (setLock) + { + foreach (var playerSkill in Instance.SkillPlayer) if (!playerSkill.IsDrawing) Instance.SkillAction(playerSkill.Skill.ToString(), "PlayerHurt", [@event]); - return HookResult.Continue; + return HookResult.Continue; + } } private static HookResult PlayerJump(EventPlayerJump @event, GameEventInfo info) @@ -206,14 +212,10 @@ namespace jRandomSkills { foreach (var playerSkill in Instance.SkillPlayer) if (!playerSkill.IsDrawing) - Instance.SkillAction(playerSkill.Skill.ToString(), "OnTick"); - - foreach (var playerSkill in Instance.SkillPlayer) - { - if (playerSkill.SkillDescriptionHudExpired >= DateTime.Now) - if (playerSkill.Skill != Skills.None) - ShowSkillDescription(Utilities.GetPlayerFromSteamId(playerSkill.SteamID)); - } + if (SkillsInfo.GetValue(playerSkill.Skill, "disableOnFreezeTime") && SkillUtils.IsFreezeTime()) + return; + else + Instance.SkillAction(playerSkill.Skill.ToString(), "OnTick"); } private static HookResult PlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info) @@ -262,11 +264,13 @@ namespace jRandomSkills var skillPlayer = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (skillPlayer == null) continue; skillPlayer.IsDrawing = true; + skillPlayer.PrintHTML = null; } Instance.RemoveListener(CheckTransmit); int freezetime = ConVar.Find("mp_freezetime")?.GetPrimitiveValue() ?? 0; - Instance.AddTimer((Instance?.GameRules?.TeamIntroPeriod == true ? 7 : 0) + Math.Max(freezetime - Config.LoadedConfig.Settings.SkillTimeBeforeStart, 0) + .3f, SetSkill); + freezeTimeEnd = DateTime.Now.AddSeconds(freezetime + (Instance?.GameRules?.TeamIntroPeriod == true ? 7 : 0)); + Instance?.AddTimer((Instance?.GameRules?.TeamIntroPeriod == true ? 7 : 0) + Math.Max(freezetime - Config.LoadedConfig.SkillTimeBeforeStart, 0) + .3f, SetSkill); return HookResult.Continue; } @@ -298,11 +302,11 @@ namespace jRandomSkills var skillInfo = SkillData.Skills.FirstOrDefault(p => p.Skill == _playerSkill.Skill); var specialSkillInfo = SkillData.Skills.FirstOrDefault(s => s.Skill == _playerSkill.SpecialSkill); if (skillInfo == null) continue; - skillsText += $" {ChatColors.DarkRed}{_player.PlayerName}{ChatColors.Lime}: {(_playerSkill.SpecialSkill == Skills.None || specialSkillInfo == null ? player.GetSkillName(skillInfo.Skill) : $"{player.GetSkillName(specialSkillInfo.Skill)} -> {player.GetSkillName(skillInfo.Skill)}")}\n"; + skillsText += $" {ChatColors.DarkRed}{_player.PlayerName}{ChatColors.Lime}: {(_playerSkill.SpecialSkill == Skills.None || specialSkillInfo == null ? player.GetSkillName(skillInfo.Skill, _playerSkill.SkillChance) : $"{player.GetSkillName(specialSkillInfo.Skill)} -> {player.GetSkillName(skillInfo.Skill, _playerSkill.SkillChance)}")}\n"; } } - if (Config.LoadedConfig.Settings.SummaryAfterTheRound && !string.IsNullOrEmpty(skillsText)) + if (Config.LoadedConfig.SummaryAfterTheRound && !string.IsNullOrEmpty(skillsText)) { player.PrintToChat(" "); player.PrintToChat($" {ChatColors.Lime}{player.GetTranslation("summary_start")}"); @@ -319,39 +323,42 @@ namespace jRandomSkills private static HookResult PlayerDeath(EventPlayerDeath @event, GameEventInfo info) { - foreach (var playerSkill in Instance.SkillPlayer) - if (!playerSkill.IsDrawing) - Instance.SkillAction(playerSkill.Skill.ToString(), "PlayerDeath", [@event]); - - var victim = @event.Userid; - var attacker = @event.Attacker; - if (victim == null || attacker == null) return HookResult.Continue; - - var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == victim.SteamID); - if (playerInfo == null || playerInfo.IsDrawing) return HookResult.Continue; - Instance.SkillAction(playerInfo.Skill.ToString(), "DisableSkill", [victim]); - - if (victim == attacker) return HookResult.Continue; - if (Config.LoadedConfig.Settings.KillerSkillInfo) + lock (setLock) { - var attackerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == attacker.SteamID); - if (attackerInfo != null) - { - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == attackerInfo.Skill); - var specialSkillData = SkillData.Skills.FirstOrDefault(s => s.Skill == attackerInfo.SpecialSkill); - if (skillData == null || specialSkillData == null) return HookResult.Continue; - string skillDesc = victim.GetSkillDescription(skillData.Skill); + foreach (var playerSkill in Instance.SkillPlayer) + if (!playerSkill.IsDrawing) + Instance.SkillAction(playerSkill.Skill.ToString(), "PlayerDeath", [@event]); - SkillUtils.PrintToChat(victim, $"{victim.GetTranslation("enemy_skill")} {ChatColors.DarkRed}{attacker.PlayerName}{ChatColors.Lime}:", false); - SkillUtils.PrintToChat(victim, $"{ChatColors.DarkRed}{(attackerInfo.SpecialSkill == Skills.None ? victim.GetSkillName(skillData.Skill) : $"{victim.GetSkillName(specialSkillData.Skill)} -> {victim.GetSkillName(skillData.Skill)}")}{ChatColors.Lime} - {skillDesc}", false); + var victim = @event.Userid; + var attacker = @event.Attacker; + if (victim == null || attacker == null) return HookResult.Continue; + + var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == victim.SteamID); + if (playerInfo == null || playerInfo.IsDrawing) return HookResult.Continue; + Instance.SkillAction(playerInfo.Skill.ToString(), "DisableSkill", [victim]); + + if (victim == attacker) return HookResult.Continue; + if (Config.LoadedConfig.KillerSkillInfo) + { + var attackerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == attacker.SteamID); + if (attackerInfo != null) + { + var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == attackerInfo.Skill); + var specialSkillData = SkillData.Skills.FirstOrDefault(s => s.Skill == attackerInfo.SpecialSkill); + if (skillData == null || specialSkillData == null) return HookResult.Continue; + string skillDesc = victim.GetSkillDescription(skillData.Skill); + + SkillUtils.PrintToChat(victim, $"{victim.GetTranslation("enemy_skill")} {ChatColors.DarkRed}{attacker.PlayerName}{ChatColors.Lime}:", false); + SkillUtils.PrintToChat(victim, $"{ChatColors.DarkRed}{(attackerInfo.SpecialSkill == Skills.None ? victim.GetSkillName(skillData.Skill) : $"{victim.GetSkillName(specialSkillData.Skill)} -> {victim.GetSkillName(skillData.Skill)}")}{ChatColors.Lime} - {skillDesc}", false); + } } + return HookResult.Continue; } - return HookResult.Continue; } private static void CheckUseSkill(CCSPlayerController player, PlayerButtons pressed, PlayerButtons released) { - string? button = Config.LoadedConfig.Settings.AlternativeSkillButton; + string? button = Config.LoadedConfig.AlternativeSkillButton; if (string.IsNullOrEmpty(button)) return; if (Enum.TryParse(button, out var skillButton)) @@ -363,6 +370,9 @@ namespace jRandomSkills var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo == null || playerInfo.IsDrawing) return; + if (SkillsInfo.GetValue(playerInfo.Skill, "disableOnFreezeTime") && SkillUtils.IsFreezeTime()) + return; + var playerPawn = player.PlayerPawn.Value; if (playerPawn?.CBodyComponent == null) return; if (!player.IsValid || !player.PawnIsAlive) return; @@ -382,7 +392,7 @@ namespace jRandomSkills { var validPlayers = Utilities.GetPlayers().Where(p => p.IsValid && !p.IsBot && !p.IsHLTV && p .Team is CsTeam.CounterTerrorist or CsTeam.Terrorist).ToList(); - if (Config.LoadedConfig.Settings.GameMode == (int)Config.GameModes.TeamSkills) + if (Config.LoadedConfig.GameMode == (int)Config.GameModes.TeamSkills) { List tSkills = new(SkillData.Skills); tSkills.RemoveAll(s => s.Skill == tSkill.Skill || s.Skill == Skills.None || counterterroristSkills.Any(s2 => s2.Name == s.Skill.ToString())); @@ -392,13 +402,13 @@ namespace jRandomSkills ctSkills.RemoveAll(s => s.Skill == ctSkill.Skill || s.Skill == Skills.None || terroristSkills.Any(s2 => s2.Name == s.Skill.ToString())); ctSkill = ctSkills.Count == 0 ? noneSkill : ctSkills[Instance.Random.Next(ctSkills.Count)]; } - else if (Config.LoadedConfig.Settings.GameMode == (int)Config.GameModes.SameSkills) + else if (Config.LoadedConfig.GameMode == (int)Config.GameModes.SameSkills) { List allSkills = new(SkillData.Skills); allSkills.RemoveAll(s => s.Skill == allSkill.Skill || s.Skill == Skills.None || !allTeamsSkills.Any(s2 => s2.Name == s.Skill.ToString())); allSkill = allSkills.Count == 0 ? noneSkill : allSkills[Instance.Random.Next(allSkills.Count)]; } - else if (Config.LoadedConfig.Settings.GameMode == (int)Config.GameModes.Debug && debugSkills.Count == 0) + else if (Config.LoadedConfig.GameMode == (int)Config.GameModes.Debug && debugSkills.Count == 0) debugSkills = new(SkillData.Skills); foreach (var player in validPlayers) @@ -421,7 +431,7 @@ namespace jRandomSkills if (Instance?.GameRules != null && Instance?.GameRules.WarmupPeriod == false) { - Config.GameModes gameMode = (Config.GameModes)Config.LoadedConfig.Settings.GameMode; + Config.GameModes gameMode = (Config.GameModes)Config.LoadedConfig.GameMode; if (staticSkills.TryGetValue(player.SteamID, out var staticSkill)) randomSkill = staticSkill; else if (gameMode == Config.GameModes.Normal || gameMode == Config.GameModes.NoRepeat) @@ -431,7 +441,7 @@ namespace jRandomSkills if (validPlayers.Count(p => p.Team == player.Team) == 1) { - Config.DefaultSkillInfo[] skillsNeedsTeammates = Config.LoadedConfig.SkillsInfo.Where(s => s.NeedsTeammates).ToArray(); + SkillsInfo.DefaultSkillInfo[] skillsNeedsTeammates = SkillsInfo.LoadedConfig.Where(s => s.NeedsTeammates).ToArray(); skillList.RemoveAll(s => skillsNeedsTeammates.Any(s2 => s2.Name == s.Skill.ToString())); } @@ -452,7 +462,7 @@ namespace jRandomSkills if (playersSkills.TryGetValue(player.SteamID, out List? value)) value.Add(randomSkill); else - playersSkills.Add(player.SteamID, [randomSkill]); + playersSkills.TryAdd(player.SteamID, [randomSkill]); } } else if (gameMode == Config.GameModes.TeamSkills) @@ -474,21 +484,29 @@ namespace jRandomSkills skillPlayer.Skill = randomSkill.Skill; skillPlayer.SpecialSkill = Skills.None; - Instance?.SkillAction(randomSkill.Skill.ToString(), "EnableSkill", new [] { player }); - Debug.WriteToDebug($"Player {skillPlayer.PlayerName} has got the skill \"{player.GetSkillName(randomSkill.Skill)}\"."); - skillPlayer.SkillDescriptionHudExpired = DateTime.Now.AddSeconds(Config.LoadedConfig.Settings.SkillDescriptionDuration); - if (Config.LoadedConfig.Settings.TeamMateSkillInfo) + if (SkillsInfo.GetValue(randomSkill.Skill, "disableOnFreezeTime") && SkillUtils.IsFreezeTime()) + Instance?.AddTimer(Config.LoadedConfig.SkillTimeBeforeStart, () => { + if (Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID && p.Skill == randomSkill.Skill) == null) return; + Instance?.SkillAction(randomSkill.Skill.ToString(), "EnableSkill", new[] { player }); + }); + else + Instance?.SkillAction(randomSkill.Skill.ToString(), "EnableSkill", new[] { player }); + + Debug.WriteToDebug($"Player {skillPlayer.PlayerName} has got the skill \"{player.GetSkillName(randomSkill.Skill)}\"."); + skillPlayer.SkillDescriptionHudExpired = DateTime.Now.AddSeconds(Config.LoadedConfig.SkillDescriptionDuration); + + if (Config.LoadedConfig.TeamMateSkillInfo) { Instance?.AddTimer(.5f, () => { foreach (var teammate in teammates) { - var teammateSkill = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == teammate.SteamID)?.Skill; - if (teammateSkill != null) + var teammateInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == teammate.SteamID); + if (teammateInfo != null && teammateInfo?.Skill != null) { - var skillInfo = SkillData.Skills.FirstOrDefault(p => p.Skill == teammateSkill); - teammateSkills += $" {ChatColors.DarkRed}{teammate.PlayerName}{ChatColors.Lime}: {(skillInfo == null ? player.GetSkillName(Skills.None) : player.GetSkillName(skillInfo.Skill))}\n"; + var skillInfo = SkillData.Skills.FirstOrDefault(p => p.Skill == teammateInfo.Skill); + teammateSkills += $" {ChatColors.DarkRed}{teammate.PlayerName}{ChatColors.Lime}: {(skillInfo == null ? player.GetSkillName(Skills.None) : player.GetSkillName(skillInfo.Skill, teammateInfo.SkillChance))}\n"; } } @@ -508,7 +526,7 @@ namespace jRandomSkills { var validPlayers = Utilities.GetPlayers().Where(p => p.IsValid && !p.IsBot && !p.IsHLTV && p.Team is CsTeam.CounterTerrorist or CsTeam.Terrorist).ToList(); - if (Config.LoadedConfig.Settings.GameMode == (int)Config.GameModes.TeamSkills) + if (Config.LoadedConfig.GameMode == (int)Config.GameModes.TeamSkills) { List tSkills = new(SkillData.Skills); tSkills.RemoveAll(s => s.Skill == tSkill.Skill || s.Skill == Skills.None || counterterroristSkills.Any(s2 => s2.Name == s.Skill.ToString())); @@ -532,7 +550,7 @@ namespace jRandomSkills jSkill_SkillInfo randomSkill = noneSkill; if (Instance?.GameRules != null && Instance?.GameRules.WarmupPeriod == false) { - Config.GameModes gameMode = (Config.GameModes)Config.LoadedConfig.Settings.GameMode; + Config.GameModes gameMode = (Config.GameModes)Config.LoadedConfig.GameMode; if (staticSkills.TryGetValue(player.SteamID, out var staticSkill)) randomSkill = staticSkill; else if (gameMode == Config.GameModes.Normal || gameMode == Config.GameModes.NoRepeat) @@ -542,7 +560,7 @@ namespace jRandomSkills if (validPlayers.Count(p => p.Team == player.Team) == 1) { - Config.DefaultSkillInfo[] skillsNeedsTeammates = Config.LoadedConfig.SkillsInfo.Where(s => s.NeedsTeammates).ToArray(); + SkillsInfo.DefaultSkillInfo[] skillsNeedsTeammates = SkillsInfo.LoadedConfig.Where(s => s.NeedsTeammates).ToArray(); skillList.RemoveAll(s => skillsNeedsTeammates.Any(s2 => s2.Name == s.Skill.ToString())); } @@ -563,7 +581,7 @@ namespace jRandomSkills if (playersSkills.TryGetValue(player.SteamID, out List? value)) value.Add(randomSkill); else - playersSkills.Add(player.SteamID, [randomSkill]); + playersSkills.TryAdd(player.SteamID, [randomSkill]); } } else if (gameMode == Config.GameModes.TeamSkills) @@ -577,9 +595,17 @@ namespace jRandomSkills skillPlayer.Skill = randomSkill.Skill; skillPlayer.SpecialSkill = Skills.None; - Instance?.SkillAction(randomSkill.Skill.ToString(), "EnableSkill", new[] { player }); + + if (SkillsInfo.GetValue(randomSkill.Skill, "disableOnFreezeTime") && SkillUtils.IsFreezeTime()) + Instance?.AddTimer(Config.LoadedConfig.SkillTimeBeforeStart, () => { + if (Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID && p.Skill == randomSkill.Skill) == null) return; + Instance?.SkillAction(randomSkill.Skill.ToString(), "EnableSkill", new[] { player }); + }); + else + Instance?.SkillAction(randomSkill.Skill.ToString(), "EnableSkill", new[] { player }); + Debug.WriteToDebug($"Player {skillPlayer.PlayerName} has got the skill \"{player.GetSkillName(randomSkill.Skill)}\"."); - skillPlayer.SkillDescriptionHudExpired = DateTime.Now.AddSeconds(Config.LoadedConfig.Settings.SkillDescriptionDuration); + skillPlayer.SkillDescriptionHudExpired = DateTime.Now.AddSeconds(Config.LoadedConfig.SkillDescriptionDuration); } public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList infoList) @@ -589,26 +615,22 @@ namespace jRandomSkills Instance.SkillAction(playerSkill.Skill.ToString(), "CheckTransmit", [infoList]); } - private static void ShowSkillDescription(CCSPlayerController? player) + public static void UpdateSkillHUD(CCSPlayerController? player, string? headerLine, string? centerLine, string? extraLine, bool isDescription) { if (player == null || !player.IsValid) return; + var config = Config.LoadedConfig.HtmlHudCustomisation; + var emptySymbol = $" "; + var emptySymbol2 = $" "; - var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); - if (playerInfo == null) return; + string infoLine = string.IsNullOrEmpty(headerLine) + ? "" + : $"{headerLine}:
"; - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == playerInfo.Skill); - if (skillData == null) return; + string skillLine = $"{emptySymbol2}{centerLine}{emptySymbol2}"; - var skillName = playerInfo.Skill.ToString().ToLower(); - var value = Math.Round((double)(playerInfo.SkillChance ?? 1), 2); - var desc2 = player.GetTranslation($"{skillName}_desc2", value); - var skilLDescription = desc2 == $"{skillName}_desc2" - ? player.GetTranslation($"{skillName}_desc") - : (desc2.Contains('%') ? desc2.Replace(value.ToString(), (value * 100).ToString()) : desc2); - - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = $"{skilLDescription}
"; + string remainingLine = string.IsNullOrWhiteSpace(extraLine) + ? "" + : $"
{emptySymbol}{extraLine}{emptySymbol}"; var hudContent = infoLine + skillLine + remainingLine; player.PrintToCenterHtml(hudContent); diff --git a/jRandomSkills - SRC Files/src/player/PlayerOnTick.cs b/jRandomSkills - SRC Files/src/player/PlayerOnTick.cs index 2c96fe1..c104ec8 100644 --- a/jRandomSkills - SRC Files/src/player/PlayerOnTick.cs +++ b/jRandomSkills - SRC Files/src/player/PlayerOnTick.cs @@ -1,5 +1,6 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static CounterStrikeSharp.API.Core.Listeners; @@ -40,7 +41,7 @@ namespace jRandomSkills { if (Instance?.GameRules == null || Instance?.GameRules?.Handle == IntPtr.Zero) InitializeGameRules(); - else if (Instance != null && Config.LoadedConfig.Settings.FlashingHtmlHudFix) + else if (Instance != null && Config.LoadedConfig.FlashingHtmlHudFix) Instance.GameRules.GameRestart = Instance.GameRules?.RestartRoundTime < Server.CurrentTime; } @@ -52,17 +53,20 @@ namespace jRandomSkills string infoLine = ""; string skillLine = ""; + string remainingLine = ""; + bool showDescirptionHUD = skillPlayer.SkillDescriptionHudExpired >= DateTime.Now; + bool isDescription = true; if (SkillData.Skills.Count == 0) { - infoLine = $"{player.GetTranslation("your_skill")}:
"; - skillLine = $"{player.GetTranslation("none")}
"; + infoLine = player.GetTranslation("your_skill"); + skillLine = player.GetTranslation("none"); } else if (skillPlayer.IsDrawing) { var randomSkill = SkillData.Skills[Instance.Random.Next(SkillData.Skills.Count)]; - infoLine = $"{player.GetTranslation("drawing_skill")}:
"; - skillLine = $"{player.GetSkillName(randomSkill.Skill)}
"; + infoLine = player.GetTranslation("drawing_skill"); + skillLine = $"{player.GetSkillName(randomSkill.Skill)}"; } else if (!skillPlayer.IsDrawing) { @@ -71,11 +75,21 @@ namespace jRandomSkills var skillInfo = SkillData.Skills.FirstOrDefault(s => s.Skill == skillPlayer.Skill); if (skillInfo != null) { - infoLine = $"{player.GetTranslation("your_skill")}:
"; - skillLine = $"{player.GetSkillName(skillInfo.Skill)}
"; + infoLine = player.GetTranslation("your_skill"); + skillLine = $"{player.GetSkillName(skillInfo.Skill, skillPlayer.SkillChance)}"; + if (skillInfo.Skill != Skills.None) + { + remainingLine = string.IsNullOrEmpty(skillPlayer.PrintHTML) + ? showDescirptionHUD ? player.GetSkillDescription(skillInfo.Skill, skillPlayer.SkillChance) : "" + : skillPlayer.PrintHTML; + isDescription = string.IsNullOrEmpty(skillPlayer.PrintHTML); + } } - } else if (player?.IsValid == true && Config.LoadedConfig.Settings.DisableSpectateHUD) + } else if (player?.IsValid == true) { + if (player.Team is CsTeam.Spectator or CsTeam.None && Config.LoadedConfig.DisableSpectateHUD) + return; + var pawn = player.Pawn.Value; if (pawn == null) return; @@ -94,15 +108,19 @@ namespace jRandomSkills string pName = observeredPlayerSkill.PlayerName; if (pName.Length > 18) pName = $"{pName[..17]}..."; - infoLine = $"{player.GetTranslation("observer_skill")} {pName}:
"; - skillLine = $"{(observeredPlayerSkill.SpecialSkill == Skills.None ? player.GetSkillName(observeredPlayerSkillInfo.Skill) : $"{player.GetSkillName(observeredPlayerSpecialSkillInfo.Skill)}({player.GetSkillName(observeredPlayerSkillInfo.Skill)})")}
"; + var observerSkill = player.GetTranslation("observer_skill"); + infoLine = string.IsNullOrEmpty(observerSkill) ? pName : $"{observerSkill} {pName}"; + skillLine = $"{(observeredPlayerSkill.SpecialSkill == Skills.None + ? player.GetSkillName(observeredPlayerSkillInfo.Skill, observeredPlayerSkill.SkillChance) + : $"{player.GetSkillName(observeredPlayerSpecialSkillInfo.Skill)}({player.GetSkillName(observeredPlayerSkillInfo.Skill)})")}"; + if (showDescirptionHUD) + remainingLine = player.GetSkillDescription(observeredPlayerSkill.Skill, observeredPlayerSkill.SkillChance); } } - if (string.IsNullOrEmpty(infoLine) && string.IsNullOrEmpty(skillLine)) return; - var hudContent = infoLine + skillLine; + if (string.IsNullOrEmpty(skillLine)) return; if (player == null || !player.IsValid) return; - player.PrintToCenterHtml(hudContent); + Event.UpdateSkillHUD(player, infoLine, skillLine, remainingLine, isDescription); } } } \ No newline at end of file diff --git a/jRandomSkills - SRC Files/src/player/skills/Aimbot.cs b/jRandomSkills - SRC Files/src/player/skills/Aimbot.cs index c6e3df5..7fb09d3 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Aimbot.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Aimbot.cs @@ -4,17 +4,18 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using System.Runtime.InteropServices; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Aimbot : ISkill { private const Skills skillName = Skills.Aimbot; - private static readonly Dictionary hitGroups = []; + private static readonly ConcurrentDictionary hitGroups = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void OnTakeDamage(DynamicHook h) @@ -66,7 +67,7 @@ namespace jRandomSkills Marshal.WriteInt32(hit.Key, 56, hit.Value); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff0000", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff0000", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Anomaly.cs b/jRandomSkills - SRC Files/src/player/skills/Anomaly.cs index b1334df..bceddb5 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Anomaly.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Anomaly.cs @@ -3,6 +3,7 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; +using System.Collections.Concurrent; using static jRandomSkills.jRandomSkills; namespace jRandomSkills @@ -10,19 +11,19 @@ namespace jRandomSkills public class Anomaly : ISkill { private const Skills skillName = Skills.Anomaly; - private static readonly int maxSize = Config.GetValue(skillName, "secondsInBack"); private static readonly float tickRate = 64; - private static readonly float timerCooldown = Config.GetValue(skillName, "cooldown"); - private static readonly Dictionary SkillPlayerInfo = []; + private static readonly ConcurrentDictionary SkillPlayerInfo = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - SkillPlayerInfo.Clear(); + lock (setLock) + SkillPlayerInfo.Clear(); } public static void OnTick() @@ -41,7 +42,7 @@ namespace jRandomSkills if (skillInfo.LastRotations == null || skillInfo.LastPositions == null) continue; skillInfo.LastPositions.Add(new Vector(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z)); skillInfo.LastRotations.Add(new QAngle(pawn.EyeAngles.X, pawn.EyeAngles.Y, pawn.EyeAngles.Z)); - if (skillInfo.LastRotations.Count > maxSize) + if (skillInfo.LastRotations.Count > SkillsInfo.GetValue(skillName, "secondsInBack")) { skillInfo.LastPositions.RemoveAt(0); skillInfo.LastRotations.RemoveAt(0); @@ -53,19 +54,20 @@ namespace jRandomSkills public static void EnableSkill(CCSPlayerController player) { - SkillPlayerInfo[player.SteamID] = new PlayerSkillInfo + SkillPlayerInfo.TryAdd(player.SteamID, new PlayerSkillInfo { SteamID = player.SteamID, CanUse = true, Cooldown = DateTime.MinValue, LastPositions = [], LastRotations = [], - }; + }); } public static void DisableSkill(CCSPlayerController player) { - SkillPlayerInfo.Remove(player.SteamID); + SkillPlayerInfo.TryRemove(player.SteamID, out _); + SkillUtils.ResetPrintHTML(player); } private static void UpdateHUD(CCSPlayerController player, PlayerSkillInfo skillInfo) @@ -73,22 +75,20 @@ namespace jRandomSkills float cooldown = 0; if (skillInfo != null) { - float time = (int)(skillInfo.Cooldown.AddSeconds(timerCooldown) - DateTime.Now).TotalSeconds; + float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue(skillName, "cooldown")) - DateTime.Now).TotalSeconds); cooldown = Math.Max(time, 0); if (cooldown == 0 && skillInfo?.CanUse == false) skillInfo.CanUse = true; } - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = cooldown != 0 ? $"{player.GetTranslation("hud_info", $"{cooldown}")}
" : ""; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + if (cooldown == 0) + playerInfo.PrintHTML = null; + else + playerInfo.PrintHTML = $"{player.GetTranslation("hud_info", $"{cooldown}")}"; } public static void UseSkill(CCSPlayerController player) @@ -121,7 +121,7 @@ namespace jRandomSkills public List? LastRotations { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a86eff", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, int secondsInBack = 5, float cooldown = 15) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a86eff", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, int secondsInBack = 5, float cooldown = 15) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int SecondsInBack { get; set; } = secondsInBack; public float Cooldown { get; set; } = cooldown; diff --git a/jRandomSkills - SRC Files/src/player/skills/AntyFlash.cs b/jRandomSkills - SRC Files/src/player/skills/AntyFlash.cs index 1aa710c..418fb20 100644 --- a/jRandomSkills - SRC Files/src/player/skills/AntyFlash.cs +++ b/jRandomSkills - SRC Files/src/player/skills/AntyFlash.cs @@ -12,7 +12,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerBlind(EventPlayerBlind @event) @@ -31,7 +31,7 @@ namespace jRandomSkills if (playerInfo?.Skill == skillName) playerPawn.FlashDuration = 0.0f; else if (attackerInfo?.Skill == skillName) - playerPawn.FlashDuration = 7.0f; + playerPawn.FlashDuration = SkillsInfo.GetValue(skillName, "flashDuration"); } public static void EnableSkill(CCSPlayerController player) @@ -39,8 +39,9 @@ namespace jRandomSkills SkillUtils.TryGiveWeapon(player, CsItem.FlashbangGrenade); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#D6E6FF", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#D6E6FF", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float flashDuration = 7f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { + public float FlashDuration { get; set; } = flashDuration; } } } \ No newline at end of file diff --git a/jRandomSkills - SRC Files/src/player/skills/AntyHead.cs b/jRandomSkills - SRC Files/src/player/skills/AntyHead.cs index 0b49459..e6a3ea5 100644 --- a/jRandomSkills - SRC Files/src/player/skills/AntyHead.cs +++ b/jRandomSkills - SRC Files/src/player/skills/AntyHead.cs @@ -11,7 +11,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerHurt(EventPlayerHurt @event) @@ -38,7 +38,7 @@ namespace jRandomSkills playerPawn.Health = (int)newHealth; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8B4513", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8B4513", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/AreaReaper.cs b/jRandomSkills - SRC Files/src/player/skills/AreaReaper.cs index 87c4feb..0ecfffd 100644 --- a/jRandomSkills - SRC Files/src/player/skills/AreaReaper.cs +++ b/jRandomSkills - SRC Files/src/player/skills/AreaReaper.cs @@ -4,6 +4,7 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { @@ -15,7 +16,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -59,7 +60,7 @@ namespace jRandomSkills if (playerInfo == null) return; playerInfo.SkillChance = 0; - HashSet<(string, string)> menuItems = [(player.GetTranslation("bombsite_a"), "a"), (player.GetTranslation("bombsite_b"), "b")]; + ConcurrentBag<(string, string)> menuItems = [(player.GetTranslation("bombsite_a"), "a"), (player.GetTranslation("bombsite_b"), "b")]; SkillUtils.CreateMenu(player, menuItems); } @@ -78,7 +79,7 @@ namespace jRandomSkills bombTarget.AcceptInput("Enable"); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#edf5b5", CsTeam onlyTeam = CsTeam.CounterTerrorist, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#edf5b5", CsTeam onlyTeam = CsTeam.CounterTerrorist, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Armored.cs b/jRandomSkills - SRC Files/src/player/skills/Armored.cs index a3576d6..c7ec373 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Armored.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Armored.cs @@ -13,7 +13,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); } public static void EnableSkill(CCSPlayerController player) @@ -21,10 +21,10 @@ namespace jRandomSkills var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo == null) return; - float newScale = (float)Instance.Random.NextDouble() * (Config.GetValue(skillName, "ChanceTo") - Config.GetValue(skillName, "ChanceFrom")) + Config.GetValue(skillName, "ChanceFrom"); + float newScale = (float)Instance.Random.NextDouble() * (SkillsInfo.GetValue(skillName, "ChanceTo") - SkillsInfo.GetValue(skillName, "ChanceFrom")) + SkillsInfo.GetValue(skillName, "ChanceFrom"); playerInfo.SkillChance = newScale; newScale = (float)Math.Round(newScale, 2); - SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetTranslation("armored")}{ChatColors.Lime}: " + player.GetTranslation("armored_desc2", newScale), false); + SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(skillName)}{ChatColors.Lime}: {player.GetSkillDescription(skillName, newScale)}", false); } public static void OnTakeDamage(DynamicHook h) @@ -57,7 +57,7 @@ namespace jRandomSkills } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#d1430a", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float chanceFrom = .65f, float chanceTo = .85f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#d1430a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float chanceFrom = .65f, float chanceTo = .85f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float ChanceFrom { get; set; } = chanceFrom; public float ChanceTo { get; set; } = chanceTo; diff --git a/jRandomSkills - SRC Files/src/player/skills/Assassin.cs b/jRandomSkills - SRC Files/src/player/skills/Assassin.cs index 344dc56..2496648 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Assassin.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Assassin.cs @@ -8,13 +8,11 @@ namespace jRandomSkills public class Assassin : ISkill { private const Skills skillName = Skills.Assassin; - private static readonly float damageMultiplier = Config.GetValue(skillName, "damageMultiplier"); - private static readonly float toleranceDeg = Config.GetValue(skillName, "toleranceDeg"); private static readonly string[] nades = ["inferno", "flashbang", "smokegrenade", "decoy", "hegrenade"]; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerHurt(EventPlayerHurt @event) @@ -32,7 +30,7 @@ namespace jRandomSkills if (playerInfo?.Skill != skillName) return; if (IsBehind(attacker!, victim!)) - SkillUtils.TakeHealth(victim!.PlayerPawn.Value, (int)(damage * (damageMultiplier - 1f))); + SkillUtils.TakeHealth(victim!.PlayerPawn.Value, (int)(damage * (SkillsInfo.GetValue(skillName, "damageMultiplier") - 1f))); } private static bool IsBehind(CCSPlayerController attacker, CCSPlayerController victim) @@ -47,6 +45,7 @@ namespace jRandomSkills private static (float, float) GetAngleRange(float angle) { + var toleranceDeg = SkillsInfo.GetValue(skillName, "toleranceDeg"); float min = angle - toleranceDeg; float max = angle + toleranceDeg; @@ -63,7 +62,7 @@ namespace jRandomSkills return (target >= a || target <= b); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#d9d9d9", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float damageMultiplier = 2f, float toleranceDeg = 45f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#d9d9d9", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float damageMultiplier = 2f, float toleranceDeg = 45f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float DamageMultiplier { get; set; } = damageMultiplier; public float ToleranceDeg { get; set; } = toleranceDeg; diff --git a/jRandomSkills - SRC Files/src/player/skills/Astronaut.cs b/jRandomSkills - SRC Files/src/player/skills/Astronaut.cs index b41d016..3effa63 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Astronaut.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Astronaut.cs @@ -13,7 +13,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); } public static void EnableSkill(CCSPlayerController player) @@ -43,13 +43,13 @@ namespace jRandomSkills var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo == null) return; - float gravityModifier = (float)Math.Round(Instance.Random.NextDouble() * (Config.GetValue(skillName, "ChanceTo") - Config.GetValue(skillName, "chanceFrom")) + Config.GetValue(skillName, "chanceFrom"), 1); + float gravityModifier = (float)Math.Round(Instance.Random.NextDouble() * (SkillsInfo.GetValue(skillName, "ChanceTo") - SkillsInfo.GetValue(skillName, "chanceFrom")) + SkillsInfo.GetValue(skillName, "chanceFrom"), 1); playerInfo.SkillChance = gravityModifier; - SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetTranslation("astronaut")}{ChatColors.Lime}: " + player.GetTranslation("astronaut_desc2", gravityModifier), false); + SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(skillName)}{ChatColors.Lime}: {player.GetSkillDescription(skillName, gravityModifier)}", false); player.PlayerPawn.Value.ActualGravityScale = gravityModifier; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#7E10AD", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float chanceFrom = .1f, float chanceTo = .7f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#7E10AD", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float chanceFrom = .1f, float chanceTo = .7f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float ChanceFrom { get; set; } = chanceFrom; public float ChanceTo { get; set; } = chanceTo; diff --git a/jRandomSkills - SRC Files/src/player/skills/Bankrupt.cs b/jRandomSkills - SRC Files/src/player/skills/Bankrupt.cs index 76f7f66..e500f20 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Bankrupt.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Bankrupt.cs @@ -4,6 +4,7 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { @@ -13,7 +14,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -33,7 +34,7 @@ namespace jRandomSkills if (playerInfo == null || playerInfo.Skill != skillName) continue; var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); - HashSet<(string, string)> menuItems = enemies.Select(e => ($"{e.PlayerName} : {(e.InGameMoneyServices == null ? 0 : e.InGameMoneyServices.Account)}$", e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => ($"{e.PlayerName} : {(e.InGameMoneyServices == null ? 0 : e.InGameMoneyServices.Account)}$", e.Index.ToString()))); SkillUtils.UpdateMenu(player, menuItems); } } @@ -74,7 +75,7 @@ namespace jRandomSkills var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); if (enemies.Length > 0) { - HashSet<(string, string)> menuItems = enemies.Select(e => ($"{e.PlayerName} : {(e.InGameMoneyServices == null ? 0 : e.InGameMoneyServices.Account)}$", e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => ($"{e.PlayerName} : {(e.InGameMoneyServices == null ? 0 : e.InGameMoneyServices.Account)}$", e.Index.ToString()))); SkillUtils.CreateMenu(player, menuItems); } else @@ -96,7 +97,7 @@ namespace jRandomSkills SkillUtils.CloseMenu(player); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#abab33", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#abab33", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Baseball.cs b/jRandomSkills - SRC Files/src/player/skills/Baseball.cs index 26dfbf9..8a21e90 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Baseball.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Baseball.cs @@ -4,20 +4,18 @@ using CounterStrikeSharp.API.Modules.Entities.Constants; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Baseball : ISkill { private const Skills skillName = Skills.Baseball; - private static readonly float speedMultipier = Config.GetValue(skillName, "speedMultipier"); - private static readonly float maxSpeed = Config.GetValue(skillName, "maxSpeed"); - private static readonly int damageDeal = Config.GetValue(skillName, "damageDeal"); - private static readonly HashSet decoys = []; + private static readonly ConcurrentDictionary decoys = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerHurt(EventPlayerHurt @event) @@ -32,7 +30,7 @@ namespace jRandomSkills var attackerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == attacker?.SteamID); if (attackerInfo?.Skill != skillName) return; - SkillUtils.TakeHealth(victim!.PlayerPawn.Value, damageDeal); + SkillUtils.TakeHealth(victim!.PlayerPawn.Value, SkillsInfo.GetValue(skillName, "damageDeal")); } public static void OnEntitySpawned(CEntityInstance entity) @@ -52,7 +50,7 @@ namespace jRandomSkills var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo?.Skill != skillName) return; - decoys.Add(decoy); + decoys.TryAdd(decoy, 0); } public static void DecoyStarted(EventDecoyStarted @event) @@ -62,25 +60,25 @@ namespace jRandomSkills var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo?.Skill != skillName) return; - var decoy = decoys.FirstOrDefault(d => d.Index == @event.Entityid); + var decoy = decoys.FirstOrDefault(d => d.Key.Index == @event.Entityid).Key; if (decoy != null && decoy.IsValid) decoy.AcceptInput("Kill"); } public static void OnTick() { - foreach (var decoy in decoys) + foreach (var decoy in decoys.Keys) { if (!decoy.IsValid) { - decoys.Remove(decoy); + decoys.TryRemove(decoy, out _); continue; } decoy.Bounces = 0; if (Server.TickCount % 8 != 0) continue; var vel = decoy.AbsVelocity; float speed = vel.Length(); - float targetSpeed = Math.Min(speed * speedMultipier, maxSpeed); + float targetSpeed = Math.Min(speed * SkillsInfo.GetValue(skillName, "speedMultipier"), SkillsInfo.GetValue(skillName, "maxSpeed")); if (speed > .01f) { @@ -99,7 +97,7 @@ namespace jRandomSkills SkillUtils.TryGiveWeapon(player, CsItem.DecoyGrenade); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#2effc7", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float speedMultipier = 2f, float maxSpeed = 900f, int damageDeal = 9999) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#2effc7", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float speedMultipier = 2f, float maxSpeed = 900f, int damageDeal = 9999) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float SpeedMultipier { get; set; } = speedMultipier; public float MaxSpeed { get; set; } = maxSpeed; diff --git a/jRandomSkills - SRC Files/src/player/skills/Behind.cs b/jRandomSkills - SRC Files/src/player/skills/Behind.cs index 4cab3d5..d658b64 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Behind.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Behind.cs @@ -12,7 +12,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); } public static void PlayerHurt(EventPlayerHurt @event) @@ -32,11 +32,11 @@ namespace jRandomSkills { var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo == null) return; - float newChance = (float)Instance.Random.NextDouble() * (Config.GetValue(skillName, "ChanceTo") - Config.GetValue(skillName, "ChanceFrom")) + Config.GetValue(skillName, "ChanceFrom"); + float newChance = (float)Instance.Random.NextDouble() * (SkillsInfo.GetValue(skillName, "ChanceTo") - SkillsInfo.GetValue(skillName, "ChanceFrom")) + SkillsInfo.GetValue(skillName, "ChanceFrom"); playerInfo.SkillChance = newChance; newChance = (float)Math.Round(newChance, 2) * 100; newChance = (float)Math.Round(newChance); - SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetTranslation("behind")}{ChatColors.Lime}: " + player.GetTranslation("behind_desc2", newChance), false); + SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(skillName)}{ChatColors.Lime}: {player.GetSkillDescription(skillName, newChance)}", false); } private static void RotateEnemy(CCSPlayerController player) @@ -58,7 +58,7 @@ namespace jRandomSkills pawn.Teleport(currentPosition, newAngles, new Vector(0, 0, 0)); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#00FF00", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float chanceFrom = .2f, float chanceTo = .4f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#00FF00", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float chanceFrom = .2f, float chanceTo = .4f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float ChanceFrom { get; set; } = chanceFrom; public float ChanceTo { get; set; } = chanceTo; diff --git a/jRandomSkills - SRC Files/src/player/skills/BladeMaster.cs b/jRandomSkills - SRC Files/src/player/skills/BladeMaster.cs index 28fe9ce..fdd5054 100644 --- a/jRandomSkills - SRC Files/src/player/skills/BladeMaster.cs +++ b/jRandomSkills - SRC Files/src/player/skills/BladeMaster.cs @@ -10,12 +10,31 @@ namespace jRandomSkills { private const Skills skillName = Skills.BladeMaster; private static readonly string[] noReflectionWeapon = ["inferno", "flashbang", "smokegrenade", "decoy", "hegrenade", "knife", "taser"]; - private static readonly float torseReflectionChance = Config.GetValue(skillName, "torseReflectionChance") * 100; - private static readonly float legReflectionChance = Config.GetValue(skillName, "legReflectionChance") * 100; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); + } + + public static void OnTick() + { + foreach (var player in Utilities.GetPlayers()) + { + if (!Instance.IsPlayerValid(player)) continue; + + var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); + if (playerInfo?.Skill != skillName) continue; + + var playerPawn = player.PlayerPawn?.Value; + if (playerPawn == null || !playerPawn.IsValid || playerPawn.VelocityModifier == 0) continue; + + var weaponServices = playerPawn.WeaponServices; + if (weaponServices == null) return; + if (weaponServices.ActiveWeapon == null || !weaponServices.ActiveWeapon.IsValid || weaponServices.ActiveWeapon.Value == null || !weaponServices.ActiveWeapon.Value.IsValid || weaponServices.ActiveWeapon.Value.DesignerName != "weapon_knife") + return; + + playerPawn.VelocityModifier = SkillsInfo.GetValue(skillName, "velocityModifier"); + } } public static void PlayerHurt(EventPlayerHurt @event) @@ -32,11 +51,11 @@ namespace jRandomSkills int chance = Instance.Random.Next(0, 101); if (hitGroup == HitGroup_t.HITGROUP_LEFTLEG || hitGroup == HitGroup_t.HITGROUP_RIGHTLEG) { - if (chance > legReflectionChance) + if (chance > SkillsInfo.GetValue(skillName, "legReflectionChance") * 100) return; } else - if (chance > torseReflectionChance) + if (chance > SkillsInfo.GetValue(skillName, "torseReflectionChance") * 100) return; var pawn = victim!.PlayerPawn.Value; @@ -63,10 +82,11 @@ namespace jRandomSkills Utilities.SetStateChanged(playerPawn, "CBaseEntity", "m_iHealth"); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#cc7504", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float torseReflectionChance = .95f, float legReflectionChance = .80f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#cc7504", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float torseReflectionChance = .95f, float legReflectionChance = .80f, float velocityModifier = .85f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float TorseReflectionChance { get; set; } = torseReflectionChance; public float LegReflectionChance { get; set; } = legReflectionChance; + public float VelocityModifier { get; set; } = velocityModifier; } } } \ No newline at end of file diff --git a/jRandomSkills - SRC Files/src/player/skills/BunnyHop.cs b/jRandomSkills - SRC Files/src/player/skills/BunnyHop.cs index c243ddf..6e12bc0 100644 --- a/jRandomSkills - SRC Files/src/player/skills/BunnyHop.cs +++ b/jRandomSkills - SRC Files/src/player/skills/BunnyHop.cs @@ -9,13 +9,10 @@ namespace jRandomSkills public class BunnyHop : ISkill { private const Skills skillName = Skills.BunnyHop; - private static readonly float maxSpeed = Config.GetValue(skillName, "maxSpeed"); - private static readonly float bunnyHopVelocity = Config.GetValue(skillName, "jumpVelocity"); - private static readonly float jumpBoost = Config.GetValue(skillName, "jumpBoost"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void OnTick() @@ -39,7 +36,8 @@ namespace jRandomSkills if (buttons.HasFlag(PlayerButtons.Jump) && flags.HasFlag(PlayerFlags.FL_ONGROUND) && !playerPawn.MoveType.HasFlag(MoveType_t.MOVETYPE_LADDER)) { - playerPawn.AbsVelocity.Z = bunnyHopVelocity; + playerPawn.AbsVelocity.Z = SkillsInfo.GetValue(skillName, "jumpVelocity"); + var maxSpeed = SkillsInfo.GetValue(skillName, "maxSpeed"); var vX = playerPawn.AbsVelocity.X; var vY = playerPawn.AbsVelocity.Y; @@ -48,7 +46,7 @@ namespace jRandomSkills if (speed2D < maxSpeed) { - var newSpeed = Math.Min(speed2D * jumpBoost, maxSpeed); + var newSpeed = Math.Min(speed2D * SkillsInfo.GetValue(skillName, "jumpBoost"), maxSpeed); scale = newSpeed / (speed2D == 0 ? 1 : speed2D); } else if (speed2D > maxSpeed) @@ -59,7 +57,7 @@ namespace jRandomSkills } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#d1430a", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float maxSpeed = 500f, float jumpVelocity = 300f, float jumpBoost = 2f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#d1430a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float maxSpeed = 500f, float jumpVelocity = 300f, float jumpBoost = 2f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float MaxSpeed { get; set; } = maxSpeed; public float JumpVelocity { get; set; } = jumpVelocity; diff --git a/jRandomSkills - SRC Files/src/player/skills/C4Camouflage.cs b/jRandomSkills - SRC Files/src/player/skills/C4Camouflage.cs index af04d01..54035a0 100644 --- a/jRandomSkills - SRC Files/src/player/skills/C4Camouflage.cs +++ b/jRandomSkills - SRC Files/src/player/skills/C4Camouflage.cs @@ -5,17 +5,18 @@ using CounterStrikeSharp.API.Core.Attributes; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class C4Camouflage : ISkill { private const Skills skillName = Skills.C4Camouflage; - private static readonly Dictionary> invisibleEntities = []; + private static readonly ConcurrentDictionary> invisibleEntities = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void WeaponPickup(EventItemPickup @event) @@ -48,6 +49,16 @@ namespace jRandomSkills } } + public static void OnTick() + { + foreach (var player in Utilities.GetPlayers()) + { + if (!player.PawnIsAlive) + if (invisibleEntities.ContainsKey(player.SteamID)) + invisibleEntities.TryRemove(player.SteamID, out _); + } + } + public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList infoList) { foreach (var (info, player) in infoList) @@ -81,7 +92,7 @@ namespace jRandomSkills { SetPlayerVisibility(player, true); SetWeaponVisibility(player, true); - invisibleEntities.Remove(player.SteamID); + invisibleEntities.TryRemove(player.SteamID, out _); } private static void SetPlayerVisibility(CCSPlayerController player, bool enabled) @@ -101,7 +112,7 @@ namespace jRandomSkills var playerPawn = player.PlayerPawn.Value!; if (playerPawn.WeaponServices == null) return; - invisibleEntities.Remove(player.SteamID); + invisibleEntities.TryRemove(player.SteamID, out _); foreach (var weapon in playerPawn.WeaponServices.MyWeapons) { if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid) @@ -114,16 +125,16 @@ namespace jRandomSkills items.Add(weapon.Index); } else - invisibleEntities.Add(player.SteamID, [weapon.Index]); + invisibleEntities.TryAdd(player.SteamID, [weapon.Index]); } } } if (visible) - invisibleEntities.Remove(player.SteamID); + invisibleEntities.TryRemove(player.SteamID, out _); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#00911f", CsTeam onlyTeam = CsTeam.Terrorist, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#00911f", CsTeam onlyTeam = CsTeam.Terrorist, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Catapult.cs b/jRandomSkills - SRC Files/src/player/skills/Catapult.cs index 89953de..a0d24b6 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Catapult.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Catapult.cs @@ -12,7 +12,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); } public static void PlayerHurt(EventPlayerHurt @event) @@ -36,14 +36,14 @@ namespace jRandomSkills { var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo == null) return; - float newChance = (float)Instance.Random.NextDouble() * (Config.GetValue(skillName, "chanceTo") - Config.GetValue(skillName, "chanceFrom")) + Config.GetValue(skillName, "chanceFrom"); + float newChance = (float)Instance.Random.NextDouble() * (SkillsInfo.GetValue(skillName, "chanceTo") - SkillsInfo.GetValue(skillName, "chanceFrom")) + SkillsInfo.GetValue(skillName, "chanceFrom"); playerInfo.SkillChance = newChance; newChance = (float)Math.Round(newChance, 2) * 100; newChance = (float)Math.Round(newChance); - SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetTranslation("catapult")}{ChatColors.Lime}: " + player.GetTranslation("catapult_desc2", newChance), false); + SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(skillName)}{ChatColors.Lime}: {player.GetSkillDescription(skillName, newChance)}", false); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FF4500", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float chanceFrom = .2f, float chanceTo = .4f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FF4500", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float chanceFrom = .2f, float chanceTo = .4f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float ChanceFrom { get; set; } = chanceFrom; public float ChanceTo { get; set; } = chanceTo; diff --git a/jRandomSkills - SRC Files/src/player/skills/Chicken.cs b/jRandomSkills - SRC Files/src/player/skills/Chicken.cs index ec3cea2..ef7f1c2 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Chicken.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Chicken.cs @@ -5,7 +5,7 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using static jRandomSkills.jRandomSkills; using jRandomSkills.src.utils; -using CounterStrikeSharp.API.Core.Attributes; +using System.Collections.Concurrent; namespace jRandomSkills { @@ -22,17 +22,21 @@ namespace jRandomSkills "weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249", "weapon_negev" ]; - private static readonly Dictionary chickens = []; + private static readonly ConcurrentDictionary chickens = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { foreach (var player in Utilities.GetPlayers()) SetWeaponAttack(player, false); + foreach (var valuePair in chickens) + if (valuePair.Value != null && valuePair.Value.IsValid) + valuePair.Value.AcceptInput("Kill"); + chickens.Clear(); } public static void WeaponPickup(EventItemPickup @event) @@ -44,17 +48,6 @@ namespace jRandomSkills SetWeaponAttack(player, true); } - public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList infoList) - { - foreach (var (info, player) in infoList) - { - if (player == null) continue; - if (chickens.TryGetValue(player, out var chicken)) - if (chicken != null && chicken.IsValid) - info.TransmitEntities.Remove(chicken.Index); - } - } - public static void EnableSkill(CCSPlayerController player) { var playerPawn = player.PlayerPawn?.Value; @@ -79,6 +72,7 @@ namespace jRandomSkills public static void DisableSkill(CCSPlayerController player) { + SkillUtils.ResetPrintHTML(player); var playerPawn = player.PlayerPawn?.Value; if (playerPawn != null) { @@ -100,7 +94,7 @@ namespace jRandomSkills { if (chicken != null && chicken.IsValid) chicken.AcceptInput("Kill"); - chickens.Remove(player); + chickens.TryRemove(player, out _); } } @@ -129,16 +123,21 @@ namespace jRandomSkills var chickenModel = Utilities.CreateEntityByName("prop_dynamic"); if (chickenModel == null) return; - Vector pos = new(0, 0, 0); - + chickenModel.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags = (uint)(chickenModel.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags & ~(1 << 2)); chickenModel.SetModel("models/chicken/chicken.vmdl"); chickenModel.Render = Color.FromArgb(255, 255, 255, 255); - chickenModel.Teleport(pos, playerPawn.AbsRotation, null); + chickenModel.Teleport(playerPawn.AbsOrigin, playerPawn.AbsRotation, null); chickenModel.DispatchSpawn(); chickenModel.AcceptInput("InitializeSpawnFromWorld", playerPawn, playerPawn, ""); Utilities.SetStateChanged(chickenModel, "CBaseEntity", "m_CBodyComponent"); - Instance.AddTimer(1f, () => chickens.TryAdd(player, chickenModel)); + + chickenModel.CBodyComponent.SceneNode.GetSkeletonInstance().Scale = 1; + Utilities.SetStateChanged(chickenModel, "CBaseEntity", "m_CBodyComponent"); + Server.NextFrame(() => chickenModel.AcceptInput("SetScale", chickenModel, chickenModel, "1")); + + chickenModel.AcceptInput("SetParent", playerPawn, playerPawn, "!activator"); + chickens.TryAdd(player, chickenModel); } public static void OnTick() @@ -153,12 +152,6 @@ namespace jRandomSkills var pawn = player.PlayerPawn.Value; if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null || chicken.AbsOrigin == null) continue; - float X = (float)Math.Round(pawn.AbsOrigin.X, 2); - float Y = (float)Math.Round(pawn.AbsOrigin.Y, 2); - float Z = (float)Math.Round(pawn.AbsOrigin.Z, 2); - Vector pos = new(X, Y, Z); - if (chicken.AbsOrigin.X != pos.X || chicken.AbsOrigin.Y != pos.Y || chicken.AbsOrigin.Z != pos.Z) - chicken.Teleport(pos, pawn.AbsRotation, null); pawn.VelocityModifier = 1.1f; UpdateHUD(player); } @@ -170,21 +163,20 @@ namespace jRandomSkills var pawn = player.PlayerPawn.Value; if (pawn.WeaponServices == null || pawn.WeaponServices.ActiveWeapon == null || !pawn.WeaponServices.ActiveWeapon.IsValid || pawn.WeaponServices.ActiveWeapon.Value == null || !pawn.WeaponServices.ActiveWeapon.Value.IsValid) return; - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; var weapon = pawn.WeaponServices.ActiveWeapon.Value; - if (weapon == null || !disabledWeapons.Contains(weapon.DesignerName)) return; + if (weapon == null || !disabledWeapons.Contains(weapon.DesignerName)) + { + playerInfo.PrintHTML = null; + return; + } - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = $"{player.GetTranslation("disabled_weapon")}
"; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + playerInfo.PrintHTML = $"{player.GetTranslation("disabled_weapon")}"; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FF8B42", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FF8B42", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/ChillOut.cs b/jRandomSkills - SRC Files/src/player/skills/ChillOut.cs index 864705e..3231113 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ChillOut.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ChillOut.cs @@ -9,11 +9,10 @@ namespace jRandomSkills public class ChillOut : ISkill { private const Skills skillName = Skills.ChillOut; - private static readonly float bombArmedTime = Config.GetValue(skillName, "bombArmedTime"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void BombBeginplant(EventBombBeginplant @event) @@ -29,12 +28,12 @@ namespace jRandomSkills { var bomb = bombEntities.FirstOrDefault(); if (bomb != null) - bomb.ArmedTime = Server.CurrentTime + bombArmedTime; + bomb.ArmedTime = Server.CurrentTime + SkillsInfo.GetValue(skillName, "bombArmedTime"); } } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#343deb", CsTeam onlyTeam = CsTeam.CounterTerrorist, bool needsTeammates = false, float bombArmedTime = 10f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#343deb", CsTeam onlyTeam = CsTeam.CounterTerrorist, bool disableOnFreezeTime = false, bool needsTeammates = false, float bombArmedTime = 10f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float BombArmedTime { get; set; } = bombArmedTime; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Cutter.cs b/jRandomSkills - SRC Files/src/player/skills/Cutter.cs index 5403b4c..4a4ee5b 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Cutter.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Cutter.cs @@ -11,7 +11,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerHurt(EventPlayerHurt @event) @@ -29,7 +29,7 @@ namespace jRandomSkills SkillUtils.TakeHealth(victim!.PlayerPawn.Value, 9999); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#88a31a", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#88a31a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Darkness.cs b/jRandomSkills - SRC Files/src/player/skills/Darkness.cs index 1effdb4..0feb39a 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Darkness.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Darkness.cs @@ -4,31 +4,35 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Darkness : ISkill { private const Skills skillName = Skills.Darkness; - private static readonly float brightness = Config.GetValue(skillName, "brightness"); - private static readonly Dictionary> defaultPostProcessings = []; - private static readonly List newPostProcessing = []; + private static readonly ConcurrentDictionary> defaultPostProcessings = []; + private static readonly ConcurrentBag newPostProcessing = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - foreach (var player in defaultPostProcessings.Keys) - DisableSkill(player); - foreach (var postProcessing in newPostProcessing) - if (postProcessing != null && postProcessing.IsValid) - postProcessing.AcceptInput("Kill"); - newPostProcessing.Clear(); - foreach (var player in Utilities.GetPlayers()) - SkillUtils.CloseMenu(player); + lock (setLock) + { + foreach (var player in defaultPostProcessings.Keys) + DisableSkill(player); + foreach (var postProcessing in newPostProcessing) + if (postProcessing != null && postProcessing.IsValid) + postProcessing.AcceptInput("Kill"); + newPostProcessing.Clear(); + foreach (var player in Utilities.GetPlayers()) + SkillUtils.CloseMenu(player); + } } public static void PlayerDeath(EventPlayerDeath @event) @@ -49,7 +53,7 @@ namespace jRandomSkills if (playerInfo == null || playerInfo.Skill != skillName) continue; var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); - HashSet<(string, string)> menuItems = enemies.Select(e => (e.PlayerName, e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.UpdateMenu(player, menuItems); } } @@ -90,7 +94,7 @@ namespace jRandomSkills var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); if (enemies.Length > 0) { - HashSet<(string, string)> menuItems = enemies.Select(e => (e.PlayerName, e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.CreateMenu(player, menuItems); } else @@ -99,8 +103,11 @@ namespace jRandomSkills public static void DisableSkill(CCSPlayerController player) { - SetUpPostProcessing(player, true); - SkillUtils.CloseMenu(player); + lock (setLock) + { + SetUpPostProcessing(player, true); + SkillUtils.CloseMenu(player); + } } private static void SetUpPostProcessing(CCSPlayerController player, bool dontCreateNew = false) @@ -109,8 +116,8 @@ namespace jRandomSkills var pawn = player.PlayerPawn.Value; if (pawn == null || !pawn.IsValid || pawn.CameraServices == null) return; - if (!defaultPostProcessings.TryGetValue(player, out _)) - defaultPostProcessings[player] = []; + if (!defaultPostProcessings.ContainsKey(player)) + defaultPostProcessings.TryAdd(player, []); int i = 0; foreach (var postProcessingVolume in pawn.CameraServices.PostProcessingVolumes) @@ -120,16 +127,18 @@ namespace jRandomSkills if (dontCreateNew) { - if (i < defaultPostProcessings[player].Count) - postProcessingVolume.Raw = defaultPostProcessings[player][i].EntityHandle.Raw; + if (defaultPostProcessings.TryGetValue(player, out var defaultList) && i < defaultList.Count) + postProcessingVolume.Raw = defaultList[i].EntityHandle.Raw; } else { - defaultPostProcessings[player].Add(postProcessingVolume.Value); + if (defaultPostProcessings.TryGetValue(player, out var defaultList)) + defaultList.Add(postProcessingVolume.Value); var postProcessing = Utilities.CreateEntityByName("post_processing_volume"); if (postProcessing == null) return; + var brightness = SkillsInfo.GetValue(skillName, "brightness"); postProcessing.ExposureControl = true; postProcessing.MaxExposure = brightness; postProcessing.MinExposure = brightness; @@ -142,10 +151,10 @@ namespace jRandomSkills Utilities.SetStateChanged(pawn, "CBasePlayerPawn", "m_pCameraServices"); if (dontCreateNew) - defaultPostProcessings.Remove(player); + defaultPostProcessings.TryRemove(player, out _); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#383838", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float brightness = .01f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#383838", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float brightness = .01f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float Brightness { get; set; } = brightness; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Dash.cs b/jRandomSkills - SRC Files/src/player/skills/Dash.cs index d005f83..a5039bd 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Dash.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Dash.cs @@ -13,12 +13,9 @@ namespace jRandomSkills private static readonly int?[] J = new int?[64]; private static readonly PlayerButtons[] LB = new PlayerButtons[64]; - private static readonly float jumpVelocity = Config.GetValue(skillName, "jumpVelocity"); - private static readonly float pushVelocity = Config.GetValue(skillName, "pushVelocity"); - public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void OnTick() @@ -28,11 +25,11 @@ namespace jRandomSkills if (!Instance.IsPlayerValid(player)) return; var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo?.Skill == skillName) - GiveAdditionalJump(player); + HandleDash(player); } } - private static void GiveAdditionalJump(CCSPlayerController player) + private static void HandleDash(CCSPlayerController player) { var playerPawn = player.PlayerPawn.Value; if (playerPawn == null || !playerPawn.IsValid) return; @@ -73,8 +70,8 @@ namespace jRandomSkills float moveAngle = MathF.Atan2(moveX, moveY) * (180f / MathF.PI); QAngle dashAngles = new(0, playerPawn.EyeAngles.Y + moveAngle, 0); - Vector newVelocity = SkillUtils.GetForwardVector(dashAngles) * pushVelocity; - newVelocity.Z = playerPawn.AbsVelocity.Z + jumpVelocity; + Vector newVelocity = SkillUtils.GetForwardVector(dashAngles) * SkillsInfo.GetValue(skillName, "pushVelocity"); + newVelocity.Z = playerPawn.AbsVelocity.Z + SkillsInfo.GetValue(skillName, "jumpVelocity"); playerPawn.AbsVelocity.X = newVelocity.X; playerPawn.AbsVelocity.Y = newVelocity.Y; @@ -85,7 +82,7 @@ namespace jRandomSkills LB[player.Slot] = buttons; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#42bbfc", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float jumpVelocity = 150f, float pushVelocity = 600f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#42bbfc", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float jumpVelocity = 150f, float pushVelocity = 600f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float JumpVelocity { get; set; } = jumpVelocity; public float PushVelocity { get; set; } = pushVelocity; diff --git a/jRandomSkills - SRC Files/src/player/skills/Deactivator.cs b/jRandomSkills - SRC Files/src/player/skills/Deactivator.cs index 33e883d..627c38a 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Deactivator.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Deactivator.cs @@ -4,6 +4,7 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { @@ -13,7 +14,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -33,7 +34,7 @@ namespace jRandomSkills if (playerInfo == null || playerInfo.Skill != skillName) continue; var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); - HashSet<(string, string)> menuItems = []; + ConcurrentBag<(string, string)> menuItems = []; foreach (var enemy in enemies) { var enemyInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == enemy.SteamID); @@ -74,7 +75,7 @@ namespace jRandomSkills var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); if (enemies.Length > 0) { - HashSet<(string, string)> menuItems = []; + ConcurrentBag<(string, string)> menuItems = []; foreach (var enemy in enemies) { var enemyInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == enemy.SteamID); @@ -118,7 +119,7 @@ namespace jRandomSkills } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#919191", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#919191", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Deaf.cs b/jRandomSkills - SRC Files/src/player/skills/Deaf.cs index 35c1346..1d6501d 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Deaf.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Deaf.cs @@ -5,22 +5,23 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Deaf : ISkill { private const Skills skillName = Skills.Deaf; - private static readonly HashSet deafPlayers = []; + private static readonly ConcurrentDictionary deafPlayers = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - foreach (var player in deafPlayers) + foreach (var player in deafPlayers.Keys) DisableSkill(player); deafPlayers.Clear(); foreach (var player in Utilities.GetPlayers()) @@ -29,7 +30,7 @@ namespace jRandomSkills public static void PlayerMakeSound(UserMessage um) { - foreach (var player in deafPlayers) + foreach (var player in deafPlayers.Keys) um.Recipients.Remove(player); } @@ -44,7 +45,7 @@ namespace jRandomSkills if (playerInfo == null || playerInfo.Skill != skillName) continue; var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); - HashSet<(string, string)> menuItems = enemies.Select(e => (e.PlayerName, e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.UpdateMenu(player, menuItems); } } @@ -70,7 +71,7 @@ namespace jRandomSkills return; } - deafPlayers.Add(enemy); + deafPlayers.TryAdd(enemy, 0); playerInfo.SkillChance = 1; player.PrintToChat($" {ChatColors.Green}" + player.GetTranslation("deaf_player_info", enemy.PlayerName)); enemy.PrintToChat($" {ChatColors.Red}" + player.GetTranslation("deaf_enemy_info")); @@ -85,7 +86,7 @@ namespace jRandomSkills var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); if (enemies.Length > 0) { - HashSet<(string, string)> menuItems = enemies.Select(e => (e.PlayerName, e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.CreateMenu(player, menuItems); } else @@ -94,11 +95,11 @@ namespace jRandomSkills public static void DisableSkill(CCSPlayerController player) { - deafPlayers.Remove(player); + deafPlayers.TryRemove(player, out _); SkillUtils.CloseMenu(player); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#dae01f", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#dae01f", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Disarmament.cs b/jRandomSkills - SRC Files/src/player/skills/Disarmament.cs index 952a72e..cb84648 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Disarmament.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Disarmament.cs @@ -12,7 +12,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); } public static void PlayerHurt(EventPlayerHurt @event) @@ -32,8 +32,7 @@ namespace jRandomSkills var weaponName = weaponServices?.ActiveWeapon?.Value?.DesignerName; if (weaponName != null && !weaponName.Contains("weapon_knife") && !weaponName.Contains("weapon_c4")) - victim.ExecuteClientCommand("slot3"); - //victim.DropActiveWeapon(); + victim.DropActiveWeapon(); } } } @@ -42,14 +41,14 @@ namespace jRandomSkills { var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo == null) return; - float newChance = (float)Instance.Random.NextDouble() * (Config.GetValue(skillName, "chanceTo") - Config.GetValue(skillName, "chanceFrom")) + Config.GetValue(skillName, "chanceFrom"); + float newChance = (float)Instance.Random.NextDouble() * (SkillsInfo.GetValue(skillName, "chanceTo") - SkillsInfo.GetValue(skillName, "chanceFrom")) + SkillsInfo.GetValue(skillName, "chanceFrom"); playerInfo.SkillChance = newChance; newChance = (float)Math.Round(newChance, 2) * 100; newChance = (float)Math.Round(newChance); - SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetTranslation("disarmament")}{ChatColors.Lime}: " + player.GetTranslation("disarmament_desc2", newChance), false); + SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(skillName)}{ChatColors.Lime}: {player.GetSkillDescription(skillName, newChance)}", false); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FF4500", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float chanceFrom = .2f, float chanceTo = .5f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FF4500", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float chanceFrom = .2f, float chanceTo = .35f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float ChanceFrom { get; set; } = chanceFrom; public float ChanceTo { get; set; } = chanceTo; diff --git a/jRandomSkills - SRC Files/src/player/skills/Distancer.cs b/jRandomSkills - SRC Files/src/player/skills/Distancer.cs index c4f3000..5e4a516 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Distancer.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Distancer.cs @@ -2,36 +2,45 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; -using jRandomSkills.src.utils; +using System.Collections.Concurrent; namespace jRandomSkills { public class Distancer : ISkill { private const Skills skillName = Skills.Distancer; - private static readonly HashSet distancerPlayers = []; + private static readonly ConcurrentDictionary distancerPlayers = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - distancerPlayers.Clear(); + lock (setLock) + distancerPlayers.Clear(); } public static void OnTick() { - foreach (var player in distancerPlayers) + if (SkillUtils.IsFreezeTime()) return; + foreach (var player in distancerPlayers.Keys) { + var playerInfo = jRandomSkills.Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; + + if (playerInfo.SkillDescriptionHudExpired >= DateTime.Now) + { + playerInfo.PrintHTML = null; + return; + } + var playerPawn = player.PlayerPawn.Value; if (playerPawn == null || !playerPawn.IsValid) return; if (playerPawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) return; - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; - string closetEnemy = "Bot"; double closetDistance = double.MaxValue; @@ -47,27 +56,22 @@ namespace jRandomSkills } string distanceColor = closetDistance > 1500 ? "#00FF00" : closetDistance > 600 ? "#FFFF00" : "#FF0000"; - - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = $"{closetEnemy}: {closetDistance}
"; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + playerInfo.PrintHTML = $"{closetEnemy}: {closetDistance}"; } } public static void EnableSkill(CCSPlayerController player) { - distancerPlayers.Add(player); + distancerPlayers.TryAdd(player, 0); } public static void DisableSkill(CCSPlayerController player) { - distancerPlayers.Remove(player); + distancerPlayers.TryRemove(player, out _); + SkillUtils.ResetPrintHTML(player); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#00f2ff", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#00f2ff", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Dracula.cs b/jRandomSkills - SRC Files/src/player/skills/Dracula.cs index 3fcb8c8..a49474e 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Dracula.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Dracula.cs @@ -9,11 +9,10 @@ namespace jRandomSkills public class Dracula : ISkill { private const Skills skillName = Skills.Dracula; - private static readonly float healthRegainScale = Config.GetValue(skillName, "healthRegainScale"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerHurt(EventPlayerHurt @event) @@ -33,7 +32,7 @@ namespace jRandomSkills var attackerPawn = attacker.PlayerPawn.Value; if (attackerPawn == null) return; - int newHealth = (int)(attackerPawn.Health + (damage * healthRegainScale)); + int newHealth = (int)(attackerPawn.Health + (damage * SkillsInfo.GetValue(skillName, "healthRegainScale"))); attackerPawn.MaxHealth = Math.Max(newHealth, 100); Utilities.SetStateChanged(attackerPawn, "CBaseEntity", "m_iMaxHealth"); @@ -42,7 +41,7 @@ namespace jRandomSkills Utilities.SetStateChanged(attackerPawn, "CBaseEntity", "m_iHealth"); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FA050D", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float healthRegainScale = .3f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FA050D", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float healthRegainScale = .3f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float HealthRegainScale { get; set; } = healthRegainScale; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Duplicator.cs b/jRandomSkills - SRC Files/src/player/skills/Duplicator.cs index 834b621..7242187 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Duplicator.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Duplicator.cs @@ -4,6 +4,7 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { @@ -13,7 +14,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); } public static void NewRound() @@ -33,7 +34,7 @@ namespace jRandomSkills if (playerInfo == null || playerInfo.Skill != skillName) continue; var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p != player && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); - HashSet<(string, string)> menuItems = []; + ConcurrentBag<(string, string)> menuItems = []; foreach (var enemy in enemies) { var enemyInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == enemy.SteamID); @@ -75,7 +76,7 @@ namespace jRandomSkills if (enemies.Length > 0) { List skills = []; - HashSet<(string, string)> menuItems = []; + ConcurrentBag<(string, string)> menuItems = []; foreach (var enemy in enemies) { var enemyInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == enemy.SteamID); @@ -135,12 +136,19 @@ namespace jRandomSkills playerInfo.Skill = enemySkill; playerInfo.SpecialSkill = skillName; SkillUtils.CloseMenu(player); - Instance.SkillAction(enemySkill.ToString(), "EnableSkill", [player]); + + if (SkillsInfo.GetValue(enemySkill, "disableOnFreezeTime") && SkillUtils.IsFreezeTime()) + Instance?.AddTimer(Math.Max((float)(Event.freezeTimeEnd - DateTime.Now).TotalSeconds, 0), () => { + if (Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID && p.Skill == enemySkill) == null) return; + Instance?.SkillAction(enemySkill.ToString(), "EnableSkill", new[] { player }); + }); + else + Instance?.SkillAction(enemySkill.ToString(), "EnableSkill", new[] { player }); }); player.PrintToChat($" {ChatColors.Green}" + player.GetTranslation("duplicator_player_info", enemy.PlayerName)); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffb73b", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffb73b", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Dwarf.cs b/jRandomSkills - SRC Files/src/player/skills/Dwarf.cs index cb80ed8..389776c 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Dwarf.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Dwarf.cs @@ -13,7 +13,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); } public static void NewRound() @@ -33,12 +33,12 @@ namespace jRandomSkills var playerPawn = player.PlayerPawn?.Value; if (playerPawn != null && player.IsValid) { - float newSize = (float)Instance.Random.NextDouble() * (Config.GetValue(skillName, "maxScale") - Config.GetValue(skillName, "minScale")) + Config.GetValue(skillName, "minScale"); + float newSize = (float)Instance.Random.NextDouble() * (SkillsInfo.GetValue(skillName, "maxScale") - SkillsInfo.GetValue(skillName, "minScale")) + SkillsInfo.GetValue(skillName, "minScale"); newSize = (float)Math.Round(newSize, 2); playerInfo.SkillChance = newSize; SkillUtils.ChangePlayerScale(player, newSize); - SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetTranslation("dwarf")}{ChatColors.Lime}: " + player.GetTranslation("dwarf_desc2", newSize), false); + SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(skillName)}{ChatColors.Lime}: {player.GetSkillDescription(skillName, newSize)}", false); } } @@ -56,7 +56,7 @@ namespace jRandomSkills } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffff00", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float minScale = .6f, float maxScale = .95f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffff00", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float minScale = .6f, float maxScale = .95f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float MinScale { get; set; } = minScale; public float MaxScale { get; set; } = maxScale; diff --git a/jRandomSkills - SRC Files/src/player/skills/Earthquake.cs b/jRandomSkills - SRC Files/src/player/skills/Earthquake.cs index b15e2bd..1c682df 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Earthquake.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Earthquake.cs @@ -4,42 +4,41 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Earthquake : ISkill { private const Skills skillName = Skills.Earthquake; - private static readonly float timerCooldown = Config.GetValue(skillName, "cooldown"); - private static readonly float amplitude = Config.GetValue(skillName, "amplitude"); - private static readonly float frequency = Config.GetValue(skillName, "frequency"); - private static readonly float duration = Config.GetValue(skillName, "duration"); - private static readonly float radius = Config.GetValue(skillName, "radius"); - private static readonly Dictionary SkillPlayerInfo = []; + private static readonly ConcurrentDictionary SkillPlayerInfo = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - SkillPlayerInfo.Clear(); + lock (setLock) + SkillPlayerInfo.Clear(); } public static void EnableSkill(CCSPlayerController player) { - SkillPlayerInfo[player.SteamID] = new PlayerSkillInfo + SkillPlayerInfo.TryAdd(player.SteamID, new PlayerSkillInfo { SteamID = player.SteamID, CanUse = true, Cooldown = DateTime.MinValue, - }; + }); } public static void DisableSkill(CCSPlayerController player) { - SkillPlayerInfo.Remove(player.SteamID); + SkillPlayerInfo.TryRemove(player.SteamID, out _); + SkillUtils.ResetPrintHTML(player); } public static void PlayerDeath(EventPlayerDeath @event) @@ -48,7 +47,7 @@ namespace jRandomSkills if (player == null || !player.IsValid) return; var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo?.Skill == skillName) - SkillPlayerInfo.Remove(player.SteamID); + SkillPlayerInfo.TryRemove(player.SteamID, out _); } public static void OnTick() @@ -67,22 +66,20 @@ namespace jRandomSkills float cooldown = 0; if (skillInfo != null) { - float time = (int)(skillInfo.Cooldown.AddSeconds(timerCooldown) - DateTime.Now).TotalSeconds; + float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue(skillName, "cooldown")) - DateTime.Now).TotalSeconds); cooldown = Math.Max(time, 0); if (cooldown == 0 && skillInfo?.CanUse == false) skillInfo.CanUse = true; } - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; + if (cooldown == 0) + return; - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = cooldown != 0 ? $"{player.GetTranslation("hud_info", $"{cooldown}")}
" : ""; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + string remainingLine = $"{player.GetTranslation("hud_info", $"{cooldown}")}"; } public static void UseSkill(CCSPlayerController player) @@ -119,10 +116,10 @@ namespace jRandomSkills shake.DispatchSpawn(); if (!shake.IsValid) return; - shake.Amplitude = amplitude; - shake.Frequency = frequency; - shake.Duration = duration; - shake.Radius = radius; + shake.Amplitude = SkillsInfo.GetValue(skillName, "amplitude"); + shake.Frequency = SkillsInfo.GetValue(skillName, "frequency"); + shake.Duration = SkillsInfo.GetValue(skillName, "duration"); + shake.Radius = SkillsInfo.GetValue(skillName, "radius"); shake.Teleport(new Vector(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z + pawn.ViewOffset.Z)); shake.AcceptInput("SetParent", pawn, pawn, "!activator"); @@ -136,7 +133,7 @@ namespace jRandomSkills public DateTime Cooldown { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = false, string color = "#42f59b", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float cooldown = 16f, float amplitude = 15f, float frequency = 500f, float duration = 8f, float radius = 50f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = false, string color = "#42f59b", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float cooldown = 16f, float amplitude = 15f, float frequency = 500f, float duration = 8f, float radius = 50f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float Cooldown { get; set; } = cooldown; public float Amplitude { get; set; } = amplitude; diff --git a/jRandomSkills - SRC Files/src/player/skills/EnemySpawn.cs b/jRandomSkills - SRC Files/src/player/skills/EnemySpawn.cs index 6c8c6d9..e5c4768 100644 --- a/jRandomSkills - SRC Files/src/player/skills/EnemySpawn.cs +++ b/jRandomSkills - SRC Files/src/player/skills/EnemySpawn.cs @@ -4,27 +4,30 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class EnemySpawn : ISkill { private const Skills skillName = Skills.EnemySpawn; - private static readonly float timerCooldown = Config.GetValue(skillName, "cooldown"); - private static readonly Dictionary SkillPlayerInfo = []; + private static readonly ConcurrentDictionary SkillPlayerInfo = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - SkillPlayerInfo.Clear(); + lock (setLock) + SkillPlayerInfo.Clear(); } public static void OnTick() { + if (SkillUtils.IsFreezeTime()) return; foreach (var player in Utilities.GetPlayers()) { var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); @@ -36,17 +39,19 @@ namespace jRandomSkills public static void EnableSkill(CCSPlayerController player) { - SkillPlayerInfo[player.SteamID] = new PlayerSkillInfo + var cooldownBeforeUse = SkillsInfo.GetValue(skillName, "cooldownBeforeUse"); + SkillPlayerInfo.TryAdd(player.SteamID, new PlayerSkillInfo { SteamID = player.SteamID, - CanUse = true, - Cooldown = DateTime.MinValue, - }; + CanUse = false, + Cooldown = cooldownBeforeUse <= 0 ? DateTime.MinValue : Event.freezeTimeEnd.AddSeconds(cooldownBeforeUse - SkillsInfo.GetValue(skillName, "cooldown")), + }); } public static void DisableSkill(CCSPlayerController player) { - SkillPlayerInfo.Remove(player.SteamID); + SkillPlayerInfo.TryRemove(player.SteamID, out _); + SkillUtils.ResetPrintHTML(player); } private static void UpdateHUD(CCSPlayerController player, PlayerSkillInfo skillInfo) @@ -54,22 +59,20 @@ namespace jRandomSkills float cooldown = 0; if (skillInfo != null) { - float time = (int)(skillInfo.Cooldown.AddSeconds(timerCooldown) - DateTime.Now).TotalSeconds; + float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue(skillName, "cooldown")) - DateTime.Now).TotalSeconds); cooldown = Math.Max(time, 0); if (cooldown == 0 && skillInfo?.CanUse == false) skillInfo.CanUse = true; } - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = cooldown != 0 ? $"{player.GetTranslation("hud_info", $"{cooldown}")}
" : ""; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + if (cooldown == 0) + playerInfo.PrintHTML = null; + else + playerInfo.PrintHTML = $"{player.GetTranslation("hud_info", $"{cooldown}")}"; } public static void UseSkill(CCSPlayerController player) @@ -109,9 +112,10 @@ namespace jRandomSkills public DateTime Cooldown { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff8c92", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float cooldown = 15f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff8c92", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, float cooldown = 15f, float cooldownBeforeUse = 10f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float Cooldown { get; set; } = cooldown; + public float CooldownBeforeUse { get; set; } = cooldownBeforeUse; } } } \ No newline at end of file diff --git a/jRandomSkills - SRC Files/src/player/skills/ExplosiveShot.cs b/jRandomSkills - SRC Files/src/player/skills/ExplosiveShot.cs index 09dd2a8..0030720 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ExplosiveShot.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ExplosiveShot.cs @@ -5,49 +5,66 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using Vector = CounterStrikeSharp.API.Modules.Utils.Vector; namespace jRandomSkills { public class ExplosiveShot : ISkill { private const Skills skillName = Skills.ExplosiveShot; - private static readonly float damage = Config.GetValue(skillName, "damage"); - private static readonly float damageRadius = Config.GetValue(skillName, "damageRadius"); + + private static readonly QAngle angle = new(5, 10, -4); + private static int lastTick = 0; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); } public static void EnableSkill(CCSPlayerController player) { var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo == null) return; - float newChance = (float)Instance.Random.NextDouble() * (Config.GetValue(skillName, "ChanceTo") - Config.GetValue(skillName, "ChanceFrom")) + Config.GetValue(skillName, "ChanceFrom"); + float newChance = (float)Instance.Random.NextDouble() * (SkillsInfo.GetValue(skillName, "ChanceTo") - SkillsInfo.GetValue(skillName, "ChanceFrom")) + SkillsInfo.GetValue(skillName, "ChanceFrom"); playerInfo.SkillChance = newChance; newChance = (float)Math.Round(newChance, 2) * 100; newChance = (float)Math.Round(newChance); - SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetTranslation("explosiveshot")}{ChatColors.Lime}: " + player.GetTranslation("explosiveshot_desc2", newChance), false); + SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(skillName)}{ChatColors.Lime}: {player.GetSkillDescription(skillName, newChance)}", false); } private static void SpawnExplosion(Vector vector) { - var heProjectile = Utilities.CreateEntityByName("hegrenade_projectile"); - if (heProjectile == null || !heProjectile.IsValid) return; - - Vector pos = vector; - heProjectile.TicksAtZeroVelocity = 100; - heProjectile.TeamNum = (byte)CsTeam.None; - heProjectile.Damage = damage; - heProjectile.DmgRadius = damageRadius; - heProjectile.Teleport(pos, null, new Vector(0, 0, -10)); - heProjectile.DispatchSpawn(); - heProjectile.AcceptInput("InitializeSpawnFromWorld", null, null, ""); - heProjectile.DetonateTime = 0; + lastTick = Server.TickCount; + SkillUtils.CreateHEGrenadeProjectile(vector, angle, new Vector(0, 0, 0), 0); } + public static void OnEntitySpawned(CEntityInstance entity) + { + if (entity.DesignerName != "hegrenade_projectile") return; + + var heProjectile = entity.As(); + if (heProjectile == null || !heProjectile.IsValid || heProjectile.AbsRotation == null) return; + + Server.NextFrame(() => + { + if (heProjectile == null || !heProjectile.IsValid) return; + if (!(NearlyEquals(angle.X, heProjectile.AbsRotation.X) && NearlyEquals(angle.Y, heProjectile.AbsRotation.Y) && NearlyEquals(angle.Z, heProjectile.AbsRotation.Z))) + return; + + heProjectile.TicksAtZeroVelocity = 100; + heProjectile.TeamNum = (byte)CsTeam.None; + heProjectile.Damage = SkillsInfo.GetValue(skillName, "damage"); + heProjectile.DmgRadius = SkillsInfo.GetValue(skillName, "damageRadius"); + heProjectile.DetonateTime = 0; + }); + } + + private static bool NearlyEquals(float a, float b, float epsilon = 0.001f) => Math.Abs(a -b) < epsilon; + public static void OnTakeDamage(DynamicHook h) { + if (lastTick == Server.TickCount) return; + CEntityInstance param = h.GetParam(0); CTakeDamageInfo param2 = h.GetParam(1); @@ -69,7 +86,7 @@ namespace jRandomSkills SpawnExplosion(param2.DamagePosition); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#9c0000", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float damage = 25f, float damageRadius = 210f, float chanceFrom = .15f, float chanceTo = .3f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#9c0000", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float damage = 25f, float damageRadius = 210f, float chanceFrom = .15f, float chanceTo = .3f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float Damage { get; set; } = damage; public float DamageRadius { get; set; } = damageRadius; diff --git a/jRandomSkills - SRC Files/src/player/skills/FalconEye.cs b/jRandomSkills - SRC Files/src/player/skills/FalconEye.cs index bebd74b..59cc02e 100644 --- a/jRandomSkills - SRC Files/src/player/skills/FalconEye.cs +++ b/jRandomSkills - SRC Files/src/player/skills/FalconEye.cs @@ -4,18 +4,18 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using System.Drawing; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class FalconEye : ISkill { private const Skills skillName = Skills.FalconEye; - private static readonly float distance = Config.GetValue(skillName, "distance"); - private static readonly Dictionary cameras = []; + private static readonly ConcurrentDictionary cameras = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -65,7 +65,7 @@ namespace jRandomSkills ChangeCamera(player, true); continue; } - Vector pos = new(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z + 1000); + Vector pos = new(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z + SkillsInfo.GetValue(skillName, "distance")); QAngle angle = new(90, 0, -pawn.V_angle.Y); cameraInfo.Item2.Teleport(pos, angle); } @@ -104,12 +104,12 @@ namespace jRandomSkills var pawn = player.PlayerPawn.Value; if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) return 0; - Vector pos = new(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z + distance); + Vector pos = new(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z + SkillsInfo.GetValue(skillName, "distance")); camera.Render = Color.FromArgb(0, 255, 255, 255); camera.Teleport(pos, new QAngle(90, 0, 0)); camera.DispatchSpawn(); - cameras[player.SteamID] = (pawn.CameraServices!.ViewEntity.Raw, camera); + cameras.AddOrUpdate(player.SteamID, (pawn.CameraServices!.ViewEntity.Raw, camera), (k, v) => (pawn.CameraServices!.ViewEntity.Raw, camera)); return camera.EntityHandle.Raw; } @@ -132,7 +132,7 @@ namespace jRandomSkills } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#d1f542", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float distance = 1000f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#d1f542", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float distance = 1000f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float Distance { get; set; } = distance; } diff --git a/jRandomSkills - SRC Files/src/player/skills/FastReload.cs b/jRandomSkills - SRC Files/src/player/skills/FastReload.cs index efbb244..64edfdc 100644 --- a/jRandomSkills - SRC Files/src/player/skills/FastReload.cs +++ b/jRandomSkills - SRC Files/src/player/skills/FastReload.cs @@ -12,7 +12,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void UseSkill(CCSPlayerController player) @@ -40,7 +40,7 @@ namespace jRandomSkills Utilities.SetStateChanged(activeWeapon, "CBasePlayerWeapon", "m_iClip1"); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffc061", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffc061", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Flash.cs b/jRandomSkills - SRC Files/src/player/skills/Flash.cs index 971f9f0..50c0583 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Flash.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Flash.cs @@ -5,17 +5,18 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Flash : ISkill { private const Skills skillName = Skills.Flash; - public static readonly Dictionary jumpedPlayers = []; + public static readonly ConcurrentDictionary jumpedPlayers = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); } public static void NewRound() @@ -46,7 +47,7 @@ namespace jRandomSkills var player = @event.Userid; if (player == null || !player.IsValid) return; if (!jumpedPlayers.TryGetValue(player.SteamID, out _)) return; - jumpedPlayers[player.SteamID] = Server.TickCount + 20; + jumpedPlayers.AddOrUpdate(player.SteamID, Server.TickCount + 20, (k, v) => Server.TickCount + 20); } public static void EnableSkill(CCSPlayerController player) @@ -55,16 +56,13 @@ namespace jRandomSkills var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerPawn == null || playerInfo == null) return; - var skillConfig = Config.LoadedConfig.SkillsInfo.FirstOrDefault(s => s.Name == skillName.ToString()); - if (skillConfig == null) return; - - float newSpeed = (float)Instance.Random.NextDouble() * (Config.GetValue(skillName, "ChanceTo") - Config.GetValue(skillName, "ChanceFrom")) + Config.GetValue(skillName, "ChanceFrom"); + float newSpeed = (float)Instance.Random.NextDouble() * (SkillsInfo.GetValue(skillName, "ChanceTo") - SkillsInfo.GetValue(skillName, "ChanceFrom")) + SkillsInfo.GetValue(skillName, "ChanceFrom"); newSpeed = (float)Math.Round(newSpeed, 2); playerInfo.SkillChance = newSpeed; jumpedPlayers.TryAdd(player.SteamID, 0); playerPawn.VelocityModifier = newSpeed; - SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetTranslation("flash")}{ChatColors.Lime}: " + player.GetTranslation("flash_desc2", newSpeed), false); + SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(skillName)}{ChatColors.Lime}: {player.GetSkillDescription(skillName, newSpeed)}", false); } public static void DisableSkill(CCSPlayerController player) @@ -72,7 +70,7 @@ namespace jRandomSkills var playerPawn = player.PlayerPawn.Value; if (playerPawn == null) return; playerPawn.VelocityModifier = 1; - jumpedPlayers.Remove(player.SteamID); + jumpedPlayers.TryRemove(player.SteamID, out _); } public static void OnTick() @@ -100,7 +98,7 @@ namespace jRandomSkills } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#A31912", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float chanceFrom = 1.2f, float chanceTo = 3.0f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#A31912", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float chanceFrom = 1.2f, float chanceTo = 3.0f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float ChanceFrom { get; set; } = chanceFrom; public float ChanceTo { get; set; } = chanceTo; diff --git a/jRandomSkills - SRC Files/src/player/skills/Fortnite.cs b/jRandomSkills - SRC Files/src/player/skills/Fortnite.cs index 69d952e..799360a 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Fortnite.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Fortnite.cs @@ -6,29 +6,30 @@ using jRandomSkills.src.player; using jRandomSkills.src.utils; using static CounterStrikeSharp.API.Core.Listeners; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Fortnite : ISkill { private const Skills skillName = Skills.Fortnite; - private static readonly float timerCooldown = Config.GetValue(skillName, "cooldown"); - private static readonly int barricadeHealth = Config.GetValue(skillName, "barricadeHealth"); - private static readonly string propModel = Config.GetValue(skillName, "propModel"); - - private static readonly Dictionary SkillPlayerInfo = []; - private static readonly Dictionary barricades = []; + private static readonly ConcurrentDictionary SkillPlayerInfo = []; + private static readonly ConcurrentDictionary barricades = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); - Instance.RegisterListener((ResourceManifest manifest) => manifest.AddResource(propModel)); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); + Instance.RegisterListener((ResourceManifest manifest) => manifest.AddResource(SkillsInfo.GetValue(skillName, "propModel"))); } public static void NewRound() { - SkillPlayerInfo.Clear(); - barricades.Clear(); + lock (setLock) + { + SkillPlayerInfo.Clear(); + barricades.Clear(); + } } public static void OnTick() @@ -44,17 +45,18 @@ namespace jRandomSkills public static void EnableSkill(CCSPlayerController player) { - SkillPlayerInfo[player.SteamID] = new PlayerSkillInfo + SkillPlayerInfo.TryAdd(player.SteamID, new PlayerSkillInfo { SteamID = player.SteamID, CanUse = true, Cooldown = DateTime.MinValue, - }; + }); } public static void DisableSkill(CCSPlayerController player) { - SkillPlayerInfo.Remove(player.SteamID); + SkillPlayerInfo.TryRemove(player.SteamID, out _); + SkillUtils.ResetPrintHTML(player); } private static void UpdateHUD(CCSPlayerController player, PlayerSkillInfo skillInfo) @@ -62,22 +64,20 @@ namespace jRandomSkills float cooldown = 0; if (skillInfo != null) { - float time = (int)(skillInfo.Cooldown.AddSeconds(timerCooldown) - DateTime.Now).TotalSeconds; + float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue(skillName, "cooldown")) - DateTime.Now).TotalSeconds); cooldown = Math.Max(time, 0); if (cooldown == 0 && skillInfo?.CanUse == false) skillInfo.CanUse = true; } - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = cooldown != 0 ? $"{player.GetTranslation("hud_info", $"{cooldown}")}
" : ""; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + if (cooldown == 0) + playerInfo.PrintHTML = null; + else + playerInfo.PrintHTML = $"{player.GetTranslation("hud_info", $"{cooldown}")}"; } public static void UseSkill(CCSPlayerController player) @@ -111,10 +111,10 @@ namespace jRandomSkills box.Collision.SolidType = SolidType_t.SOLID_VPHYSICS; box.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags = (uint)(box.CBodyComponent!.SceneNode!.Owner!.Entity!.Flags & ~(1 << 2)); box.DispatchSpawn(); - barricades.Add(box.Index, barricadeHealth); + barricades.TryAdd(box.Index, SkillsInfo.GetValue(skillName, "barricadeHealth")); Server.NextFrame(() => { - box.SetModel(propModel); + box.SetModel(SkillsInfo.GetValue(skillName, "propModel")); box.Teleport(pos, angle, null); }); } @@ -137,7 +137,7 @@ namespace jRandomSkills if (barricades.TryGetValue(box.Index, out int health)) { health -= (int)param2.Damage; - barricades[box.Index] = health; + barricades.AddOrUpdate(box.Index, health, (k, v) => health); if (health <= 0) box.AcceptInput("Kill"); } else box.AcceptInput("Kill"); @@ -150,7 +150,7 @@ namespace jRandomSkills public DateTime Cooldown { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1b04cc", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float cooldown = 2f, int barricadeHealth = 115, string propModel = "models/props/de_aztec/hr_aztec/aztec_scaffolding/aztec_scaffold_wall_support_128.vmdl") : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1b04cc", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, float cooldown = 2f, int barricadeHealth = 115, string propModel = "models/props/de_aztec/hr_aztec/aztec_scaffolding/aztec_scaffold_wall_support_128.vmdl") : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float Cooldown { get; set; } = cooldown; public int BarricadeHealth { get; set; } = barricadeHealth; diff --git a/jRandomSkills - SRC Files/src/player/skills/FragileBomb.cs b/jRandomSkills - SRC Files/src/player/skills/FragileBomb.cs index c4ee45f..a4db055 100644 --- a/jRandomSkills - SRC Files/src/player/skills/FragileBomb.cs +++ b/jRandomSkills - SRC Files/src/player/skills/FragileBomb.cs @@ -12,20 +12,21 @@ namespace jRandomSkills public class FragileBomb : ISkill { private const Skills skillName = Skills.FragileBomb; - private static int bombHealth = Config.GetValue(skillName, "maxBombHealth"); - private static readonly int maxBombHealth = Config.GetValue(skillName, "maxBombHealth"); + private static int bombHealth = 1000; + private static int maxBombHealth = 1000; private static CPlantedC4? plantedC4; private static CTriggerMultiple? triggerC4; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - bombHealth = maxBombHealth; + bombHealth = SkillsInfo.GetValue(skillName, "maxBombHealth"); + maxBombHealth = SkillsInfo.GetValue(skillName, "maxBombHealth"); plantedC4 = null; triggerC4 = null; } @@ -110,7 +111,7 @@ namespace jRandomSkills Localization.PrintTranslationToChatAll($" {ChatColors.Gold}{{0}}: {ChatColors.Red}{bombHealth}{ChatColors.Gold}/{ChatColors.Green}{maxBombHealth}", ["fragilebomb_bomb_health"]); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#5d00ff", CsTeam onlyTeam = CsTeam.CounterTerrorist, bool needsTeammates = false, int maxBombHealth = 1000) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#5d00ff", CsTeam onlyTeam = CsTeam.CounterTerrorist, bool disableOnFreezeTime = false, bool needsTeammates = false, int maxBombHealth = 1000) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int MaxBombHealth { get; set; } = maxBombHealth; } diff --git a/jRandomSkills - SRC Files/src/player/skills/FriendlyFire.cs b/jRandomSkills - SRC Files/src/player/skills/FriendlyFire.cs index 464c496..c399433 100644 --- a/jRandomSkills - SRC Files/src/player/skills/FriendlyFire.cs +++ b/jRandomSkills - SRC Files/src/player/skills/FriendlyFire.cs @@ -9,12 +9,11 @@ namespace jRandomSkills public class FriendlyFire : ISkill { private const Skills skillName = Skills.FriendlyFire; - private static readonly float healthMultiplier = Config.GetValue(skillName, "healthMultiplier"); private static readonly string[] nades = ["inferno", "flashbang", "smokegrenade", "decoy", "hegrenade"]; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerHurt(EventPlayerHurt @event) @@ -35,10 +34,10 @@ namespace jRandomSkills var pawn = victim.PlayerPawn.Value; if (pawn == null || !pawn.IsValid) return; - SkillUtils.AddHealth(pawn, damage + (int)(damage * healthMultiplier), pawn.MaxHealth); + SkillUtils.AddHealth(pawn, damage + (int)(damage * SkillsInfo.GetValue(skillName, "healthMultiplier")), pawn.MaxHealth); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff0000", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = true, float healthMultiplier = 1.5f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff0000", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = true, float healthMultiplier = 1.5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float HealthMultiplier { get; set; } = healthMultiplier; } diff --git a/jRandomSkills - SRC Files/src/player/skills/FrozenDecoy.cs b/jRandomSkills - SRC Files/src/player/skills/FrozenDecoy.cs index cd80bec..2719d45 100644 --- a/jRandomSkills - SRC Files/src/player/skills/FrozenDecoy.cs +++ b/jRandomSkills - SRC Files/src/player/skills/FrozenDecoy.cs @@ -10,13 +10,11 @@ namespace jRandomSkills public class FrozenDecoy : ISkill { private const Skills skillName = Skills.FrozenDecoy; - private static readonly float decoyRadius = Config.GetValue(skillName, "triggerRadius"); - private static readonly int slownessMultiplier = Config.GetValue(skillName, "slownessMultiplier"); private static readonly List decoys = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -47,13 +45,14 @@ namespace jRandomSkills foreach (Vector decoyPos in decoys) foreach (var player in Utilities.GetPlayers().Where(p => p.Team == CsTeam.Terrorist || p.Team == CsTeam.CounterTerrorist)) { + var decoyRadius = SkillsInfo.GetValue(skillName, "triggerRadius"); var pawn = player.PlayerPawn.Value; if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) return; double distance = SkillUtils.GetDistance(decoyPos, pawn.AbsOrigin); if (distance <= decoyRadius) { double modifier = Math.Clamp(distance / decoyRadius, 0f, 1f); - pawn.VelocityModifier = (float)Math.Pow(modifier, slownessMultiplier); + pawn.VelocityModifier = (float)Math.Pow(modifier, SkillsInfo.GetValue(skillName, "slownessMultiplier")); } } } @@ -63,7 +62,7 @@ namespace jRandomSkills SkillUtils.TryGiveWeapon(player, CsItem.DecoyGrenade); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#00eaff", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float triggerRadius = 180, int slownessMultiplier = 5) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#00eaff", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float triggerRadius = 180, int slownessMultiplier = 5) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float TriggerRadius { get; set; } = triggerRadius; public int SlownessMultiplier { get; set; } = slownessMultiplier; diff --git a/jRandomSkills - SRC Files/src/player/skills/Gambler.cs b/jRandomSkills - SRC Files/src/player/skills/Gambler.cs index f096292..178f60c 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Gambler.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Gambler.cs @@ -4,17 +4,17 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Gambler : ISkill { private const Skills skillName = Skills.Gambler; - private static readonly int refreshPrice = Config.GetValue(skillName, "refreshPrice"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -50,7 +50,14 @@ namespace jRandomSkills if (skill.Skill != skillName) playerInfo.SpecialSkill = skillName; playerInfo.SkillChance = 1; - Instance.SkillAction(skill.Skill.ToString(), "EnableSkill", [player]); + + if (SkillsInfo.GetValue(skill.Skill, "disableOnFreezeTime") && SkillUtils.IsFreezeTime()) + Instance?.AddTimer(Math.Max((float)(Event.freezeTimeEnd - DateTime.Now).TotalSeconds, 0), () => { + if (Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID && p.Skill == skill.Skill) == null) return; + Instance?.SkillAction(skill.Skill.ToString(), "EnableSkill", new[] { player }); + }); + else + Instance?.SkillAction(skill.Skill.ToString(), "EnableSkill", new[] { player }); }); } @@ -58,7 +65,7 @@ namespace jRandomSkills { if (player == null || !player.IsValid || player.InGameMoneyServices == null) return; var account = player.InGameMoneyServices.Account; - player.InGameMoneyServices.Account = Math.Max(0, account - refreshPrice); + player.InGameMoneyServices.Account = Math.Max(0, account - SkillsInfo.GetValue(skillName, "refreshPrice")); Utilities.SetStateChanged(player, "CCSPlayerController", "m_pInGameMoneyServices"); } @@ -73,10 +80,9 @@ namespace jRandomSkills skills.Remove(firstSkill); var secondSkill = skills[Instance.Random.Next(skills.Count)]; - HashSet<(string, string)> menuItems = [(player.GetSkillName(firstSkill.Skill), firstSkill.Skill.ToString()), - (player.GetSkillName(secondSkill.Skill), secondSkill.Skill.ToString()), - (player.GetTranslation("gambler_more", refreshPrice), skillName.ToString())]; - SkillUtils.CreateMenu(player, menuItems); + ConcurrentBag<(string, string)> menuItems = [(player.GetSkillName(firstSkill.Skill), firstSkill.Skill.ToString()), + (player.GetSkillName(secondSkill.Skill), secondSkill.Skill.ToString())]; + SkillUtils.CreateMenu(player, menuItems, (player.GetTranslation("gambler_more", SkillsInfo.GetValue(skillName, "refreshPrice")), skillName.ToString())); } public static void DisableSkill(CCSPlayerController player) @@ -94,7 +100,7 @@ namespace jRandomSkills if (Utilities.GetPlayers().FindAll(p => p.Team == player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator).Count == 1) { - Config.DefaultSkillInfo[] skillsNeedsTeammates = Config.LoadedConfig.SkillsInfo.Where(s => s.NeedsTeammates).ToArray(); + SkillsInfo.DefaultSkillInfo[] skillsNeedsTeammates = SkillsInfo.LoadedConfig.Where(s => s.NeedsTeammates).ToArray(); skillList.RemoveAll(s => skillsNeedsTeammates.Any(s2 => s2.Name == s.Skill.ToString())); } @@ -106,7 +112,7 @@ namespace jRandomSkills return skillList.Count == 0 ? [Event.noneSkill] : skillList; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#7eff47", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, int refreshPrice = 150) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#7eff47", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, int refreshPrice = 150) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int RefreshPrice { get; set; } = refreshPrice; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Ghost.cs b/jRandomSkills - SRC Files/src/player/skills/Ghost.cs index b60e47c..544be29 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Ghost.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Ghost.cs @@ -7,6 +7,7 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { @@ -25,14 +26,14 @@ namespace jRandomSkills "weapon_g3sg1", "weapon_nova", "weapon_xm1014", "weapon_mag7", "weapon_sawedoff", "weapon_m249", "weapon_negev" ]; - private static readonly Dictionary> invisibleEntities = []; + private static readonly ConcurrentDictionary> invisibleEntities = []; public static void LoadSkill() { - if (Config.LoadedConfig.SkillsInfo.FirstOrDefault(s => s.Name == skillName.ToString())?.Active != true) + if (SkillsInfo.LoadedConfig.FirstOrDefault(s => s.Name == skillName.ToString())?.Active != true) return; - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -85,10 +86,11 @@ namespace jRandomSkills public static void DisableSkill(CCSPlayerController player) { + SkillUtils.ResetPrintHTML(player); SetPlayerVisibility(player, true); SetWeaponVisibility(player, true); SetWeaponAttack(player, false); - invisibleEntities.Remove(player.SteamID); + invisibleEntities.TryRemove(player.SteamID, out _); } public static void OnTick() @@ -98,6 +100,9 @@ namespace jRandomSkills var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo?.Skill == skillName) UpdateHUD(player); + if (!player.PawnIsAlive) + if (invisibleEntities.ContainsKey(player.SteamID)) + invisibleEntities.TryRemove(player.SteamID, out _); } } @@ -121,10 +126,7 @@ namespace jRandomSkills var playerPawn = player.PlayerPawn.Value!; if (playerPawn.WeaponServices == null) return; - // var color = visible ? Color.FromArgb(255, 255, 255, 255) : Color.FromArgb(0, 255, 255, 255); - // var shadowStrength = visible ? 1.0f : 0.0f; - - invisibleEntities.Remove(player.SteamID); + invisibleEntities.TryRemove(player.SteamID, out _); foreach (var weapon in playerPawn.WeaponServices.MyWeapons) { if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid) @@ -137,17 +139,13 @@ namespace jRandomSkills items.Add(weapon.Index); } else - invisibleEntities.Add(player.SteamID, [weapon.Index]); + invisibleEntities.TryAdd(player.SteamID, [weapon.Index]); } - /* - weapon.Value.Render = color; - weapon.Value.ShadowStrength = shadowStrength; - Utilities.SetStateChanged(weapon.Value, "CBaseModelEntity", "m_clrRender");*/ } } if (visible) - invisibleEntities.Remove(player.SteamID); + invisibleEntities.TryRemove(player.SteamID, out _); } private static void SetWeaponAttack(CCSPlayerController player, bool disableWeapon) @@ -174,21 +172,20 @@ namespace jRandomSkills var pawn = player.PlayerPawn.Value; if (pawn == null || !pawn.IsValid || pawn.WeaponServices == null) return; - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; var weapon = pawn.WeaponServices.ActiveWeapon.Value; - if (weapon == null || !weapon.IsValid || !disabledWeapons.Contains(weapon.DesignerName)) return; + if (weapon == null || !weapon.IsValid || !disabledWeapons.Contains(weapon.DesignerName)) + { + playerInfo.PrintHTML = null; + return; + } - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = $"{player.GetTranslation("disabled_weapon")}
"; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + playerInfo.PrintHTML = $"{player.GetTranslation("disabled_weapon")}"; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FFFFFF", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FFFFFF", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Glaz.cs b/jRandomSkills - SRC Files/src/player/skills/Glaz.cs index 6104f50..97199df 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Glaz.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Glaz.cs @@ -4,6 +4,7 @@ using CounterStrikeSharp.API.Core.Attributes; using CounterStrikeSharp.API.Modules.Entities.Constants; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; +using System.Collections.Concurrent; using static jRandomSkills.jRandomSkills; namespace jRandomSkills @@ -11,26 +12,28 @@ namespace jRandomSkills public class Glaz : ISkill { private const Skills skillName = Skills.Glaz; - private readonly static List smokes = []; + private readonly static ConcurrentDictionary smokes = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - smokes.Clear(); + lock (setLock) + smokes.Clear(); } public static void SmokegrenadeDetonate(EventSmokegrenadeDetonate @event) { - smokes.Add(@event.Entityid); + smokes.TryAdd(@event.Entityid, 0); } public static void SmokegrenadeExpired(EventSmokegrenadeExpired @event) { - smokes.Remove(@event.Entityid); + smokes.TryRemove(@event.Entityid, out _); } public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList infoList) @@ -44,7 +47,7 @@ namespace jRandomSkills var observerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == observedPlayer?.SteamID); if (playerInfo?.Skill != skillName && observerInfo?.Skill != skillName) continue; - foreach (var smoke in smokes) + foreach (var smoke in smokes.Keys) info.TransmitEntities.Remove(smoke); } } @@ -55,7 +58,7 @@ namespace jRandomSkills SkillUtils.TryGiveWeapon(player, CsItem.SmokeGrenade); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#5d00ff", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#5d00ff", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Glitch.cs b/jRandomSkills - SRC Files/src/player/skills/Glitch.cs index a47b7a0..ad3563e 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Glitch.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Glitch.cs @@ -4,22 +4,25 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Glitch : ISkill { private const Skills skillName = Skills.Glitch; - private static readonly HashSet glitchedPlayers = []; + private static readonly ConcurrentDictionary glitchedPlayers = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - glitchedPlayers.Clear(); + lock (setLock) + glitchedPlayers.Clear(); foreach (var player in Utilities.GetPlayers()) { DisableSkill(player); @@ -44,7 +47,7 @@ namespace jRandomSkills if (playerInfo == null || playerInfo.Skill != skillName) continue; var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); - HashSet<(string, string)> menuItems = enemies.Select(e => (e.PlayerName, e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.UpdateMenu(player, menuItems); } } @@ -70,7 +73,7 @@ namespace jRandomSkills return; } - glitchedPlayers.Add(enemy); + glitchedPlayers.TryAdd(enemy, 0); enemy.ReplicateConVar("sv_disable_radar", "1"); playerInfo.SkillChance = 1; player.PrintToChat($" {ChatColors.Green}" + player.GetTranslation("glitch_player_info", enemy.PlayerName)); @@ -86,7 +89,7 @@ namespace jRandomSkills var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); if (enemies.Length > 0) { - HashSet<(string, string)> menuItems = enemies.Select(e => (e.PlayerName, e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.CreateMenu(player, menuItems); } else @@ -96,11 +99,11 @@ namespace jRandomSkills public static void DisableSkill(CCSPlayerController player) { player.ReplicateConVar("sv_disable_radar", "0"); - glitchedPlayers.Remove(player); + glitchedPlayers.TryRemove(player, out _); SkillUtils.CloseMenu(player); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#f542ef", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#f542ef", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Glue.cs b/jRandomSkills - SRC Files/src/player/skills/Glue.cs index 3bed166..746e01a 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Glue.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Glue.cs @@ -11,7 +11,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void OnEntitySpawned(CEntityInstance entity) @@ -34,7 +34,7 @@ namespace jRandomSkills grenade.Bounces = 555; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#fff52e", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#fff52e", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/GodMode.cs b/jRandomSkills - SRC Files/src/player/skills/GodMode.cs index eca08c2..e1db45a 100644 --- a/jRandomSkills - SRC Files/src/player/skills/GodMode.cs +++ b/jRandomSkills - SRC Files/src/player/skills/GodMode.cs @@ -4,34 +4,35 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class GodMode : ISkill { private const Skills skillName = Skills.GodMode; - private static readonly float timerCooldown = Config.GetValue(skillName, "cooldown"); - private static readonly float duration = Config.GetValue(skillName, "duration"); - private static readonly Dictionary SkillPlayerInfo = []; + private static readonly ConcurrentDictionary SkillPlayerInfo = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - SkillPlayerInfo.Clear(); + lock (setLock) + SkillPlayerInfo.Clear(); } public static void EnableSkill(CCSPlayerController player) { - SkillPlayerInfo[player.SteamID] = new PlayerSkillInfo + SkillPlayerInfo.TryAdd(player.SteamID, new PlayerSkillInfo { SteamID = player.SteamID, CanUse = true, Cooldown = DateTime.MinValue, - }; + }); } public static void OnTick() @@ -47,7 +48,8 @@ namespace jRandomSkills public static void DisableSkill(CCSPlayerController player) { - SkillPlayerInfo.Remove(player.SteamID); + SkillPlayerInfo.TryRemove(player.SteamID, out _); + SkillUtils.ResetPrintHTML(player); } private static void UpdateHUD(CCSPlayerController player, PlayerSkillInfo skillInfo) @@ -55,22 +57,20 @@ namespace jRandomSkills float cooldown = 0; if (skillInfo != null) { - float time = (int)(skillInfo.Cooldown.AddSeconds(timerCooldown) - DateTime.Now).TotalSeconds; + float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue(skillName, "cooldown")) - DateTime.Now).TotalSeconds); cooldown = Math.Max(time, 0); if (cooldown == 0 && skillInfo?.CanUse == false) skillInfo.CanUse = true; } - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = cooldown != 0 ? $"{player.GetTranslation("hud_info", $"{cooldown}")}
" : ""; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + if (cooldown == 0) + playerInfo.PrintHTML = null; + else + playerInfo.PrintHTML = $"{player.GetTranslation("hud_info", $"{cooldown}")}"; } public static void UseSkill(CCSPlayerController player) @@ -89,7 +89,7 @@ namespace jRandomSkills player.PrintToChat($" {ChatColors.Green} {player.GetTranslation("godmode_on")}"); player.PlayerPawn.Value.TakesDamage = false; - Instance.AddTimer(duration, () => { + Instance.AddTimer(SkillsInfo.GetValue(skillName, "duration"), () => { if (player.IsValid && player.PawnIsAlive) { player.PlayerPawn.Value.TakesDamage = true; @@ -107,7 +107,7 @@ namespace jRandomSkills public DateTime Cooldown { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#e0d83a", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float cooldown = 30f, float duration = 2f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#e0d83a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, float cooldown = 30f, float duration = 2f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float Cooldown { get; set; } = cooldown; public float Duration { get; set; } = duration; diff --git a/jRandomSkills - SRC Files/src/player/skills/Grenadier.cs b/jRandomSkills - SRC Files/src/player/skills/Grenadier.cs index 51df178..83fcdfb 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Grenadier.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Grenadier.cs @@ -12,7 +12,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void EnableSkill(CCSPlayerController player) @@ -32,7 +32,7 @@ namespace jRandomSkills player!.GiveNamedItem($"weapon_{weapon}"); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#4a6e21", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#4a6e21", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/HealingSmoke.cs b/jRandomSkills - SRC Files/src/player/skills/HealingSmoke.cs index 6520f20..590843b 100644 --- a/jRandomSkills - SRC Files/src/player/skills/HealingSmoke.cs +++ b/jRandomSkills - SRC Files/src/player/skills/HealingSmoke.cs @@ -10,13 +10,11 @@ namespace jRandomSkills public class HealingSmoke : ISkill { private const Skills skillName = Skills.HealingSmoke; - private static readonly int smokeHeal = Config.GetValue(skillName, "smokeHeal"); - private static readonly float smokeRadius = Config.GetValue(skillName, "smokeRadius"); private static readonly List smokes = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -74,8 +72,8 @@ namespace jRandomSkills foreach (var player in Utilities.GetPlayers()) if (Server.TickCount % 17 == 0) if (player.PlayerPawn.Value != null && player.PlayerPawn.Value.IsValid && player.PlayerPawn.Value.AbsOrigin != null) - if (SkillUtils.GetDistance(smokePos, player.PlayerPawn.Value.AbsOrigin) <= smokeRadius) - AddHealth(player.PlayerPawn.Value, smokeHeal); + if (SkillUtils.GetDistance(smokePos, player.PlayerPawn.Value.AbsOrigin) <= SkillsInfo.GetValue(skillName, "smokeRadius")) + AddHealth(player.PlayerPawn.Value, SkillsInfo.GetValue(skillName, "smokeHeal")); } public static void EnableSkill(CCSPlayerController player) @@ -95,7 +93,7 @@ namespace jRandomSkills Utilities.SetStateChanged(player, "CBaseEntity", "m_iHealth"); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1fe070", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, int smokeHeal = 1, float smokeRadius = 180) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1fe070", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, int smokeHeal = 1, float smokeRadius = 180) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int SmokeHeal { get; set; } = smokeHeal; public float SmokeRadius { get; set; } = smokeRadius; diff --git a/jRandomSkills - SRC Files/src/player/skills/Hermit.cs b/jRandomSkills - SRC Files/src/player/skills/Hermit.cs index 307f802..7a92ecc 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Hermit.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Hermit.cs @@ -3,31 +3,30 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Hermit : ISkill { private const Skills skillName = Skills.Hermit; - private static readonly Dictionary dictionary = new() - { - { "weapon_glock", 120 }, { "weapon_usp_silencer", 24 }, { "weapon_hkp2000", 52 }, { "weapon_p250", 26 }, - { "weapon_cz75", 12 }, { "weapon_deagle", 35 }, { "weapon_fiveseven", 100 }, { "weapon_elite", 120 }, - { "weapon_tec9", 90 }, { "weapon_revolver", 8 }, { "weapon_mac10", 100 }, { "weapon_mp9", 120 }, - { "weapon_mp7", 120 }, { "weapon_mp5", 120 }, { "weapon_mp5sd", 120 }, { "weapon_ump45", 100 }, - { "weapon_p90", 100 }, { "weapon_bizon", 120 }, { "weapon_ak47", 90 }, { "weapon_m4a1", 90 }, - { "weapon_m4a1_silencer", 80 }, { "weapon_galilar", 90 }, { "weapon_famas", 90 }, { "weapon_aug", 90 }, - { "weapon_sg556", 90 }, { "weapon_ssg08", 90 }, { "weapon_awp", 30 }, { "weapon_scar20", 90 }, - { "weapon_g3sg1", 90 }, { "weapon_nova", 32 }, { "weapon_xm1014", 32 }, { "weapon_sawedoff", 32 }, - { "weapon_mag7", 32 }, { "weapon_m249", 200 }, { "weapon_negev", 300 } - }; - private static readonly Dictionary maxReserveAmmo = dictionary; - - private static readonly int healthToAdd = Config.GetValue(skillName, "healthToAdd"); + private static readonly ConcurrentDictionary ConcurrentDictionary = new( + [ + new KeyValuePair("weapon_glock", 120), new KeyValuePair("weapon_usp_silencer", 24), new KeyValuePair("weapon_hkp2000", 52), new KeyValuePair("weapon_p250", 26), + new KeyValuePair("weapon_cz75", 12), new KeyValuePair("weapon_deagle", 35), new KeyValuePair("weapon_fiveseven", 100), new KeyValuePair("weapon_elite", 120), + new KeyValuePair("weapon_tec9", 90), new KeyValuePair("weapon_revolver", 8), new KeyValuePair("weapon_mac10", 100), new KeyValuePair("weapon_mp9", 120), + new KeyValuePair("weapon_mp7", 120), new KeyValuePair("weapon_mp5", 120), new KeyValuePair("weapon_mp5sd", 120), new KeyValuePair("weapon_ump45", 100), + new KeyValuePair("weapon_p90", 100), new KeyValuePair("weapon_bizon", 120), new KeyValuePair("weapon_ak47", 90), new KeyValuePair("weapon_m4a1", 90), + new KeyValuePair("weapon_m4a1_silencer", 80), new KeyValuePair("weapon_galilar", 90), new KeyValuePair("weapon_famas", 90), new KeyValuePair("weapon_aug", 90), + new KeyValuePair("weapon_sg556", 90), new KeyValuePair("weapon_ssg08", 90), new KeyValuePair("weapon_awp", 30), new KeyValuePair("weapon_scar20", 90), + new KeyValuePair("weapon_g3sg1", 90), new KeyValuePair("weapon_nova", 32), new KeyValuePair("weapon_xm1014", 32), new KeyValuePair("weapon_sawedoff", 32), + new KeyValuePair("weapon_mag7", 32), new KeyValuePair("weapon_m249", 200), new KeyValuePair("weapon_negev", 300) + ]); + private static readonly ConcurrentDictionary maxReserveAmmo = ConcurrentDictionary; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerDeath(EventPlayerDeath @event) @@ -50,10 +49,10 @@ namespace jRandomSkills Utilities.SetStateChanged(weapon, "CBasePlayerWeapon", "m_iClip1"); Utilities.SetStateChanged(weapon, "CBasePlayerWeapon", "m_pReserveAmmo"); - SkillUtils.AddHealth(pawn, healthToAdd); + SkillUtils.AddHealth(pawn, SkillsInfo.GetValue(skillName, "healthToAdd")); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ded678", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, int healthToAdd = 25) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ded678", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, int healthToAdd = 25) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int HealthToAdd { get; set; } = healthToAdd; } diff --git a/jRandomSkills - SRC Files/src/player/skills/HolyHandGrenade.cs b/jRandomSkills - SRC Files/src/player/skills/HolyHandGrenade.cs index 8bd53c0..5458b32 100644 --- a/jRandomSkills - SRC Files/src/player/skills/HolyHandGrenade.cs +++ b/jRandomSkills - SRC Files/src/player/skills/HolyHandGrenade.cs @@ -10,12 +10,10 @@ namespace jRandomSkills public class HolyHandGrenade : ISkill { private const Skills skillName = Skills.HolyHandGrenade; - private static readonly float damageMultiplier = Config.GetValue(skillName, "damageMultiplier"); - private static readonly float damageRadiusMultiplier = Config.GetValue(skillName, "damageRadiusMultiplier"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void OnEntitySpawned(CEntityInstance @event) @@ -36,8 +34,8 @@ namespace jRandomSkills var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player?.SteamID); if (playerInfo?.Skill != skillName) return; - hegrenade.Damage *= damageMultiplier; - hegrenade.DmgRadius *= damageRadiusMultiplier; + hegrenade.Damage *= SkillsInfo.GetValue(skillName, "damageMultiplier"); + hegrenade.DmgRadius *= SkillsInfo.GetValue(skillName, "damageRadiusMultiplier"); }); } @@ -46,7 +44,7 @@ namespace jRandomSkills SkillUtils.TryGiveWeapon(player, CsItem.HEGrenade); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffdd00", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float damageMultiplier = 2f, float damageRadiusMultiplier = 2f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffdd00", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float damageMultiplier = 2f, float damageRadiusMultiplier = 2f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float DamageMultiplier { get; set; } = damageMultiplier; public float DamageRadiusMultiplier { get; set; } = damageRadiusMultiplier; diff --git a/jRandomSkills - SRC Files/src/player/skills/Impostor.cs b/jRandomSkills - SRC Files/src/player/skills/Impostor.cs index 39956b9..7008a15 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Impostor.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Impostor.cs @@ -15,7 +15,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void EnableSkill(CCSPlayerController player) @@ -58,7 +58,7 @@ namespace jRandomSkills }); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#99140B", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#99140B", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/InfiniteAmmo.cs b/jRandomSkills - SRC Files/src/player/skills/InfiniteAmmo.cs index 805b9dd..452d4d0 100644 --- a/jRandomSkills - SRC Files/src/player/skills/InfiniteAmmo.cs +++ b/jRandomSkills - SRC Files/src/player/skills/InfiniteAmmo.cs @@ -11,7 +11,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void WeaponFire(EventWeaponFire @event) @@ -53,7 +53,7 @@ namespace jRandomSkills activeWeaponHandle.Value.Clip1 = 100; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#0000FF", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#0000FF", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Jackal.cs b/jRandomSkills - SRC Files/src/player/skills/Jackal.cs index d648a62..fda9c6f 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Jackal.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Jackal.cs @@ -5,18 +5,18 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using System.Drawing; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Jackal : ISkill { private const Skills skillName = Skills.Jackal; - private static readonly int maxStepBeam = Config.GetValue(skillName, "maxStepBeam"); - private static readonly Dictionary> stepBeams = []; + private static readonly ConcurrentDictionary> stepBeams = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -36,7 +36,7 @@ namespace jRandomSkills var player = step.Key; if (!player.IsValid) { - stepBeams.Remove(player); + stepBeams.TryRemove(player, out _); continue; } @@ -51,7 +51,7 @@ namespace jRandomSkills if (newBeam != null) step.Value.Add(newBeam); - if (beams?.Count >= maxStepBeam) + if (beams?.Count >= SkillsInfo.GetValue(skillName, "maxStepBeam")) { beams[0].AcceptInput("Kill"); step.Value.RemoveAt(0); @@ -104,9 +104,9 @@ namespace jRandomSkills public static void EnableSkill(CCSPlayerController _) { SkillUtils.EnableTransmit(); - foreach (var _player in Utilities.GetPlayers().Where(p => p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator)) + foreach (var _player in Utilities.GetPlayers().Where(p => p.IsValid && !p.IsBot && !p.IsHLTV && p.PawnIsAlive && p.Team is CsTeam.CounterTerrorist or CsTeam.Terrorist)) if (!stepBeams.ContainsKey(_player)) - stepBeams.Add(_player, []); + stepBeams.TryAdd(_player, []); } public static void DisableSkill(CCSPlayerController player) @@ -119,7 +119,7 @@ namespace jRandomSkills NewRound(); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#f542ef", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, int maxStepBeam = 100) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#f542ef", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, int maxStepBeam = 100) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int MaxStepBeam { get; set; } = maxStepBeam; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Jammer.cs b/jRandomSkills - SRC Files/src/player/skills/Jammer.cs index 79e01b4..341b68e 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Jammer.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Jammer.cs @@ -4,24 +4,29 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Jammer : ISkill { private const Skills skillName = Skills.Jammer; - private static readonly HashSet jammedPlayers = []; + private static readonly ConcurrentDictionary jammedPlayers = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); } public static void NewRound() { - foreach (var player in jammedPlayers) - SetCrosshair(player, true); - jammedPlayers.Clear(); + lock (setLock) + { + foreach (var player in jammedPlayers.Keys) + SetCrosshair(player, true); + jammedPlayers.Clear(); + } foreach (var player in Utilities.GetPlayers()) SkillUtils.CloseMenu(player); } @@ -37,7 +42,7 @@ namespace jRandomSkills if (playerInfo == null || playerInfo.Skill != skillName) continue; var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); - HashSet<(string, string)> menuItems = enemies.Select(e => (e.PlayerName, e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.UpdateMenu(player, menuItems); } } @@ -63,7 +68,7 @@ namespace jRandomSkills return; } - jammedPlayers.Add(enemy); + jammedPlayers.TryAdd(enemy, 0); SetCrosshair(enemy, false); playerInfo.SkillChance = 1; player.PrintToChat($" {ChatColors.Green}" + player.GetTranslation("jammer_player_info", enemy.PlayerName)); @@ -89,7 +94,7 @@ namespace jRandomSkills var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); if (enemies.Length > 0) { - HashSet<(string, string)> menuItems = enemies.Select(e => (e.PlayerName, e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.CreateMenu(player, menuItems); } else @@ -99,11 +104,11 @@ namespace jRandomSkills public static void DisableSkill(CCSPlayerController player) { SetCrosshair(player, true); - jammedPlayers.Remove(player); + jammedPlayers.TryRemove(player, out _); SkillUtils.CloseMenu(player); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#42f5a7", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#42f5a7", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Jester.cs b/jRandomSkills - SRC Files/src/player/skills/Jester.cs index 292c802..e65c3bf 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Jester.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Jester.cs @@ -13,12 +13,10 @@ namespace jRandomSkills private const Skills skillName = Skills.Jester; private static bool jesterMode = false; private static bool jesterStarted = false; - private static readonly float minTime = Config.GetValue(skillName, "minTime"); - private static readonly float maxTime = Config.GetValue(skillName, "maxTime"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -26,6 +24,11 @@ namespace jRandomSkills jesterStarted = false; } + public static void DisableSkill(CCSPlayerController player) + { + SkillUtils.ResetPrintHTML(player); + } + public static void PlayerHurt(EventPlayerHurt @event) { if (!jesterMode) return; @@ -57,6 +60,8 @@ namespace jRandomSkills { if (!jesterStarted) { + var minTime = SkillsInfo.GetValue(skillName, "minTime"); + var maxTime = SkillsInfo.GetValue(skillName, "maxTime"); float wait = (float)Instance.Random.NextDouble() * (maxTime - minTime) + minTime; Instance.AddTimer(wait, ChangeMode); jesterStarted = true; @@ -78,6 +83,8 @@ namespace jRandomSkills player.ExecuteClientCommand("play sounds/weapons/taser/taser_charge_ready"); } } + var minTime = SkillsInfo.GetValue(skillName, "minTime"); + var maxTime = SkillsInfo.GetValue(skillName, "maxTime"); float wait = (float)Instance.Random.NextDouble() * (maxTime - minTime) + minTime; Instance.AddTimer(wait, ChangeMode); } @@ -92,6 +99,7 @@ namespace jRandomSkills public static void OnTick() { + if (SkillUtils.IsFreezeTime()) return; foreach (var player in Utilities.GetPlayers().Where(p => !p.IsBot && p.PawnIsAlive)) { if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid) continue; @@ -103,18 +111,12 @@ namespace jRandomSkills private static void UpdateHUD(CCSPlayerController player) { - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; - - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = $"{player.GetTranslation("jester_mode")}: {player.GetTranslation(jesterMode ? "jester_on" : "jester_off")}
"; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; + playerInfo.PrintHTML = $"{player.GetTranslation("jester_mode")}: {player.GetTranslation(jesterMode ? "jester_on" : "jester_off")}"; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8f108f", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float minTime = 10f, float maxTime = 25f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8f108f", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float minTime = 10f, float maxTime = 25f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float MinTime { get; set; } = minTime; public float MaxTime { get; set; } = maxTime; diff --git a/jRandomSkills - SRC Files/src/player/skills/JumpBan.cs b/jRandomSkills - SRC Files/src/player/skills/JumpBan.cs index 2b3ebd3..7761588 100644 --- a/jRandomSkills - SRC Files/src/player/skills/JumpBan.cs +++ b/jRandomSkills - SRC Files/src/player/skills/JumpBan.cs @@ -4,17 +4,18 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class JumpBan : ISkill { private const Skills skillName = Skills.JumpBan; - public static readonly Dictionary bannedPlayers = []; + public static readonly ConcurrentDictionary bannedPlayers = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -29,7 +30,7 @@ namespace jRandomSkills var player = @event.Userid; if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid) return; if (!bannedPlayers.TryGetValue(player.PlayerPawn.Value, out _)) return; - bannedPlayers[player.PlayerPawn.Value] = Server.TickCount + 10; + bannedPlayers.AddOrUpdate(player.PlayerPawn.Value, Server.TickCount + 10, (k, v) => Server.TickCount + 10); } public static void OnTick() @@ -52,7 +53,7 @@ namespace jRandomSkills if (playerInfo == null || playerInfo.Skill != skillName) continue; var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); - HashSet<(string, string)> menuItems = enemies.Select(e => (e.PlayerName, e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.UpdateMenu(player, menuItems); } } @@ -78,7 +79,7 @@ namespace jRandomSkills return; } - bannedPlayers[enemy.PlayerPawn.Value] = 0; + bannedPlayers.AddOrUpdate(enemy.PlayerPawn.Value, 0, (k, v) => 0); playerInfo.SkillChance = 1; player.PrintToChat($" {ChatColors.Green}" + player.GetTranslation("jumpban_player_info", enemy.PlayerName)); enemy.PrintToChat($" {ChatColors.Red}" + player.GetTranslation("jumpban_enemy_info")); @@ -93,7 +94,7 @@ namespace jRandomSkills var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); if (enemies.Length > 0) { - HashSet<(string, string)> menuItems = enemies.Select(e => (e.PlayerName, e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.CreateMenu(player, menuItems); } else @@ -103,11 +104,11 @@ namespace jRandomSkills public static void DisableSkill(CCSPlayerController player) { if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid) return; - bannedPlayers.Remove(player.PlayerPawn.Value); + bannedPlayers.TryRemove(player.PlayerPawn.Value, out _); SkillUtils.CloseMenu(player); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#b01e5d", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#b01e5d", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/JumpingJack.cs b/jRandomSkills - SRC Files/src/player/skills/JumpingJack.cs index f7fd383..bed8eb9 100644 --- a/jRandomSkills - SRC Files/src/player/skills/JumpingJack.cs +++ b/jRandomSkills - SRC Files/src/player/skills/JumpingJack.cs @@ -8,11 +8,10 @@ namespace jRandomSkills public class JumpingJack : ISkill { private const Skills skillName = Skills.JumpingJack; - private static readonly int addHealth = Config.GetValue(skillName, "healthToAdd"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerJump(EventPlayerJump @event) @@ -21,10 +20,10 @@ namespace jRandomSkills if (player == null || !player.IsValid) return; var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo?.Skill != skillName) return; - SkillUtils.AddHealth(player.PlayerPawn.Value, addHealth); + SkillUtils.AddHealth(player.PlayerPawn.Value, SkillsInfo.GetValue(skillName, "healthToAdd")); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a86eff", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, int healthToAdd = 3) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a86eff", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, int healthToAdd = 3) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int HealthToAdd { get; set; } = healthToAdd; } diff --git a/jRandomSkills - SRC Files/src/player/skills/KillerFlash.cs b/jRandomSkills - SRC Files/src/player/skills/KillerFlash.cs index b85ec6d..bce8de5 100644 --- a/jRandomSkills - SRC Files/src/player/skills/KillerFlash.cs +++ b/jRandomSkills - SRC Files/src/player/skills/KillerFlash.cs @@ -1,9 +1,7 @@ -using CounterStrikeSharp.API; -using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Entities.Constants; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; -using Microsoft.Extensions.Logging; using static jRandomSkills.jRandomSkills; namespace jRandomSkills @@ -11,11 +9,10 @@ namespace jRandomSkills public class KillerFlash : ISkill { private const Skills skillName = Skills.KillerFlash; - private static readonly float flashDuration = Config.GetValue(skillName, "flashDuration"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerBlind(EventPlayerBlind @event) @@ -27,7 +24,7 @@ namespace jRandomSkills var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player?.SteamID); var attackerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == attacker?.SteamID); - if (attackerInfo?.Skill == skillName && playerInfo?.Skill != Skills.AntyFlash && player!.PlayerPawn.Value!.FlashDuration >= flashDuration) + if (attackerInfo?.Skill == skillName && playerInfo?.Skill != Skills.AntyFlash && player!.PlayerPawn.Value!.FlashDuration >= SkillsInfo.GetValue(skillName, "flashDuration")) player?.PlayerPawn?.Value?.CommitSuicide(false, true); } @@ -36,7 +33,7 @@ namespace jRandomSkills SkillUtils.TryGiveWeapon(player, CsItem.FlashbangGrenade); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#57bcff", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float flashDuration = 1f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#57bcff", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float flashDuration = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float FlashDuration { get; set; } = flashDuration; } diff --git a/jRandomSkills - SRC Files/src/player/skills/LifeSwap.cs b/jRandomSkills - SRC Files/src/player/skills/LifeSwap.cs index 228d00c..fd46fb6 100644 --- a/jRandomSkills - SRC Files/src/player/skills/LifeSwap.cs +++ b/jRandomSkills - SRC Files/src/player/skills/LifeSwap.cs @@ -4,6 +4,7 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { @@ -13,7 +14,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -33,7 +34,7 @@ namespace jRandomSkills if (playerInfo == null || playerInfo.Skill != skillName) continue; var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); - HashSet<(string, string)> menuItems = enemies.Select(e => ($"{e.PlayerName} : {e.PawnHealth} HP", e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => ($"{e.PlayerName} : {e.PawnHealth} HP", e.Index.ToString()))); SkillUtils.UpdateMenu(player, menuItems); } } @@ -74,7 +75,7 @@ namespace jRandomSkills var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); if (enemies.Length > 0) { - HashSet<(string, string)> menuItems = enemies.Select(e => ($"{e.PlayerName} : {e.PawnHealth} HP", e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => ($"{e.PlayerName} : {e.PawnHealth} HP", e.Index.ToString()))); SkillUtils.CreateMenu(player, menuItems); } else @@ -99,7 +100,7 @@ namespace jRandomSkills SkillUtils.CloseMenu(player); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a3651a", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a3651a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/LongKnife.cs b/jRandomSkills - SRC Files/src/player/skills/LongKnife.cs index a41f6bc..0aa9f2b 100644 --- a/jRandomSkills - SRC Files/src/player/skills/LongKnife.cs +++ b/jRandomSkills - SRC Files/src/player/skills/LongKnife.cs @@ -1,6 +1,7 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Entities.Constants; +using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; using CounterStrikeSharp.API.Modules.Utils; using CS2TraceRay.Class; using CS2TraceRay.Enum; @@ -16,14 +17,63 @@ namespace jRandomSkills public class LongKnife : ISkill { private const Skills skillName = Skills.LongKnife; - private static readonly float maxDistance = Config.GetValue(skillName, "maxDistance"); + + private static bool hooked = false; + private const int actionCode = 503; + private static readonly MemoryFunctionVoid Shoot_Secondary = new(GameData.GetSignature("Shoot_Secondary")); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } - public unsafe static void WeaponFire(EventWeaponFire @event) + public static void NewRound() + { + Shoot_Secondary.Unhook(ShootSecondary, HookMode.Pre); + } + + public static void EnableSkill(CCSPlayerController _) + { + if (hooked) return; + hooked = true; + Shoot_Secondary.Hook(ShootSecondary, HookMode.Pre); + } + + public static void DisableSkill(CCSPlayerController player) + { + var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); + if (playerInfo == null) return; + + playerInfo.Skill = Skills.None; + if (!Instance.SkillPlayer.Any(p => p.Skill == skillName)) + { + Shoot_Secondary.Unhook(ShootSecondary, HookMode.Pre); + hooked = false; + } + } + + public static HookResult ShootSecondary(DynamicHook hook) + { + var weapon = hook.GetParam(0); + var action = hook.GetParam(1); + + if (action != actionCode || weapon?.DesignerName != "weapon_knife") return HookResult.Continue; + if (weapon.OwnerEntity.Value == null || !weapon.OwnerEntity.Value.IsValid) return HookResult.Continue; + + var pawn = weapon.OwnerEntity.Value.As(); + if (pawn == null || !pawn.IsValid || pawn.Controller.Value == null || !pawn.Controller.Value.IsValid) return HookResult.Continue; + + var player = pawn.Controller.Value.As(); + if (player == null || !player.IsValid) return HookResult.Continue; + + var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); + if (playerInfo == null || playerInfo.Skill != skillName) return HookResult.Continue; + + KnifeHit(player, true); + return HookResult.Continue; + } + + public static void WeaponFire(EventWeaponFire @event) { var player = @event.Userid; if (!Instance.IsPlayerValid(player)) return; @@ -37,8 +87,16 @@ namespace jRandomSkills var activeWeapon = pawn.WeaponServices.ActiveWeapon.Value; if (activeWeapon == null || !activeWeapon.IsValid || activeWeapon.DesignerName != "weapon_knife") return; + KnifeHit(player, false); + } + + private unsafe static void KnifeHit(CCSPlayerController player, bool heavyHit) + { + var pawn = player!.PlayerPawn.Value; + if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) return; + Vector eyePos = new(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z + pawn.ViewOffset.Z); - Vector endPos = eyePos + SkillUtils.GetForwardVector(pawn.EyeAngles) * maxDistance; + Vector endPos = eyePos + SkillUtils.GetForwardVector(pawn.EyeAngles) * SkillsInfo.GetValue(skillName, "maxDistance"); Ray ray = new(Vector3.Zero); CTraceFilter filter = new(pawn.Index, pawn.Index) @@ -55,9 +113,9 @@ namespace jRandomSkills filter.m_nHierarchyIds[0] = pawn.GetHierarchyId(); filter.m_nHierarchyIds[1] = 0; - CGameTrace trace = TraceRay.TraceHull(eyePos, endPos, filter, ray); + CGameTrace trace = TraceRay.TraceHull(eyePos, endPos, filter, ray); - if (Config.LoadedConfig.Settings.CS2TraceRayDebug) + if (Config.LoadedConfig.CS2TraceRayDebug) { CreateLine(eyePos, endPos, Color.FromArgb(255, 255, 255, 0)); CreateLine(new Vector(trace.StartPos.X, trace.StartPos.Y, trace.StartPos.Z), new Vector(trace.EndPos.X, trace.EndPos.Y, trace.EndPos.Z), Color.FromArgb(255, 255, 0, 0)); @@ -77,7 +135,7 @@ namespace jRandomSkills if (target.Handle == player.Handle || target.PlayerPawn.Value == null || !target.PlayerPawn.Value.IsValid || trace.Distance() <= 70) return; target.PlayerPawn.Value.EmitSound("Player.DamageBody.Onlooker"); - SkillUtils.TakeHealth(target.PlayerPawn.Value, Instance.Random.Next(21, 34)); + SkillUtils.TakeHealth(target.PlayerPawn.Value, heavyHit ? Instance.Random.Next(45, 55) : Instance.Random.Next(21, 34)); } private static void CreateLine(Vector start, Vector end, Color color) @@ -98,7 +156,7 @@ namespace jRandomSkills beam.AcceptInput("FollowEntity", beam, null!, ""); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#c9f8ff", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float maxDistance = 4096f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#c9f8ff", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float maxDistance = 4096f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float MaxDistance { get; set; } = maxDistance; } diff --git a/jRandomSkills - SRC Files/src/player/skills/LongZeus.cs b/jRandomSkills - SRC Files/src/player/skills/LongZeus.cs index 7c66009..c6fdcdd 100644 --- a/jRandomSkills - SRC Files/src/player/skills/LongZeus.cs +++ b/jRandomSkills - SRC Files/src/player/skills/LongZeus.cs @@ -16,11 +16,10 @@ namespace jRandomSkills public class LongZeus : ISkill { private const Skills skillName = Skills.LongZeus; - private static readonly float maxDistance = Config.GetValue(skillName, "maxDistance"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public unsafe static void WeaponFire(EventWeaponFire @event) @@ -38,7 +37,7 @@ namespace jRandomSkills if (activeWeapon == null || !activeWeapon.IsValid || activeWeapon.DesignerName != "weapon_taser") return; Vector eyePos = new(pawn.AbsOrigin.X, pawn.AbsOrigin.Y, pawn.AbsOrigin.Z + pawn.ViewOffset.Z); - Vector endPos = eyePos + SkillUtils.GetForwardVector(pawn.EyeAngles) * maxDistance; + Vector endPos = eyePos + SkillUtils.GetForwardVector(pawn.EyeAngles) * SkillsInfo.GetValue(skillName, "maxDistance"); Ray ray = new(Vector3.Zero); CTraceFilter filter = new(pawn.Index, pawn.Index) @@ -57,7 +56,7 @@ namespace jRandomSkills filter.m_nHierarchyIds[1] = 0; CGameTrace trace = TraceRay.TraceHull(eyePos, endPos, filter, ray); - if (Config.LoadedConfig.Settings.CS2TraceRayDebug) + if (Config.LoadedConfig.CS2TraceRayDebug) { CreateLine(eyePos, endPos, Color.FromArgb(255, 255, 255, 0)); CreateLine(new Vector(trace.StartPos.X, trace.StartPos.Y, trace.StartPos.Z), new Vector(trace.EndPos.X, trace.EndPos.Y, trace.EndPos.Z), Color.FromArgb(255, 255, 0, 0)); @@ -102,7 +101,7 @@ namespace jRandomSkills beam.AcceptInput("FollowEntity", beam, null!, ""); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#6effc7", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float maxDistance = 4096f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#6effc7", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float maxDistance = 4096f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float MaxDistance { get; set; } = maxDistance; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Magnifier.cs b/jRandomSkills - SRC Files/src/player/skills/Magnifier.cs index f3336b0..e3b939d 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Magnifier.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Magnifier.cs @@ -4,18 +4,18 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Magnifier : ISkill { private const Skills skillName = Skills.Magnifier; - private static readonly uint customFOV = Config.GetValue(skillName, "customFOV"); - private static readonly Dictionary playersFOV = []; + private static readonly ConcurrentDictionary playersFOV = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -45,7 +45,7 @@ namespace jRandomSkills if (playerInfo == null || playerInfo.Skill != skillName) continue; var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); - HashSet<(string, string)> menuItems = enemies.Select(e => (e.PlayerName, e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.UpdateMenu(player, menuItems); } } @@ -72,8 +72,8 @@ namespace jRandomSkills } if (!playersFOV.ContainsKey(enemy)) - playersFOV.Add(enemy, enemy.DesiredFOV); - enemy.DesiredFOV = customFOV; + playersFOV.TryAdd(enemy, enemy.DesiredFOV); + enemy.DesiredFOV = SkillsInfo.GetValue(skillName, "customFOV"); Utilities.SetStateChanged(enemy, "CBasePlayerController", "m_iDesiredFOV"); playerInfo.SkillChance = 1; @@ -90,7 +90,7 @@ namespace jRandomSkills var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); if (enemies.Length > 0) { - HashSet<(string, string)> menuItems = enemies.Select(e => (e.PlayerName, e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.CreateMenu(player, menuItems); } else @@ -104,11 +104,11 @@ namespace jRandomSkills player.DesiredFOV = fov; Utilities.SetStateChanged(player, "CBasePlayerController", "m_iDesiredFOV"); } - playersFOV.Remove(player); + playersFOV.TryRemove(player, out _); SkillUtils.CloseMenu(player); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#9ba882", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, uint customFOV = 50) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#9ba882", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, uint customFOV = 50) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public uint CustomFOV { get; set; } = customFOV; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Medic.cs b/jRandomSkills - SRC Files/src/player/skills/Medic.cs index 2708ac2..502912b 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Medic.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Medic.cs @@ -4,29 +4,30 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Medic : ISkill { private const Skills skillName = Skills.Medic; - private static readonly int healthToAdd = Config.GetValue(skillName, "healthToAdd"); - private static readonly int healthShotLimit = Config.GetValue(skillName, "healthShotLimit"); - private static readonly float timerCooldown = Config.GetValue(skillName, "cooldown"); - private static readonly Dictionary SkillPlayerInfo = []; + private static readonly ConcurrentDictionary SkillPlayerInfo = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - SkillPlayerInfo.Clear(); + lock (setLock) + SkillPlayerInfo.Clear(); } public static void OnTick() { + if (SkillUtils.IsFreezeTime()) return; foreach (var player in Utilities.GetPlayers()) { var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); @@ -38,18 +39,19 @@ namespace jRandomSkills public static void EnableSkill(CCSPlayerController player) { - SkillPlayerInfo[player.SteamID] = new PlayerSkillInfo + SkillPlayerInfo.TryAdd(player.SteamID, new PlayerSkillInfo { SteamID = player.SteamID, CanUse = true, Cooldown = DateTime.MinValue, - Count = healthShotLimit, - }; + Count = SkillsInfo.GetValue(skillName, "healthShotLimit"), + }); } public static void DisableSkill(CCSPlayerController player) { - SkillPlayerInfo.Remove(player.SteamID); + SkillPlayerInfo.TryRemove(player.SteamID, out _); + SkillUtils.ResetPrintHTML(player); } private static void UpdateHUD(CCSPlayerController player, PlayerSkillInfo skillInfo) @@ -57,7 +59,7 @@ namespace jRandomSkills float cooldown = 0; if (skillInfo != null) { - float time = (int)(skillInfo.Cooldown.AddSeconds(timerCooldown) - DateTime.Now).TotalSeconds; + float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue(skillName, "cooldown")) - DateTime.Now).TotalSeconds); cooldown = Math.Max(time, 0); if (cooldown == 0 && skillInfo?.CanUse == false) @@ -67,14 +69,14 @@ namespace jRandomSkills var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); if (skillData == null) return; - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = cooldown != 0 - ? $"{player.GetTranslation("hud_info", $"{cooldown}")}
" - : $"{(skillInfo == null ? 0 : skillInfo.Count)}/{healthShotLimit}
"; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + string remainingLine = cooldown != 0 + ? $"{player.GetTranslation("hud_info", $"{cooldown}")}" + : $"{(skillInfo == null ? 0 : skillInfo.Count)}/{SkillsInfo.GetValue(skillName, "healthShotLimit")}"; + + playerInfo.PrintHTML = remainingLine; } public static void UseSkill(CCSPlayerController player) @@ -90,7 +92,7 @@ namespace jRandomSkills skillInfo.CanUse = false; skillInfo.Cooldown = DateTime.Now; skillInfo.Count -= 1; - SkillUtils.AddHealth(playerPawn, healthToAdd); + SkillUtils.AddHealth(playerPawn, SkillsInfo.GetValue(skillName, "healthToAdd")); player.EmitSound("Healthshot.Success"); } } @@ -104,7 +106,7 @@ namespace jRandomSkills public DateTime Cooldown { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#10c212", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, int healthToAdd = 50, int healthShotLimit = 3, float cooldown = 1f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#10c212", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, int healthToAdd = 50, int healthShotLimit = 3, float cooldown = 1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int HealthToAdd { get; set; } = healthToAdd; public int HealthShotLimit { get; set; } = healthShotLimit; diff --git a/jRandomSkills - SRC Files/src/player/skills/MoneySwap.cs b/jRandomSkills - SRC Files/src/player/skills/MoneySwap.cs index 90962ba..98b7559 100644 --- a/jRandomSkills - SRC Files/src/player/skills/MoneySwap.cs +++ b/jRandomSkills - SRC Files/src/player/skills/MoneySwap.cs @@ -4,6 +4,7 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { @@ -13,7 +14,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -33,7 +34,7 @@ namespace jRandomSkills if (playerInfo == null || playerInfo.Skill != skillName) continue; var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); - HashSet<(string, string)> menuItems = enemies.Select(e => ($"{e.PlayerName} : {(e.InGameMoneyServices == null ? 0 : e.InGameMoneyServices.Account)}$", e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => ($"{e.PlayerName} : {(e.InGameMoneyServices == null ? 0 : e.InGameMoneyServices.Account)}$", e.Index.ToString()))); SkillUtils.UpdateMenu(player, menuItems); } } @@ -74,7 +75,7 @@ namespace jRandomSkills var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); if (enemies.Length > 0) { - HashSet<(string, string)> menuItems = enemies.Select(e => ($"{e.PlayerName} : {(e.InGameMoneyServices == null ? 0 : e.InGameMoneyServices.Account)}$", e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => ($"{e.PlayerName} : {(e.InGameMoneyServices == null ? 0 : e.InGameMoneyServices.Account)}$", e.Index.ToString()))); SkillUtils.CreateMenu(player, menuItems); } else @@ -102,7 +103,7 @@ namespace jRandomSkills SkillUtils.CloseMenu(player); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#52f54c", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#52f54c", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Muhammed.cs b/jRandomSkills - SRC Files/src/player/skills/Muhammed.cs index 1df3178..89ce837 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Muhammed.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Muhammed.cs @@ -1,23 +1,21 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; -using CounterStrikeSharp.API.Modules.Entities; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; -using Microsoft.Extensions.Logging; using static jRandomSkills.jRandomSkills; +using Vector = CounterStrikeSharp.API.Modules.Utils.Vector; namespace jRandomSkills { public class Muhammed : ISkill { private const Skills skillName = Skills.Muhammed; - private static readonly float explosionRadius = Config.GetValue(skillName, "explosionRadius"); - private static readonly int explosionDamage = Config.GetValue(skillName, "explosionDamage"); + private static readonly QAngle angle = new(10, -5, 9); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerDeath(EventPlayerDeath @event) @@ -32,39 +30,50 @@ namespace jRandomSkills private static void SpawnExplosion(CCSPlayerController player) { - var heProjectile = Utilities.CreateEntityByName("hegrenade_projectile"); - if (heProjectile == null || !heProjectile.IsValid) return; - var pawn = player.PlayerPawn.Value; if (pawn == null || !pawn.IsValid || pawn.AbsOrigin == null) return; Vector pos = pawn.AbsOrigin; pos.Z += 10; - heProjectile.TicksAtZeroVelocity = 100; - heProjectile.TeamNum = player.TeamNum; - heProjectile.Damage = explosionDamage; - heProjectile.DmgRadius = (int)explosionRadius; - heProjectile.Teleport(pos, null, new Vector(0, 0, -10)); - heProjectile.DispatchSpawn(); - heProjectile.AcceptInput("InitializeSpawnFromWorld", player.PlayerPawn.Value, player.PlayerPawn.Value, ""); - heProjectile.DetonateTime = 0; - Server.PrintToChatAll($" {ChatColors.DarkRed}► {ChatColors.Green}[{ChatColors.DarkRed} jRandomSkills {ChatColors.Green}] {ChatColors.DarkRed}{player.PlayerName}: {ChatColors.Lime}ALLAHU AKBAR!!!"); + SkillUtils.CreateHEGrenadeProjectile(pos, angle, new Vector(0, 0, -10), player.TeamNum); + + foreach (var _p in Utilities.GetPlayers().Where(p => p.IsValid && p.Team is CsTeam.CounterTerrorist or CsTeam.Terrorist && !p.IsBot)) + SkillUtils.PrintToChat(_p, $"{ChatColors.DarkRed}{player.PlayerName}: {ChatColors.Lime}{_p.GetTranslation("muhammed_explosion")}", false); var fileNames = new[] { "radiobotfallback01", "radiobotfallback02", "radiobotfallback04" }; - Instance.AddTimer(0.1f, () => + var randomFile = fileNames[new Random().Next(fileNames.Length)]; + player.ExecuteClientCommand($"play vo/agents/balkan/{randomFile}.vsnd"); + } + + public static void OnEntitySpawned(CEntityInstance entity) + { + if (entity.DesignerName != "hegrenade_projectile") return; + + var heProjectile = entity.As(); + if (heProjectile == null || !heProjectile.IsValid || heProjectile.AbsRotation == null) return; + + Server.NextFrame(() => { - var randomFile = fileNames[new Random().Next(fileNames.Length)]; - player.ExecuteClientCommand($"play vo/agents/balkan/{randomFile}.vsnd"); + if (heProjectile == null || !heProjectile.IsValid) return; + if (!(NearlyEquals(angle.X, heProjectile.AbsRotation.X) && NearlyEquals(angle.Y, heProjectile.AbsRotation.Y) && NearlyEquals(angle.Z, heProjectile.AbsRotation.Z))) + return; + + heProjectile.TicksAtZeroVelocity = 100; + heProjectile.Damage = SkillsInfo.GetValue(skillName, "explosionDamage"); + heProjectile.DmgRadius = SkillsInfo.GetValue(skillName, "explosionRadius"); + heProjectile.DetonateTime = 0; }); } + private static bool NearlyEquals(float a, float b, float epsilon = 0.001f) => Math.Abs(a - b) < epsilon; + private static bool IsDeadPlayerValid(CCSPlayerController? player) { return player != null && player.IsValid && player.PlayerPawn?.Value != null; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#F5CB42", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float explosionRadius = 500.0f, int explosionDamage = 999) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#F5CB42", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float explosionRadius = 500.0f, int explosionDamage = 999) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float ExplosionRadius { get; set; } = explosionRadius; public int ExplosionDamage { get; set; } = explosionDamage; diff --git a/jRandomSkills - SRC Files/src/player/skills/Ninja.cs b/jRandomSkills - SRC Files/src/player/skills/Ninja.cs index e10b33d..b5f9f89 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Ninja.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Ninja.cs @@ -5,26 +5,26 @@ using CounterStrikeSharp.API.Core.Attributes; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Ninja : ISkill { private const Skills skillName = Skills.Ninja; - private static readonly float idlePercentInvisibility = Config.GetValue(skillName, "idlePercentInvisibility"); - private static readonly float duckPercentInvisibility = Config.GetValue(skillName, "duckPercentInvisibility"); - private static readonly float knifePercentInvisibility = Config.GetValue(skillName, "knifePercentInvisibility"); - private static readonly Dictionary invisibilityChanged = []; - private static readonly Dictionary> invisibleEntities = []; + private static readonly ConcurrentDictionary invisibilityChanged = []; + private static readonly ConcurrentDictionary> invisibleEntities = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - invisibilityChanged.Clear(); + lock (setLock) + invisibilityChanged.Clear(); } public static void WeaponEquip(EventItemEquip @event) @@ -66,6 +66,9 @@ namespace jRandomSkills var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo?.Skill == skillName) UpdateNinja(player); + if (!player.PawnIsAlive) + if (invisibleEntities.ContainsKey(player.SteamID)) + invisibleEntities.TryRemove(player.SteamID, out _); } } @@ -78,7 +81,7 @@ namespace jRandomSkills { SetPlayerVisibility(player, 0); SetWeaponVisibility(player, 0); - invisibleEntities.Remove(player.SteamID); + invisibleEntities.TryRemove(player.SteamID, out _); } private static void UpdateNinja(CCSPlayerController? player) @@ -97,18 +100,18 @@ namespace jRandomSkills float percentInvisibility = 0; if (buttons.HasFlag(PlayerButtons.Duck)) - percentInvisibility += duckPercentInvisibility; + percentInvisibility += SkillsInfo.GetValue(skillName, "duckPercentInvisibility"); if (activeWeapon != null && activeWeapon.DesignerName == "weapon_knife") - percentInvisibility += knifePercentInvisibility; + percentInvisibility += SkillsInfo.GetValue(skillName, "knifePercentInvisibility"); if (!buttons.HasFlag(PlayerButtons.Moveleft) && !buttons.HasFlag(PlayerButtons.Moveright) && !buttons.HasFlag(PlayerButtons.Forward) && !buttons.HasFlag(PlayerButtons.Back) && flags.HasFlag(PlayerFlags.FL_ONGROUND)) - percentInvisibility += idlePercentInvisibility; + percentInvisibility += SkillsInfo.GetValue(skillName, "idlePercentInvisibility"); SetWeaponVisibility(player, percentInvisibility); if (invisibilityChanged.TryGetValue(player.Handle, out float oldInvisibility)) if (percentInvisibility == oldInvisibility) return; - invisibilityChanged[player.Handle] = percentInvisibility; + invisibilityChanged.TryAdd(player.Handle, percentInvisibility); SetPlayerVisibility(player, percentInvisibility); } @@ -131,7 +134,7 @@ namespace jRandomSkills var color = Color.FromArgb(Math.Max(255 - (int)(255 * percentInvisibility * 2), 0), 255, 255, 255); - invisibleEntities.Remove(player.SteamID); + invisibleEntities.TryRemove(player.SteamID, out _); if (color.A != 0) return; foreach (var weapon in playerPawn.WeaponServices.MyWeapons) @@ -144,12 +147,12 @@ namespace jRandomSkills items.Add(weapon.Index); } else - invisibleEntities.Add(player.SteamID, [weapon.Index]); + invisibleEntities.TryAdd(player.SteamID, [weapon.Index]); } } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#dedede", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float idlePercentInvisibility = .3f, float duckPercentInvisibility = .3f, float knifePercentInvisibility = .3f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#dedede", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float idlePercentInvisibility = .3f, float duckPercentInvisibility = .3f, float knifePercentInvisibility = .3f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float IdlePercentInvisibility { get; set; } = idlePercentInvisibility; public float DuckPercentInvisibility { get; set; } = duckPercentInvisibility; diff --git a/jRandomSkills - SRC Files/src/player/skills/NoNades.cs b/jRandomSkills - SRC Files/src/player/skills/NoNades.cs index 98a56ba..5d47669 100644 --- a/jRandomSkills - SRC Files/src/player/skills/NoNades.cs +++ b/jRandomSkills - SRC Files/src/player/skills/NoNades.cs @@ -11,7 +11,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerHurt(EventPlayerHurt @event) @@ -28,7 +28,7 @@ namespace jRandomSkills SkillUtils.AddHealth(player!.PlayerPawn.Value, damage); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a38c1a", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a38c1a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/NoRecoil.cs b/jRandomSkills - SRC Files/src/player/skills/NoRecoil.cs index f7520c0..644f686 100644 --- a/jRandomSkills - SRC Files/src/player/skills/NoRecoil.cs +++ b/jRandomSkills - SRC Files/src/player/skills/NoRecoil.cs @@ -12,7 +12,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -49,7 +49,7 @@ namespace jRandomSkills } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8a42f5", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8a42f5", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Noclip.cs b/jRandomSkills - SRC Files/src/player/skills/Noclip.cs index 4aa3a15..9655772 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Noclip.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Noclip.cs @@ -4,24 +4,25 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Noclip : ISkill { private const Skills skillName = Skills.Noclip; - private static readonly float timerCooldown = Config.GetValue(skillName, "cooldown"); - private static readonly float duration = Config.GetValue(skillName, "duration"); - private static readonly Dictionary SkillPlayerInfo = []; + private static readonly ConcurrentDictionary SkillPlayerInfo = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - SkillPlayerInfo.Clear(); + lock (setLock) + SkillPlayerInfo.Clear(); } public static void OnTick() @@ -37,17 +38,20 @@ namespace jRandomSkills public static void EnableSkill(CCSPlayerController player) { - SkillPlayerInfo[player.SteamID] = new PlayerSkillInfo + SkillPlayerInfo.TryAdd(player.SteamID, new PlayerSkillInfo { SteamID = player.SteamID, CanUse = true, + IsFlying = false, Cooldown = DateTime.MinValue, - }; + LastPosition = null, + }); } public static void DisableSkill(CCSPlayerController player) { - SkillPlayerInfo.Remove(player.SteamID); + SkillPlayerInfo.TryRemove(player.SteamID, out _); + SkillUtils.ResetPrintHTML(player); } private static void UpdateHUD(CCSPlayerController player, PlayerSkillInfo skillInfo) @@ -56,35 +60,35 @@ namespace jRandomSkills float flying = 0; if (skillInfo != null) { - float time = (int)(skillInfo.Cooldown.AddSeconds(timerCooldown) - DateTime.Now).TotalSeconds; + float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue(skillName, "cooldown")) - DateTime.Now).TotalSeconds); cooldown = Math.Max(time, 0); - float flyingTime = (int)(skillInfo.Cooldown.AddSeconds(duration) - DateTime.Now).TotalMilliseconds; + float flyingTime = (int)(skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue(skillName, "duration")) - DateTime.Now).TotalMilliseconds; flying = Math.Max(flyingTime, 0); if (cooldown == 0 && skillInfo?.CanUse == false) skillInfo.CanUse = true; } - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = cooldown != 0 - ? ( - flying != 0 - ? $"{player.GetTranslation("active_hud_info", $"{Math.Round(flying / 100, 2)}")}
" - : $"{player.GetTranslation("hud_info", $"{cooldown}")}
" - ) : ""; + if (cooldown == 0) + { + playerInfo.PrintHTML = null; + return; + } - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + playerInfo.PrintHTML = + skillInfo?.IsFlying == true + ? $"{player.GetTranslation("active_hud_info", $"{Math.Round(flying / 100, 2)}")}" + : $"{player.GetTranslation("hud_info", $"{cooldown}")}"; } public static void UseSkill(CCSPlayerController player) { var playerPawn = player.PlayerPawn.Value; + var duration = SkillsInfo.GetValue(skillName, "duration"); if (playerPawn?.CBodyComponent == null) return; if (SkillPlayerInfo.TryGetValue(player.SteamID, out var skillInfo)) @@ -93,10 +97,39 @@ namespace jRandomSkills if (skillInfo.CanUse) { skillInfo.CanUse = false; + skillInfo.IsFlying = true; skillInfo.Cooldown = DateTime.Now; + skillInfo.LastPosition = playerPawn.AbsOrigin == null ? null : new Vector(playerPawn.AbsOrigin.X, playerPawn.AbsOrigin.Y, playerPawn.AbsOrigin.Z); playerPawn.ActualMoveType = MoveType_t.MOVETYPE_NOCLIP; - Instance.AddTimer(duration, () => playerPawn.ActualMoveType = MoveType_t.MOVETYPE_WALK); + Instance.AddTimer(duration, () => { + if (playerPawn == null || !playerPawn.IsValid || !skillInfo.IsFlying) return; + skillInfo.IsFlying = false; + playerPawn.ActualMoveType = MoveType_t.MOVETYPE_WALK; + }); + + Instance.AddTimer(duration + 4, () => { + if (playerPawn == null || !playerPawn.IsValid || !player.PawnIsAlive || skillInfo.IsFlying) return; + if (skillInfo.LastPosition == null || playerPawn.AbsOrigin == null) return; + skillInfo.IsFlying = false; + var diff = Math.Abs(playerPawn.AbsOrigin.Z - skillInfo.LastPosition.Z); + if (diff > 3000 && playerPawn.AbsOrigin.Z < skillInfo.LastPosition.Z) + playerPawn.Teleport(skillInfo.LastPosition, null, new Vector(0,0,0)); + }); + } + else if (skillInfo.IsFlying) + { + skillInfo.IsFlying = false; + playerPawn.ActualMoveType = MoveType_t.MOVETYPE_WALK; + + Instance.AddTimer(4, () => { + if (playerPawn == null || !playerPawn.IsValid || !player.PawnIsAlive || skillInfo.IsFlying) return; + if (skillInfo.LastPosition == null || playerPawn.AbsOrigin == null) return; + skillInfo.IsFlying = false; + var diff = Math.Abs(playerPawn.AbsOrigin.Z - skillInfo.LastPosition.Z); + if (diff > 3000 && playerPawn.AbsOrigin.Z < skillInfo.LastPosition.Z) + playerPawn.Teleport(skillInfo.LastPosition, null, new Vector(0, 0, 0)); + }); } } } @@ -105,10 +138,12 @@ namespace jRandomSkills { public ulong SteamID { get; set; } public bool CanUse { get; set; } + public bool IsFlying { get; set; } public DateTime Cooldown { get; set; } + public Vector? LastPosition { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#44ebd4", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float cooldown = 30f, float duration = 2f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#44ebd4", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, float cooldown = 30f, float duration = 2f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float Cooldown { get; set; } = cooldown; public float Duration { get; set; } = duration; diff --git a/jRandomSkills - SRC Files/src/player/skills/None.cs b/jRandomSkills - SRC Files/src/player/skills/None.cs index 16995fe..d77ddbe 100644 --- a/jRandomSkills - SRC Files/src/player/skills/None.cs +++ b/jRandomSkills - SRC Files/src/player/skills/None.cs @@ -9,10 +9,10 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FFFFFF", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FFFFFF", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/OneShot.cs b/jRandomSkills - SRC Files/src/player/skills/OneShot.cs index 5c57689..dd57557 100644 --- a/jRandomSkills - SRC Files/src/player/skills/OneShot.cs +++ b/jRandomSkills - SRC Files/src/player/skills/OneShot.cs @@ -12,7 +12,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void OnTakeDamage(DynamicHook h) @@ -42,7 +42,7 @@ namespace jRandomSkills param2.Damage = 1000f; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff5CD9", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff5CD9", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/OnlyHead.cs b/jRandomSkills - SRC Files/src/player/skills/OnlyHead.cs index 7009f5b..4b81485 100644 --- a/jRandomSkills - SRC Files/src/player/skills/OnlyHead.cs +++ b/jRandomSkills - SRC Files/src/player/skills/OnlyHead.cs @@ -12,7 +12,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerHurt(EventPlayerHurt @event) @@ -44,7 +44,7 @@ namespace jRandomSkills Utilities.SetStateChanged(playerPawn, "CBaseEntity", "m_iHealth"); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#3c47de", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#3c47de", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/PawelJumper.cs b/jRandomSkills - SRC Files/src/player/skills/PawelJumper.cs index 99c2613..b702514 100644 --- a/jRandomSkills - SRC Files/src/player/skills/PawelJumper.cs +++ b/jRandomSkills - SRC Files/src/player/skills/PawelJumper.cs @@ -10,16 +10,13 @@ namespace jRandomSkills public class PawelJumper : ISkill { private const Skills skillName = Skills.PawelJumper; - private static readonly int extraJumpsMin = Config.GetValue(skillName, "extraJumpsMin"); - private static readonly int extraJumpsMax = Config.GetValue(skillName, "extraJumpsMax"); - private static readonly PlayerFlags[] LF = new PlayerFlags[64]; private static readonly int?[] J = new int?[64]; private static readonly PlayerButtons[] LB = new PlayerButtons[64]; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); } public static void OnTick() @@ -39,12 +36,12 @@ namespace jRandomSkills var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerPawn == null || playerInfo == null) return; - var skillConfig = Config.LoadedConfig.SkillsInfo.FirstOrDefault(s => s.Name == skillName.ToString()); + var skillConfig = SkillsInfo.LoadedConfig.FirstOrDefault(s => s.Name == skillName.ToString()); if (skillConfig == null) return; - float extraJumps = (float)Instance.Random.Next(extraJumpsMin, extraJumpsMax + 1); + float extraJumps = (float)Instance.Random.Next(SkillsInfo.GetValue(skillName, "extraJumpsMin"), SkillsInfo.GetValue(skillName, "extraJumpsMax") + 1); playerInfo.SkillChance = extraJumps; - SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetTranslation("paweljumper")}{ChatColors.Lime}: " + player.GetTranslation("paweljumper_desc2", extraJumps), false); + SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(skillName)}{ChatColors.Lime}: {player.GetSkillDescription(skillName, extraJumps)}", false); } private static void GiveAdditionalJump(CCSPlayerController player) @@ -76,7 +73,7 @@ namespace jRandomSkills LB[player.Slot] = buttons; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FFA500", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, int extraJumpsMin = 1, int extraJumpsMax = 4) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#FFA500", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, int extraJumpsMin = 1, int extraJumpsMax = 4) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int ExtraJumpsMin { get; set; } = extraJumpsMin; public int ExtraJumpsMax { get; set; } = extraJumpsMax; diff --git a/jRandomSkills - SRC Files/src/player/skills/Phoenix.cs b/jRandomSkills - SRC Files/src/player/skills/Phoenix.cs index ecb88b5..3e443be 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Phoenix.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Phoenix.cs @@ -9,10 +9,11 @@ namespace jRandomSkills public class Phoenix : ISkill { private const Skills skillName = Skills.Phoenix; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); } public static void PlayerDeath(EventPlayerDeath @event) @@ -25,9 +26,12 @@ namespace jRandomSkills { if (Instance.Random.NextDouble() <= playerInfo.SkillChance) { - player.Respawn(); - Instance.AddTimer(.2f, () => player.Respawn()); - SkillUtils.PrintToChat(player, player.GetTranslation("phoenix_respawn"), false); ; + lock (setLock) + { + player.Respawn(); + Instance.AddTimer(.2f, () => player.Respawn()); + SkillUtils.PrintToChat(player, player.GetTranslation("phoenix_respawn"), false); + } } } } @@ -36,19 +40,14 @@ namespace jRandomSkills { var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo == null) return; - float newChance = (float)Instance.Random.NextDouble() * (Config.GetValue(skillName, "ChanceTo") - Config.GetValue(skillName, "ChanceFrom")) + Config.GetValue(skillName, "ChanceFrom"); + float newChance = (float)Instance.Random.NextDouble() * (SkillsInfo.GetValue(skillName, "ChanceTo") - SkillsInfo.GetValue(skillName, "ChanceFrom")) + SkillsInfo.GetValue(skillName, "ChanceFrom"); playerInfo.SkillChance = newChance; newChance = (float)Math.Round(newChance, 2) * 100; newChance = (float)Math.Round(newChance); - SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetTranslation("phoenix")}{ChatColors.Lime}: " + player.GetTranslation("phoenix_desc2", newChance), false); + SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(skillName)}{ChatColors.Lime}: {player.GetSkillDescription(skillName, newChance)}", false); } - private static bool IsDeadPlayerValid(CCSPlayerController player) - { - return player != null && player.IsValid && player.PlayerPawn?.Value != null; - } - - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff5C0A", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float chanceFrom = .2f, float chanceTo = .4f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff5C0A", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float chanceFrom = .2f, float chanceTo = .4f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float ChanceFrom { get; set; } = chanceFrom; public float ChanceTo { get; set; } = chanceTo; diff --git a/jRandomSkills - SRC Files/src/player/skills/Pilot.cs b/jRandomSkills - SRC Files/src/player/skills/Pilot.cs index ad8926b..f7633eb 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Pilot.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Pilot.cs @@ -4,20 +4,18 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Pilot : ISkill { private const Skills skillName = Skills.Pilot; - private static readonly float maximumFuel = Config.GetValue(skillName, "maximumFuel"); - private static readonly float fuelConsumption = Config.GetValue(skillName, "fuelConsumption"); - private static readonly float refuelling = Config.GetValue(skillName, "refuelling"); - private static readonly Dictionary PlayerPilotInfo = []; + private static readonly ConcurrentDictionary PlayerPilotInfo = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -37,24 +35,26 @@ namespace jRandomSkills public static void EnableSkill(CCSPlayerController player) { - PlayerPilotInfo[player.SteamID] = new Pilot_PlayerInfo + PlayerPilotInfo.TryAdd(player.SteamID, new Pilot_PlayerInfo { SteamID = player.SteamID, - Fuel = maximumFuel, - }; + Fuel = SkillsInfo.GetValue(skillName, "maximumFuel"), + }); } public static void DisableSkill(CCSPlayerController player) { - PlayerPilotInfo.Remove(player.SteamID); + PlayerPilotInfo.TryRemove(player.SteamID, out _); + SkillUtils.ResetPrintHTML(player); } private static void HandlePilot(CCSPlayerController player) { var buttons = player.Buttons; + var maximumFuel = SkillsInfo.GetValue(skillName, "maximumFuel"); if (PlayerPilotInfo.TryGetValue(player.SteamID, out var pilotInfo)) { - pilotInfo.Fuel = Math.Min(Math.Max(0, pilotInfo.Fuel - (buttons.HasFlag(PlayerButtons.Use) ? fuelConsumption : -refuelling)), maximumFuel); + pilotInfo.Fuel = Math.Min(Math.Max(0, pilotInfo.Fuel - (buttons.HasFlag(PlayerButtons.Use) ? SkillsInfo.GetValue(skillName, "fuelConsumption") : -SkillsInfo.GetValue(skillName, "refuelling"))), maximumFuel); if (buttons.HasFlag(PlayerButtons.Use)) if (pilotInfo.Fuel > 0 && player.PlayerPawn.Value != null && player.PlayerPawn.Value.IsValid && !player.PlayerPawn.Value.IsDefusing) ApplyPilotEffect(player); @@ -64,23 +64,27 @@ namespace jRandomSkills private static void UpdateHUD(CCSPlayerController player, Pilot_PlayerInfo pilotInfo) { + if (pilotInfo == null) return; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; + + var maximumFuel = SkillsInfo.GetValue(skillName, "maximumFuel"); + if (pilotInfo.Fuel == maximumFuel && playerInfo.SkillDescriptionHudExpired >= DateTime.Now) + { + playerInfo.PrintHTML = null; + return; + } + var buttons = player.Buttons; float fuelPercentage = maximumFuel; - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; - string fuelColor = GetFuelColor(pilotInfo.Fuel); - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = $"{player.GetTranslation("pilot_hud_info")}: {(pilotInfo.Fuel/maximumFuel)*100:F0}%
"; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + playerInfo.PrintHTML = $"{player.GetTranslation("pilot_hud_info")}: {(pilotInfo.Fuel/maximumFuel)*100:F0}%"; } private static string GetFuelColor(float fuelPercentage) { + var maximumFuel = SkillsInfo.GetValue(skillName, "maximumFuel"); if (fuelPercentage > (maximumFuel/2f)) return "#00FF00"; if (fuelPercentage > (maximumFuel/4f)) return "#FFFF00"; return "#FF0000"; @@ -120,7 +124,7 @@ namespace jRandomSkills public float Fuel { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1466F5", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float maximumFuel = 150f, float fuelConsumption = .64f, float refuelling = .1f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1466F5", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, float maximumFuel = 150f, float fuelConsumption = .64f, float refuelling = .1f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float MaximumFuel { get; set; } = maximumFuel; public float FuelConsumption { get; set; } = fuelConsumption; diff --git a/jRandomSkills - SRC Files/src/player/skills/Planter.cs b/jRandomSkills - SRC Files/src/player/skills/Planter.cs index 5fffe7b..b9133b8 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Planter.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Planter.cs @@ -10,11 +10,10 @@ namespace jRandomSkills public class Planter : ISkill { private const Skills skillName = Skills.Planter; - private static readonly int extraC4BlowTime = Config.GetValue(skillName, "extraC4BlowTime"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void BombPlanted(EventBombPlanted @event) @@ -27,7 +26,7 @@ namespace jRandomSkills var plantedBomb = Utilities.FindAllEntitiesByDesignerName("planted_c4").FirstOrDefault(); if (plantedBomb != null) - Server.NextFrame(() => plantedBomb.C4Blow = (float)Server.EngineTime + extraC4BlowTime); + Server.NextFrame(() => plantedBomb.C4Blow = (float)Server.EngineTime + SkillsInfo.GetValue(skillName, "extraC4BlowTime")); } public static void DisableSkill(CCSPlayerController player) @@ -43,12 +42,12 @@ namespace jRandomSkills if (!Instance.IsPlayerValid(player)) continue; var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); - if (playerInfo?.Skill == skillName && Instance?.GameRules?.FreezePeriod == false) + if (playerInfo?.Skill == skillName) Schema.SetSchemaValue(player!.PlayerPawn.Value!.Handle, "CCSPlayerPawn", "m_bInBombZone", true); } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#7d7d7d", CsTeam onlyTeam = CsTeam.Terrorist, bool needsTeammates = false, int extraC4BlowTime = 60) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#7d7d7d", CsTeam onlyTeam = CsTeam.Terrorist, bool disableOnFreezeTime = true, bool needsTeammates = false, int extraC4BlowTime = 60) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int ExtraC4BlowTime { get; set; } = extraC4BlowTime; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Poison.cs b/jRandomSkills - SRC Files/src/player/skills/Poison.cs index e56f53c..308df12 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Poison.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Poison.cs @@ -4,35 +4,36 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Poison : ISkill { private const Skills skillName = Skills.Poison; - private static readonly float cooldown = Config.GetValue(skillName, "Cooldown"); - private static readonly int healthToDamage = Config.GetValue(skillName, "Damage"); - private static readonly HashSet poisonedPlayers = []; + private static readonly ConcurrentDictionary poisonedPlayers = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); } public static void NewRound() { - poisonedPlayers.Clear(); + lock (setLock) + poisonedPlayers.Clear(); } public static void OnTick() { - if (Server.TickCount % (int)(64 * cooldown) == 0) + if (Server.TickCount % (int)(64 * SkillsInfo.GetValue(skillName, "Cooldown")) == 0) { - foreach (var player in poisonedPlayers) + foreach (var player in poisonedPlayers.Keys) { var pawn = player.PlayerPawn.Value; if (pawn == null || !pawn.IsValid) continue; - SkillUtils.TakeHealth(pawn, healthToDamage); + SkillUtils.TakeHealth(pawn, SkillsInfo.GetValue(skillName, "Damage")); } } @@ -45,7 +46,7 @@ namespace jRandomSkills if (playerInfo == null || playerInfo.Skill != skillName) continue; var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); - HashSet<(string, string)> menuItems = enemies.Select(e => (e.PlayerName, e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.UpdateMenu(player, menuItems); } } @@ -71,7 +72,7 @@ namespace jRandomSkills return; } - poisonedPlayers.Add(enemy); + poisonedPlayers.TryAdd(enemy, 0); playerInfo.SkillChance = 1; player.PrintToChat($" {ChatColors.Green}" + player.GetTranslation("poison_player_info", enemy.PlayerName)); enemy.PrintToChat($" {ChatColors.Red}" + player.GetTranslation("poison_enemy_info")); @@ -86,7 +87,7 @@ namespace jRandomSkills var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); if (enemies.Length > 0) { - HashSet<(string, string)> menuItems = enemies.Select(e => (e.PlayerName, e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.CreateMenu(player, menuItems); } else @@ -95,11 +96,11 @@ namespace jRandomSkills public static void DisableSkill(CCSPlayerController player) { - poisonedPlayers.Remove(player); + poisonedPlayers.TryRemove(player, out _); SkillUtils.CloseMenu(player); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#902eff", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float cooldown = 1, int damage = 1) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#902eff", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, float cooldown = 1, int damage = 1) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int Damage { get; set; } = damage; public float Cooldown { get; set; } = cooldown; diff --git a/jRandomSkills - SRC Files/src/player/skills/PrimaryBan.cs b/jRandomSkills - SRC Files/src/player/skills/PrimaryBan.cs index b3c9cb4..e0c9c7c 100644 --- a/jRandomSkills - SRC Files/src/player/skills/PrimaryBan.cs +++ b/jRandomSkills - SRC Files/src/player/skills/PrimaryBan.cs @@ -4,13 +4,15 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class PrimaryBan : ISkill { private const Skills skillName = Skills.PrimaryBan; - private static readonly HashSet bannedPlayers = []; + private static readonly ConcurrentDictionary bannedPlayers = []; + private static readonly object setLock = new(); private static readonly string[] disabledWeapons = [ "ak47", "m4a1", "m4a4", "m4a1_silencer", "famas", "galilar", "aug", "sg553", "mp9", "mac10", "bizon", "mp7", "ump45", @@ -19,14 +21,17 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - bannedPlayers.Clear(); - foreach (var player in Utilities.GetPlayers()) - SkillUtils.CloseMenu(player); + lock (setLock) + { + bannedPlayers.Clear(); + foreach (var player in Utilities.GetPlayers()) + SkillUtils.CloseMenu(player); + } } public static void WeaponEquip(EventItemEquip @event) @@ -34,7 +39,7 @@ namespace jRandomSkills var player = @event.Userid; var weapon = @event.Item; if (player == null || !player.IsValid) return; - if (!bannedPlayers.Contains(player.SteamID) || !disabledWeapons.Contains(weapon)) return; + if (!bannedPlayers.ContainsKey(player.SteamID) || !disabledWeapons.Contains(weapon)) return; player.ExecuteClientCommand("slot3"); } @@ -49,7 +54,7 @@ namespace jRandomSkills if (playerInfo == null || playerInfo.Skill != skillName) continue; var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); - HashSet<(string, string)> menuItems = enemies.Select(e => (e.PlayerName, e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.UpdateMenu(player, menuItems); } } @@ -75,7 +80,7 @@ namespace jRandomSkills return; } - bannedPlayers.Add(enemy.SteamID); + bannedPlayers.TryAdd(enemy.SteamID, 0); CheckWeapon(enemy); playerInfo.SkillChance = 1; player.PrintToChat($" {ChatColors.Green}" + player.GetTranslation("primaryban_player_info", enemy.PlayerName)); @@ -88,7 +93,7 @@ namespace jRandomSkills if (activeWeapon == null || !activeWeapon.IsValid) return; if (activeWeapon.DesignerName == null || string.IsNullOrEmpty(activeWeapon.DesignerName)) return; - if (!bannedPlayers.Contains(player.SteamID) || !disabledWeapons.Contains(activeWeapon.DesignerName?.Replace("weapon_", ""))) return; + if (!bannedPlayers.ContainsKey(player.SteamID) || !disabledWeapons.Contains(activeWeapon.DesignerName?.Replace("weapon_", ""))) return; player.ExecuteClientCommand("slot3"); } @@ -101,7 +106,7 @@ namespace jRandomSkills var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); if (enemies.Length > 0) { - HashSet<(string, string)> menuItems = enemies.Select(e => (e.PlayerName, e.Index.ToString())).ToHashSet(); + ConcurrentBag<(string, string)> menuItems = new(enemies.Select(e => (e.PlayerName, e.Index.ToString()))); SkillUtils.CreateMenu(player, menuItems); } else @@ -110,11 +115,11 @@ namespace jRandomSkills public static void DisableSkill(CCSPlayerController player) { - bannedPlayers.Remove(player.SteamID); + bannedPlayers.TryRemove(player.SteamID, out _); SkillUtils.CloseMenu(player); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffc061", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ffc061", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Prosthesis.cs b/jRandomSkills - SRC Files/src/player/skills/Prosthesis.cs index 9f4a9c7..d20c3e7 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Prosthesis.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Prosthesis.cs @@ -12,7 +12,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerHurt(EventPlayerHurt @event) @@ -44,7 +44,7 @@ namespace jRandomSkills Utilities.SetStateChanged(playerPawn, "CBaseEntity", "m_iHealth"); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#9c9c9c", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#9c9c9c", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/PsychicDefusing.cs b/jRandomSkills - SRC Files/src/player/skills/PsychicDefusing.cs index e8a643b..1dbf0e2 100644 --- a/jRandomSkills - SRC Files/src/player/skills/PsychicDefusing.cs +++ b/jRandomSkills - SRC Files/src/player/skills/PsychicDefusing.cs @@ -4,27 +4,30 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class PsychicDefusing : ISkill { private const Skills skillName = Skills.PsychicDefusing; - private static readonly Dictionary SkillPlayerInfo = []; + private static readonly ConcurrentDictionary SkillPlayerInfo = []; private static Vector? bombLocation = null; - private static readonly float maxDefusingRange = Config.GetValue(skillName, "maxDefusingRange"); - private static readonly float defusingTime = Config.GetValue(skillName, "defusingTime"); private static readonly float tickRate = 64f; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - SkillPlayerInfo.Clear(); - bombLocation = null; + lock (setLock) + { + SkillPlayerInfo.Clear(); + bombLocation = null; + } } public static void PlayerDeath(EventPlayerDeath @event) @@ -37,7 +40,7 @@ namespace jRandomSkills var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo?.Skill == skillName) - SkillPlayerInfo.Remove(pawn); + SkillPlayerInfo.TryRemove(pawn, out _); } public static void BombPlanted(EventBombPlanted _) @@ -52,18 +55,25 @@ namespace jRandomSkills if (bombLocation == null) return; foreach (var skillInfo in SkillPlayerInfo) { - var player = skillInfo.Key; + var pawn = skillInfo.Key; var info = skillInfo.Value; - if (player.AbsOrigin == null || SkillUtils.GetDistance(player.AbsOrigin, bombLocation) > maxDefusingRange) + var playerController = pawn.Controller.Value; + if (playerController == null || !pawn.Controller.IsValid) return; + + var player = playerController.As(); + if (player == null || !player.IsValid) return; + + if (pawn.AbsOrigin == null || SkillUtils.GetDistance(pawn.AbsOrigin, bombLocation) > SkillsInfo.GetValue(skillName, "maxDefusingRange")) { info.Defusing = false; - info.DefusingTime = defusingTime; + info.DefusingTime = SkillsInfo.GetValue(skillName, "defusingTime"); + SkillUtils.ResetPrintHTML(player); continue; } if (!info.Defusing) - player.EmitSound("c4.disarmstart"); + pawn.EmitSound("c4.disarmstart"); info.Defusing = true; info.DefusingTime -= (1f / tickRate); @@ -75,13 +85,11 @@ namespace jRandomSkills plantedBomb.AcceptInput("Kill"); SkillUtils.TerminateRound(CsTeam.CounterTerrorist); } - + SkillUtils.ResetPrintHTML(player); SkillPlayerInfo.Clear(); } - var playerController = player.Controller.Value; - if (playerController == null || !player.Controller.IsValid) return; - UpdateHUD(playerController.As(), info); + UpdateHUD(player, info); } } @@ -89,35 +97,34 @@ namespace jRandomSkills { var pawn = player.PlayerPawn.Value; if (pawn == null || !pawn.IsValid) return; - SkillPlayerInfo[pawn] = new PlayerSkillInfo + SkillPlayerInfo.TryAdd(pawn, new PlayerSkillInfo { SteamID = player.SteamID, Defusing = false, - DefusingTime = defusingTime, - }; + DefusingTime = SkillsInfo.GetValue(skillName, "defusingTime"), + }); } public static void DisableSkill(CCSPlayerController player) { + SkillUtils.ResetPrintHTML(player); var pawn = player.PlayerPawn.Value; if (pawn == null || !pawn.IsValid) return; - SkillPlayerInfo.Remove(pawn); + SkillPlayerInfo.TryRemove(pawn, out _); } private static void UpdateHUD(CCSPlayerController player, PlayerSkillInfo skillInfo) { if (!skillInfo.Defusing) return; - int cooldown = (int)skillInfo.DefusingTime + 1; + int cooldown = (int)Math.Ceiling(skillInfo.DefusingTime); - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = cooldown != 0 ? $"{player.GetTranslation("psychicdefusing_hud_info", $"{cooldown}")}
" : ""; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + if (cooldown == 0) + playerInfo.PrintHTML = null; + else + playerInfo.PrintHTML = $"{player.GetTranslation("psychicdefusing_hud_info", $"{cooldown}")}"; } public class PlayerSkillInfo { @@ -126,7 +133,7 @@ namespace jRandomSkills public float DefusingTime { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#507529", CsTeam onlyTeam = CsTeam.CounterTerrorist, bool needsTeammates = false, float maxDefusingRange = 80f, float defusingTime = 10f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#507529", CsTeam onlyTeam = CsTeam.CounterTerrorist, bool disableOnFreezeTime = false, bool needsTeammates = false, float maxDefusingRange = 80f, float defusingTime = 10f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float MaxDefusingRange { get; set; } = maxDefusingRange; public float DefusingTime { get; set; } = defusingTime; diff --git a/jRandomSkills - SRC Files/src/player/skills/Push.cs b/jRandomSkills - SRC Files/src/player/skills/Push.cs index ea02564..440b205 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Push.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Push.cs @@ -9,12 +9,10 @@ namespace jRandomSkills public class Push : ISkill { private const Skills skillName = Skills.Push; - private static readonly float jumpVelocity = Config.GetValue(skillName, "jumpVelocity"); - private static readonly float pushVelocity = Config.GetValue(skillName, "pushVelocity"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); } public static void PlayerHurt(EventPlayerHurt @event) @@ -38,11 +36,11 @@ namespace jRandomSkills { var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo == null) return; - float newChance = (float)Instance.Random.NextDouble() * (Config.GetValue(skillName, "ChanceTo") - Config.GetValue(skillName, "ChanceFrom")) + Config.GetValue(skillName, "ChanceFrom"); + float newChance = (float)Instance.Random.NextDouble() * (SkillsInfo.GetValue(skillName, "ChanceTo") - SkillsInfo.GetValue(skillName, "ChanceFrom")) + SkillsInfo.GetValue(skillName, "ChanceFrom"); playerInfo.SkillChance = newChance; newChance = (float)Math.Round(newChance, 2) * 100; newChance = (float)Math.Round(newChance); - SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetTranslation("push")}{ChatColors.Lime}: " + player.GetTranslation("push_desc2", newChance), false); + SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(skillName)}{ChatColors.Lime}: {player.GetSkillDescription(skillName, newChance)}", false); } private static void PushEnemy(CCSPlayerController player, QAngle attackerAngle) @@ -53,13 +51,13 @@ namespace jRandomSkills var currentPosition = player.PlayerPawn.Value.AbsOrigin; var currentAngles = player.PlayerPawn.Value.EyeAngles; - Vector newVelocity = SkillUtils.GetForwardVector(attackerAngle) * pushVelocity; - newVelocity.Z = player.PlayerPawn.Value.AbsVelocity.Z + jumpVelocity; + Vector newVelocity = SkillUtils.GetForwardVector(attackerAngle) * SkillsInfo.GetValue(skillName, "pushVelocity"); + newVelocity.Z = player.PlayerPawn.Value.AbsVelocity.Z + SkillsInfo.GetValue(skillName, "jumpVelocity"); player.PlayerPawn.Value.Teleport(currentPosition, currentAngles, newVelocity); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1e9ab0", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float chanceFrom = 1f, float chanceTo = 1f, float jumpVelocity = 300f, float pushVelocity = 400f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1e9ab0", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float chanceFrom = 1f, float chanceTo = 1f, float jumpVelocity = 300f, float pushVelocity = 400f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float ChanceFrom { get; set; } = chanceFrom; public float ChanceTo { get; set; } = chanceTo; diff --git a/jRandomSkills - SRC Files/src/player/skills/Pyro.cs b/jRandomSkills - SRC Files/src/player/skills/Pyro.cs index 0c54dd0..0f28b55 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Pyro.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Pyro.cs @@ -10,11 +10,10 @@ namespace jRandomSkills public class Pyro : ISkill { private const Skills skillName = Skills.Pyro; - private static readonly float regenerationMultiplier = Config.GetValue(skillName, "regenerationMultiplier"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerHurt(EventPlayerHurt @event) @@ -27,7 +26,7 @@ namespace jRandomSkills var victimInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == victim?.SteamID); if (victimInfo == null || victimInfo.Skill != skillName) return ; - RestoreHealth(victim!, damage * regenerationMultiplier); + RestoreHealth(victim!, damage * SkillsInfo.GetValue(skillName, "regenerationMultiplier")); } public static void EnableSkill(CCSPlayerController player) @@ -48,7 +47,7 @@ namespace jRandomSkills Utilities.SetStateChanged(playerPawn, "CBaseEntity", "m_iHealth"); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#3c47de", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float regenerationMultiplier = 1.5f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#3c47de", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float regenerationMultiplier = 1.5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float RegenerationMultiplier { get; set; } = regenerationMultiplier; } diff --git a/jRandomSkills - SRC Files/src/player/skills/QuickShot.cs b/jRandomSkills - SRC Files/src/player/skills/QuickShot.cs index dbc44d7..d2aa2d0 100644 --- a/jRandomSkills - SRC Files/src/player/skills/QuickShot.cs +++ b/jRandomSkills - SRC Files/src/player/skills/QuickShot.cs @@ -12,7 +12,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void OnTick() @@ -42,7 +42,7 @@ namespace jRandomSkills } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8a42f5", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8a42f5", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/RadarHack.cs b/jRandomSkills - SRC Files/src/player/skills/RadarHack.cs index 954d159..c0d26b7 100644 --- a/jRandomSkills - SRC Files/src/player/skills/RadarHack.cs +++ b/jRandomSkills - SRC Files/src/player/skills/RadarHack.cs @@ -12,7 +12,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void OnTick() @@ -51,9 +51,9 @@ namespace jRandomSkills } } - public class SkillConfig : Config.DefaultSkillInfo + public class SkillConfig : SkillsInfo.DefaultSkillInfo { - public SkillConfig(Skills skill = skillName, bool active = true, string color = "#2effcb", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : base(skill, active, color, onlyTeam, needsTeammates) + public SkillConfig(Skills skill = skillName, bool active = true, string color = "#2effcb", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : base(skill, active, color, onlyTeam, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Rambo.cs b/jRandomSkills - SRC Files/src/player/skills/Rambo.cs index b5f3990..f2a0082 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Rambo.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Rambo.cs @@ -9,17 +9,15 @@ namespace jRandomSkills public class Rambo : ISkill { private const Skills skillName = Skills.Rambo; - private static readonly int minExtraHealth = Config.GetValue(skillName, "minExtraHealth"); - private static readonly int maxExtraHealth = Config.GetValue(skillName, "maxExtraHealth"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void EnableSkill(CCSPlayerController player) { - int healthBonus = Instance.Random.Next(minExtraHealth, maxExtraHealth); + int healthBonus = Instance.Random.Next(SkillsInfo.GetValue(skillName, "minExtraHealth"), SkillsInfo.GetValue(skillName, "maxExtraHealth")); AddHealth(player, healthBonus); } @@ -52,7 +50,7 @@ namespace jRandomSkills Utilities.SetStateChanged(pawn, "CBaseEntity", "m_iHealth"); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#009905", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, int minExtraHealth = 50, int maxExtraHealth = 501) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#009905", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, int minExtraHealth = 50, int maxExtraHealth = 501) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int MinExtraHealth { get; set; } = minExtraHealth; public int MaxExtraHealth { get; set; } = maxExtraHealth; diff --git a/jRandomSkills - SRC Files/src/player/skills/RandomWeapon.cs b/jRandomSkills - SRC Files/src/player/skills/RandomWeapon.cs index 042d4f1..afcd260 100644 --- a/jRandomSkills - SRC Files/src/player/skills/RandomWeapon.cs +++ b/jRandomSkills - SRC Files/src/player/skills/RandomWeapon.cs @@ -5,14 +5,15 @@ using jRandomSkills.src.player; using jRandomSkills.src.utils; using System.Collections.Immutable; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class RandomWeapon : ISkill { private const Skills skillName = Skills.RandomWeapon; - private static readonly float timerCooldown = Config.GetValue(skillName, "cooldown"); - private static readonly Dictionary SkillPlayerInfo = []; + private static readonly ConcurrentDictionary SkillPlayerInfo = []; + private static readonly object setLock = new(); private static readonly string[] pistols = [ "weapon_deagle", "weapon_revolver", "weapon_glock", "weapon_usp_silencer", "weapon_cz75a", "weapon_fiveseven", "weapon_p250", "weapon_tec9", "weapon_elite", "weapon_hkp2000" ]; @@ -24,12 +25,13 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - SkillPlayerInfo.Clear(); + lock (setLock) + SkillPlayerInfo.Clear(); } public static void OnTick() @@ -45,17 +47,18 @@ namespace jRandomSkills public static void EnableSkill(CCSPlayerController player) { - SkillPlayerInfo[player.SteamID] = new PlayerSkillInfo + SkillPlayerInfo.TryAdd(player.SteamID, new PlayerSkillInfo { SteamID = player.SteamID, CanUse = true, Cooldown = DateTime.MinValue, - }; + }); } public static void DisableSkill(CCSPlayerController player) { - SkillPlayerInfo.Remove(player.SteamID); + SkillPlayerInfo.TryRemove(player.SteamID, out _); + SkillUtils.ResetPrintHTML(player); } private static void UpdateHUD(CCSPlayerController player, PlayerSkillInfo skillInfo) @@ -63,22 +66,20 @@ namespace jRandomSkills float cooldown = 0; if (skillInfo != null) { - float time = (int)(skillInfo.Cooldown.AddSeconds(timerCooldown) - DateTime.Now).TotalSeconds; + float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue(skillName, "cooldown")) - DateTime.Now).TotalSeconds); cooldown = Math.Max(time, 0); if (cooldown == 0 && skillInfo?.CanUse == false) skillInfo.CanUse = true; } - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = cooldown != 0 ? $"{player.GetTranslation("hud_info", $"{cooldown}")}
" : ""; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + if (cooldown == 0) + playerInfo.PrintHTML = null; + else + playerInfo.PrintHTML = $"{player.GetTranslation("hud_info", $"{cooldown}")}"; } public static void UseSkill(CCSPlayerController player) @@ -145,7 +146,7 @@ namespace jRandomSkills public DateTime Cooldown { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#e0873a", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float cooldown = 15f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#e0873a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float cooldown = 15f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float Cooldown { get; set; } = cooldown; } diff --git a/jRandomSkills - SRC Files/src/player/skills/ReZombie.cs b/jRandomSkills - SRC Files/src/player/skills/ReZombie.cs index 739f96b..deafdb3 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ReZombie.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ReZombie.cs @@ -4,29 +4,30 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using System.Drawing; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class ReZombie : ISkill { private const Skills skillName = Skills.ReZombie; - private static readonly int zombieHealth = Config.GetValue(skillName, "zombieHealth"); - private static readonly HashSet zombies = []; + private static readonly ConcurrentDictionary zombies = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void EnableSkill(CCSPlayerController player) { - zombies.Remove(player); + zombies.TryRemove(player, out _); } public static void DisableSkill(CCSPlayerController player) { if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid) return; - zombies.Remove(player); + zombies.TryRemove(player, out _); SetPlayerColor(player.PlayerPawn.Value, true); } @@ -35,13 +36,13 @@ namespace jRandomSkills var player = @event.Userid; var weapon = @event.Item; if (player == null || !player.IsValid) return; - if (!zombies.Contains(player) || weapon == "c4") return; + if (!zombies.ContainsKey(player) || weapon == "c4") return; player.ExecuteClientCommand("slot3"); } public static void NewRound() { - foreach (var player in zombies) + foreach (var player in zombies.Keys) DisableSkill(player); zombies.Clear(); } @@ -49,7 +50,7 @@ namespace jRandomSkills public static void PlayerDeath(EventPlayerDeath @event) { var player = @event.Userid; - if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid || zombies.Contains(player)) return; + if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid || zombies.ContainsKey(player)) return; var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo?.Skill != skillName) return; @@ -61,13 +62,17 @@ namespace jRandomSkills player.Respawn(); Instance.AddTimer(.2f, () => { - player.Respawn(); - zombies.Add(player); - SetPlayerColor(pawn, false); - SkillUtils.AddHealth(pawn, zombieHealth - 100, zombieHealth); - pawn.Teleport(deadPosition, deadRotation); - player.ExecuteClientCommand("slot3"); - Instance.AddTimer(1, () => player.ExecuteClientCommand("slot3")); + lock (setLock) + { + var zombieHealth = SkillsInfo.GetValue(skillName, "zombieHealth"); + player.Respawn(); + zombies.TryAdd(player, 0); + SetPlayerColor(pawn, false); + SkillUtils.AddHealth(pawn, zombieHealth - 100, zombieHealth); + pawn.Teleport(deadPosition, deadRotation); + player.ExecuteClientCommand("slot3"); + Instance.AddTimer(1, () => player.ExecuteClientCommand("slot3")); + } }); } @@ -78,7 +83,7 @@ namespace jRandomSkills Utilities.SetStateChanged(pawn, "CBaseModelEntity", "m_clrRender"); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff5C0A", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, int zombieHealth = 200) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff5C0A", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, int zombieHealth = 200) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int ZombieHealth { get; set; } = zombieHealth; } diff --git a/jRandomSkills - SRC Files/src/player/skills/ReactiveArmor.cs b/jRandomSkills - SRC Files/src/player/skills/ReactiveArmor.cs index 6a951d1..b81da13 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ReactiveArmor.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ReactiveArmor.cs @@ -4,23 +4,25 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class ReactiveArmor : ISkill { private const Skills skillName = Skills.ReactiveArmor; - private static readonly float timerCooldown = Config.GetValue(skillName, "cooldown"); - private static readonly Dictionary SkillPlayerInfo = []; + private static readonly ConcurrentDictionary SkillPlayerInfo = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - SkillPlayerInfo.Clear(); + lock (setLock) + SkillPlayerInfo.Clear(); } public static void OnTick() @@ -52,17 +54,18 @@ namespace jRandomSkills public static void EnableSkill(CCSPlayerController player) { - SkillPlayerInfo[player.SteamID] = new PlayerSkillInfo + SkillPlayerInfo.TryAdd(player.SteamID, new PlayerSkillInfo { SteamID = player.SteamID, CanUse = true, Cooldown = DateTime.MinValue, - }; + }); } public static void DisableSkill(CCSPlayerController player) { - SkillPlayerInfo.Remove(player.SteamID); + SkillPlayerInfo.TryRemove(player.SteamID, out _); + SkillUtils.ResetPrintHTML(player); } private static void UpdateHUD(CCSPlayerController player, PlayerSkillInfo skillInfo) @@ -70,22 +73,20 @@ namespace jRandomSkills float cooldown = 0; if (skillInfo != null) { - float time = (int)(skillInfo.Cooldown.AddSeconds(timerCooldown) - DateTime.Now).TotalSeconds; + float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue(skillName, "cooldown")) - DateTime.Now).TotalSeconds); cooldown = Math.Max(time, 0); if (cooldown == 0 && skillInfo?.CanUse == false) skillInfo.CanUse = true; } - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = cooldown != 0 ? $"{player.GetTranslation("hud_info", $"{cooldown}")}
" : ""; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + if (cooldown == 0) + playerInfo.PrintHTML = null; + else + playerInfo.PrintHTML = $"{player.GetTranslation("hud_info", $"{cooldown}")}"; } private static void RestoreHealth(CCSPlayerController victim, float damage) @@ -108,7 +109,7 @@ namespace jRandomSkills public DateTime Cooldown { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#3cded3", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float cooldown = 15) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#3cded3", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float cooldown = 15) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float Cooldown { get; set; } = cooldown; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Regeneration.cs b/jRandomSkills - SRC Files/src/player/skills/Regeneration.cs index 7710f44..495d96e 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Regeneration.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Regeneration.cs @@ -8,17 +8,15 @@ namespace jRandomSkills public class Regeneration : ISkill { private const Skills skillName = Skills.Regeneration; - private static float cooldown = Config.GetValue(skillName, "cooldown"); - private static int healthToAdd = Config.GetValue(skillName, "healthToAdd"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void OnTick() { - if (Server.TickCount % (int)(64 * cooldown) != 0) return; + if (Server.TickCount % (int)(64 * SkillsInfo.GetValue(skillName, "cooldown")) != 0) return; foreach (var player in Utilities.GetPlayers()) { var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); @@ -26,15 +24,15 @@ namespace jRandomSkills var pawn = player.PlayerPawn.Value; if (pawn == null || !pawn.IsValid) continue; - SkillUtils.AddHealth(pawn, healthToAdd); + SkillUtils.AddHealth(pawn, SkillsInfo.GetValue(skillName, "healthToAdd")); } } - public class SkillConfig : Config.DefaultSkillInfo + public class SkillConfig : SkillsInfo.DefaultSkillInfo { public int HealthToAdd { get; set; } public float Cooldown { get; set; } - public SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff462e", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, int healthToAdd = 1, float cooldown = .25f) : base(skill, active, color, onlyTeam, needsTeammates) + public SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff462e", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, int healthToAdd = 1, float cooldown = .25f) : base(skill, active, color, onlyTeam, needsTeammates) { HealthToAdd = healthToAdd; Cooldown = cooldown; diff --git a/jRandomSkills - SRC Files/src/player/skills/Replicator.cs b/jRandomSkills - SRC Files/src/player/skills/Replicator.cs index 2d2b3ff..2a9abd9 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Replicator.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Replicator.cs @@ -5,23 +5,25 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Replicator : ISkill { private const Skills skillName = Skills.Replicator; - private static readonly float timerCooldown = Config.GetValue(skillName, "cooldown"); - private static readonly Dictionary SkillPlayerInfo = []; + private static readonly ConcurrentDictionary SkillPlayerInfo = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - SkillPlayerInfo.Clear(); + lock (setLock) + SkillPlayerInfo.Clear(); } public static void OnTick() @@ -37,17 +39,18 @@ namespace jRandomSkills public static void EnableSkill(CCSPlayerController player) { - SkillPlayerInfo[player.SteamID] = new PlayerSkillInfo + SkillPlayerInfo.TryAdd(player.SteamID, new PlayerSkillInfo { SteamID = player.SteamID, CanUse = true, Cooldown = DateTime.MinValue, - }; + }); } public static void DisableSkill(CCSPlayerController player) { - SkillPlayerInfo.Remove(player.SteamID); + SkillPlayerInfo.TryRemove(player.SteamID, out _); + SkillUtils.ResetPrintHTML(player); } private static void UpdateHUD(CCSPlayerController player, PlayerSkillInfo skillInfo) @@ -55,22 +58,20 @@ namespace jRandomSkills float cooldown = 0; if (skillInfo != null) { - float time = (int)(skillInfo.Cooldown.AddSeconds(timerCooldown) - DateTime.Now).TotalSeconds; + float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue(skillName, "cooldown")) - DateTime.Now).TotalSeconds); cooldown = Math.Max(time, 0); if (cooldown == 0 && skillInfo?.CanUse == false) skillInfo.CanUse = true; } - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = cooldown != 0 ? $"{player.GetTranslation("hud_info", $"{cooldown}")}
" : ""; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + if (cooldown == 0) + playerInfo.PrintHTML = null; + else + playerInfo.PrintHTML = $"{player.GetTranslation("hud_info", $"{cooldown}")}"; } public static void UseSkill(CCSPlayerController player) @@ -142,7 +143,7 @@ namespace jRandomSkills public DateTime Cooldown { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a3000b", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float cooldown = 15f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a3000b", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, float cooldown = 15f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float Cooldown { get; set; } = cooldown; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Retreat.cs b/jRandomSkills - SRC Files/src/player/skills/Retreat.cs index 4604c45..f7f9b2e 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Retreat.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Retreat.cs @@ -4,23 +4,25 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Retreat : ISkill { private const Skills skillName = Skills.Retreat; - private static readonly float timerCooldown = Config.GetValue(skillName, "cooldown"); - private static readonly Dictionary SkillPlayerInfo = []; + private static readonly ConcurrentDictionary SkillPlayerInfo = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - SkillPlayerInfo.Clear(); + lock (setLock) + SkillPlayerInfo.Clear(); } public static void OnTick() @@ -36,17 +38,18 @@ namespace jRandomSkills public static void EnableSkill(CCSPlayerController player) { - SkillPlayerInfo[player.SteamID] = new PlayerSkillInfo + SkillPlayerInfo.TryAdd(player.SteamID, new PlayerSkillInfo { SteamID = player.SteamID, CanUse = true, Cooldown = DateTime.MinValue, - }; + }); } public static void DisableSkill(CCSPlayerController player) { - SkillPlayerInfo.Remove(player.SteamID); + SkillPlayerInfo.TryRemove(player.SteamID, out _); + SkillUtils.ResetPrintHTML(player); } private static void UpdateHUD(CCSPlayerController player, PlayerSkillInfo skillInfo) @@ -54,22 +57,20 @@ namespace jRandomSkills float cooldown = 0; if (skillInfo != null) { - float time = (int)(skillInfo.Cooldown.AddSeconds(timerCooldown) - DateTime.Now).TotalSeconds; + float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue(skillName, "cooldown")) - DateTime.Now).TotalSeconds); cooldown = Math.Max(time, 0); if (cooldown == 0 && skillInfo?.CanUse == false) skillInfo.CanUse = true; } - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = cooldown != 0 ? $"{player.GetTranslation("hud_info", $"{cooldown}")}
" : ""; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + if (cooldown == 0) + playerInfo.PrintHTML = null; + else + playerInfo.PrintHTML = $"{player.GetTranslation("hud_info", $"{cooldown}")}"; } public static void UseSkill(CCSPlayerController player) @@ -107,7 +108,7 @@ namespace jRandomSkills public DateTime Cooldown { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a86eff", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float cooldown = 15f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a86eff", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, float cooldown = 15f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float Cooldown { get; set; } = cooldown; } diff --git a/jRandomSkills - SRC Files/src/player/skills/ReturnToSender.cs b/jRandomSkills - SRC Files/src/player/skills/ReturnToSender.cs index 1535400..37cc42b 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ReturnToSender.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ReturnToSender.cs @@ -3,17 +3,18 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class ReturnToSender : ISkill { private const Skills skillName = Skills.ReturnToSender; - private static readonly HashSet playersToSender = []; + private static readonly ConcurrentDictionary playersToSender = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -31,18 +32,18 @@ namespace jRandomSkills var attackerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == attacker?.SteamID); if (attackerInfo == null || attackerInfo.Skill != skillName) return; - if (playersToSender.TryGetValue(victim!.Handle, out _)) + if (playersToSender.ContainsKey(victim!.Handle)) return; var spawn = GetSpawnVector(victim); if (spawn == null) return; victim!.PlayerPawn!.Value!.Teleport(spawn); - playersToSender.Add(victim.Handle); + playersToSender.TryAdd(victim.Handle, 0); } public static void DisableSkill(CCSPlayerController player) { - playersToSender.Remove(player.Handle); + playersToSender.TryRemove(player.Handle, out _); } private static Vector? GetSpawnVector(CCSPlayerController player) @@ -56,7 +57,7 @@ namespace jRandomSkills return null; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a68132", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#a68132", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/RichBoy.cs b/jRandomSkills - SRC Files/src/player/skills/RichBoy.cs index ef1dc2c..7cacadc 100644 --- a/jRandomSkills - SRC Files/src/player/skills/RichBoy.cs +++ b/jRandomSkills - SRC Files/src/player/skills/RichBoy.cs @@ -9,19 +9,17 @@ namespace jRandomSkills public class RichBoy : ISkill { private const Skills skillName = Skills.RichBoy; - private static readonly int minMoney = Config.GetValue(skillName, "minMoney"); - private static readonly int maxMoney = Config.GetValue(skillName, "maxMoney"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void EnableSkill(CCSPlayerController player) { var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo == null) return; - int moneyBonus = Instance.Random.Next(minMoney, maxMoney); + int moneyBonus = Instance.Random.Next(SkillsInfo.GetValue(skillName, "minMoney"), SkillsInfo.GetValue(skillName, "maxMoney")); playerInfo.SkillChance = moneyBonus; AddMoney(player, moneyBonus); } @@ -44,7 +42,7 @@ namespace jRandomSkills Utilities.SetStateChanged(player, "CCSPlayerController", "m_pInGameMoneyServices"); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#D4AF37", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, int minMoney = 5000, int maxMoney = 15000) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#D4AF37", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, int minMoney = 5000, int maxMoney = 15000) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int MinMoney { get; set; } = minMoney; public int MaxMoney { get; set; } = maxMoney; diff --git a/jRandomSkills - SRC Files/src/player/skills/RobinHood.cs b/jRandomSkills - SRC Files/src/player/skills/RobinHood.cs index f4bfbd9..cd9fda1 100644 --- a/jRandomSkills - SRC Files/src/player/skills/RobinHood.cs +++ b/jRandomSkills - SRC Files/src/player/skills/RobinHood.cs @@ -9,11 +9,10 @@ namespace jRandomSkills public class RobinHood : ISkill { private const Skills skillName = Skills.RobinHood; - private static readonly int moneyMultiplier = Config.GetValue(skillName, "moneyMultiplier"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerHurt(EventPlayerHurt @event) @@ -26,7 +25,7 @@ namespace jRandomSkills var attackerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == attacker?.SteamID); if (attackerInfo?.Skill != skillName) return; - int moneyToSteal = damage * moneyMultiplier; + int moneyToSteal = damage * SkillsInfo.GetValue(skillName, "moneyMultiplier"); StealMoney(victim!, attacker!, moneyToSteal); } @@ -44,7 +43,7 @@ namespace jRandomSkills Utilities.SetStateChanged(attacker!, "CCSPlayerController", "m_pInGameMoneyServices"); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#119125", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, int moneyMultiplier = 35) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#119125", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, int moneyMultiplier = 35) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int MoneyMultiplier { get; set; } = moneyMultiplier; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Rubber.cs b/jRandomSkills - SRC Files/src/player/skills/Rubber.cs index 48bd1d4..4baa918 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Rubber.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Rubber.cs @@ -3,20 +3,18 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Rubber: ISkill { private const Skills skillName = Skills.Rubber; - private static readonly float rubberTime = Config.GetValue(skillName, "slownessTime"); - private static readonly float rubberModifier = Config.GetValue(skillName, "slownessModifier"); - - private static readonly Dictionary playersToSlow = []; + private static readonly ConcurrentDictionary playersToSlow = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -35,11 +33,9 @@ namespace jRandomSkills var victimPawn = victim!.PlayerPawn.Value; if (victimPawn == null || !victimPawn.IsValid) return; + var rubberTime = SkillsInfo.GetValue(skillName, "slownessTime"); if (attackerInfo?.Skill == skillName) - if (playersToSlow.ContainsKey(victimPawn)) - playersToSlow[victimPawn] = Server.TickCount + (64 * rubberTime); - else playersToSlow.TryAdd(victimPawn, Server.TickCount + (64 * rubberTime)); - + playersToSlow.AddOrUpdate(victimPawn, Server.TickCount + (64 * rubberTime), (k, v) => Server.TickCount + (64 * rubberTime)); } public static void OnTick() @@ -51,17 +47,17 @@ namespace jRandomSkills if (time >= Server.TickCount) ChangeVelocity(pawn); else - playersToSlow.Remove(item.Key); + playersToSlow.TryRemove(item.Key, out _); } } private static void ChangeVelocity(CCSPlayerPawn pawn) { if (pawn == null || !pawn.IsValid) return; - pawn.VelocityModifier = rubberModifier; + pawn.VelocityModifier = SkillsInfo.GetValue(skillName, "slownessModifier"); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8B4513", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float slownessTime = 2f, float slownessModifier = .2f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8B4513", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float slownessTime = 2f, float slownessModifier = .2f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float SlownessTime { get; set; } = slownessTime; public float SlownessModifier { get; set; } = slownessModifier; diff --git a/jRandomSkills - SRC Files/src/player/skills/Saper.cs b/jRandomSkills - SRC Files/src/player/skills/Saper.cs index 2798f72..23c1040 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Saper.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Saper.cs @@ -12,7 +12,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void BombBegindefuse(EventBombBegindefuse @event) @@ -51,7 +51,7 @@ namespace jRandomSkills } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8A2BE2", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8A2BE2", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/SecondLife.cs b/jRandomSkills - SRC Files/src/player/skills/SecondLife.cs index 06aaefe..8f00647 100644 --- a/jRandomSkills - SRC Files/src/player/skills/SecondLife.cs +++ b/jRandomSkills - SRC Files/src/player/skills/SecondLife.cs @@ -3,18 +3,19 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class SecondLife : ISkill { private const Skills skillName = Skills.SecondLife; - private static readonly int secondLifeHealth = Config.GetValue(skillName, "startHealth"); - private static readonly HashSet secondLifePlayers = []; + private static readonly ConcurrentDictionary secondLifePlayers = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -32,26 +33,29 @@ namespace jRandomSkills if (victimInfo == null || victimInfo.Skill != skillName) return; var victimPawn = victim!.PlayerPawn.Value; - if (victimPawn!.Health > 0 || secondLifePlayers.TryGetValue(victim.Handle, out _) == true) + if (victimPawn!.Health > 0 || secondLifePlayers.ContainsKey(victim.Handle)) return; - secondLifePlayers.Add(victim.Handle); - SetHealth(victim, secondLifeHealth); - var spawn = GetSpawnVector(victim); - if (spawn != null) - victimPawn.Teleport(spawn, victimPawn.AbsRotation, null); + lock (setLock) + { + secondLifePlayers.TryAdd(victim.Handle, 0); + SetHealth(victim, SkillsInfo.GetValue(skillName, "startHealth")); + var spawn = GetSpawnVector(victim); + if (spawn != null) + victimPawn.Teleport(spawn, victimPawn.AbsRotation, null); + } } public static void EnableSkill(CCSPlayerController player) { - SetHealth(player, secondLifeHealth); + SetHealth(player, SkillsInfo.GetValue(skillName, "startHealth")); } public static void DisableSkill(CCSPlayerController player) { - secondLifePlayers.Remove(player.Handle); + secondLifePlayers.TryRemove(player.Handle, out _); if (player.PlayerPawn.Value == null) return; - SetHealth(player, Math.Min(player.PlayerPawn.Value.Health + secondLifeHealth, 100)); + SetHealth(player, Math.Min(player.PlayerPawn.Value.Health + SkillsInfo.GetValue(skillName, "startHealth"), 100)); } private static void SetHealth(CCSPlayerController player, int health) @@ -81,7 +85,7 @@ namespace jRandomSkills return abs == null ? null : new Vector(abs.X, abs.Y, abs.Z); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#d41c1c", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, int startHealth = 50) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#d41c1c", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, int startHealth = 50) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int StartHealth { get; set; } = startHealth; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Shade.cs b/jRandomSkills - SRC Files/src/player/skills/Shade.cs index 43ad32e..fb4403a 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Shade.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Shade.cs @@ -9,18 +9,18 @@ using jRandomSkills.src.utils; using System.Numerics; using static jRandomSkills.jRandomSkills; using Vector = CounterStrikeSharp.API.Modules.Utils.Vector; +using System.Collections.Concurrent; namespace jRandomSkills { public class Shade : ISkill { private const Skills skillName = Skills.Shade; - private static readonly float teleportDistance = Config.GetValue(skillName, "teleportDistance"); - private static readonly Dictionary noSpace = []; + private static readonly ConcurrentDictionary noSpace = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -44,7 +44,8 @@ namespace jRandomSkills public static void DisableSkill(CCSPlayerController player) { - noSpace.Remove(player); + noSpace.TryRemove(player, out _); + SkillUtils.ResetPrintHTML(player); } public static void OnTick() @@ -56,14 +57,9 @@ namespace jRandomSkills private static void UpdateHUD(CCSPlayerController player) { - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; - - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = $"{player.GetTranslation("shade_nospace")}
"; - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; + playerInfo.PrintHTML = $"{player.GetTranslation("shade_nospace")}"; } private unsafe static bool CheckTeleport(CCSPlayerController player, Vector startPos, Vector endPos) @@ -104,17 +100,17 @@ namespace jRandomSkills foreach (int extraAngle in angles) { QAngle newAngle = new(victimAngles.X, victimAngles.Y + extraAngle, victimAngles.Z); - Vector behindPosition = victimEyePos - SkillUtils.GetForwardVector(newAngle) * teleportDistance; + Vector behindPosition = victimEyePos - SkillUtils.GetForwardVector(newAngle) * SkillsInfo.GetValue(skillName, "teleportDistance"); if (!CheckTeleport(victim, victimEyePos, behindPosition)) continue; attackerPawn.Teleport(behindPosition, newAngle, new(0, 0, 0)); teleported = true; break; } if (!teleported) - noSpace[attacker] = Server.TickCount + (64 * 2); + noSpace.AddOrUpdate(attacker, Server.TickCount + (64 * 2), (k, v) => Server.TickCount + (64 * 2)); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#4d4d4d", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float teleportDistance = 100f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#4d4d4d", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float teleportDistance = 100f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float TeleportDistance { get; set; } = teleportDistance; } diff --git a/jRandomSkills - SRC Files/src/player/skills/ShortBomb.cs b/jRandomSkills - SRC Files/src/player/skills/ShortBomb.cs index 966e14a..21cfd85 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ShortBomb.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ShortBomb.cs @@ -9,11 +9,10 @@ namespace jRandomSkills public class ShortBomb : ISkill { private const Skills skillName = Skills.ShortBomb; - private static readonly int detonationTime = Config.GetValue(skillName, "detonationTime"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void BombPlanted(EventBombPlanted @event) @@ -26,10 +25,10 @@ namespace jRandomSkills var plantedBomb = Utilities.FindAllEntitiesByDesignerName("planted_c4").FirstOrDefault(); if (plantedBomb != null) - Server.NextFrame(() => plantedBomb.C4Blow = (float)Server.EngineTime + detonationTime); + Server.NextFrame(() => plantedBomb.C4Blow = (float)Server.EngineTime + SkillsInfo.GetValue(skillName, "detonationTime")); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#f5b74c", CsTeam onlyTeam = CsTeam.Terrorist, bool needsTeammates = false, int detonationTime = 20) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#f5b74c", CsTeam onlyTeam = CsTeam.Terrorist, bool disableOnFreezeTime = false, bool needsTeammates = false, int detonationTime = 20) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int DetonationTime { get; set; } = detonationTime; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Silent.cs b/jRandomSkills - SRC Files/src/player/skills/Silent.cs index 79df00e..78845cf 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Silent.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Silent.cs @@ -12,7 +12,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerMakeSound(UserMessage um) @@ -33,7 +33,7 @@ namespace jRandomSkills um.Recipients.Clear(); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#333333", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#333333", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/SniperElite.cs b/jRandomSkills - SRC Files/src/player/skills/SniperElite.cs index 309df2c..e880a0a 100644 --- a/jRandomSkills - SRC Files/src/player/skills/SniperElite.cs +++ b/jRandomSkills - SRC Files/src/player/skills/SniperElite.cs @@ -3,14 +3,16 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class SniperElite : ISkill { private const Skills skillName = Skills.SniperElite; - private static readonly Dictionary> playerAWPs = []; - private static readonly Dictionary sniperElites = []; + private static readonly ConcurrentDictionary> playerAWPs = []; + private static readonly ConcurrentDictionary sniperElites = []; + private static readonly object setLock = new(); private static readonly string[] rifles = [ "weapon_mp9", "weapon_mac10", "weapon_bizon", "weapon_mp7", "weapon_ump45", "weapon_p90", "weapon_mp5sd", "weapon_famas", "weapon_galilar", "weapon_m4a1", "weapon_m4a1_silencer", "weapon_ak47", @@ -19,13 +21,17 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - sniperElites.Clear(); - playerAWPs.Clear(); + + lock (setLock) + { + sniperElites.Clear(); + playerAWPs.Clear(); + } } public static void PlayerDeath(EventPlayerDeath @event) @@ -53,8 +59,8 @@ namespace jRandomSkills public static void DisableSkill(CCSPlayerController player) { - sniperElites.Remove(player.SteamID); - playerAWPs.Remove(player.SteamID); + sniperElites.TryRemove(player.SteamID, out _); + playerAWPs.TryRemove(player.SteamID, out _); } public static void UseSkill(CCSPlayerController player) @@ -79,7 +85,7 @@ namespace jRandomSkills if (activeRifle != null && activeRifle.IsValid) RemoveWeapon(player, activeRifle.DesignerName); - sniperElites[player.SteamID] = (activeRifle != null && activeRifle.IsValid) ? SkillUtils.GetDesignerName(activeRifle) : "weapon_awp"; + sniperElites.TryAdd(player.SteamID, (activeRifle != null && activeRifle.IsValid) ? SkillUtils.GetDesignerName(activeRifle) : "weapon_awp"); Server.NextFrame(() => { string weapon = holdedWeapon ?? "weapon_awp"; if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PlayerPawn.Value.IsValid) return; @@ -90,7 +96,7 @@ namespace jRandomSkills if (playerAWPs.TryGetValue(player.SteamID, out List? value)) value.Add(createdWeapon); else - playerAWPs.Add(player.SteamID, [createdWeapon]); + playerAWPs.TryAdd(player.SteamID, [createdWeapon]); } DeleteDroppedAWP(player); }); @@ -149,7 +155,7 @@ namespace jRandomSkills item.Value.AcceptInput("Kill"); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#e0873a", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#e0873a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Soldier.cs b/jRandomSkills - SRC Files/src/player/skills/Soldier.cs index 7a3c59b..7e1aec2 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Soldier.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Soldier.cs @@ -13,17 +13,17 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); } public static void EnableSkill(CCSPlayerController player) { var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo == null) return; - float newScale = (float)Instance.Random.NextDouble() * (Config.GetValue(skillName, "ChanceTo") - Config.GetValue(skillName, "ChanceFrom")) + Config.GetValue(skillName, "ChanceFrom"); + float newScale = (float)Instance.Random.NextDouble() * (SkillsInfo.GetValue(skillName, "ChanceTo") - SkillsInfo.GetValue(skillName, "ChanceFrom")) + SkillsInfo.GetValue(skillName, "ChanceFrom"); playerInfo.SkillChance = newScale; newScale = (float)Math.Round(newScale, 2); - SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetTranslation("soldier")}{ChatColors.Lime}: " + player.GetTranslation("soldier_desc2", newScale), false); + SkillUtils.PrintToChat(player, $"{ChatColors.DarkRed}{player.GetSkillName(skillName)}{ChatColors.Lime}: {player.GetSkillDescription(skillName, newScale)}", false); } public static void OnTakeDamage(DynamicHook h) @@ -56,7 +56,7 @@ namespace jRandomSkills } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#09ba00", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float chanceFrom = 1.15f, float chanceTo = 1.35f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#09ba00", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float chanceFrom = 1.15f, float chanceTo = 1.35f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float ChanceFrom { get; set; } = chanceFrom; public float ChanceTo { get; set; } = chanceTo; diff --git a/jRandomSkills - SRC Files/src/player/skills/SoundMaker.cs b/jRandomSkills - SRC Files/src/player/skills/SoundMaker.cs index 5b10cd3..ca1ce89 100644 --- a/jRandomSkills - SRC Files/src/player/skills/SoundMaker.cs +++ b/jRandomSkills - SRC Files/src/player/skills/SoundMaker.cs @@ -4,38 +4,41 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class SoundMaker : ISkill { private const Skills skillName = Skills.SoundMaker; - private static readonly float timerCooldown = Config.GetValue(skillName, "Cooldown"); - private static readonly Dictionary SkillPlayerInfo = []; + private static readonly ConcurrentDictionary SkillPlayerInfo = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - SkillPlayerInfo.Clear(); + lock (setLock) + SkillPlayerInfo.Clear(); } public static void EnableSkill(CCSPlayerController player) { - SkillPlayerInfo[player.SteamID] = new PlayerSkillInfo + SkillPlayerInfo.TryAdd(player.SteamID, new PlayerSkillInfo { SteamID = player.SteamID, CanUse = true, Cooldown = DateTime.MinValue, - }; + }); } public static void DisableSkill(CCSPlayerController player) { - SkillPlayerInfo.Remove(player.SteamID); + SkillPlayerInfo.TryRemove(player.SteamID, out _); + SkillUtils.ResetPrintHTML(player); } public static void PlayerDeath(EventPlayerDeath @event) @@ -44,7 +47,7 @@ namespace jRandomSkills if (player == null || !player.IsValid) return; var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo?.Skill == skillName) - SkillPlayerInfo.Remove(player.SteamID); + SkillPlayerInfo.TryRemove(player.SteamID, out _); } public static void OnTick() @@ -63,22 +66,20 @@ namespace jRandomSkills float cooldown = 0; if (skillInfo != null) { - float time = (int)(skillInfo.Cooldown.AddSeconds(timerCooldown) - DateTime.Now).TotalSeconds; + float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue(skillName, "Cooldown")) - DateTime.Now).TotalSeconds); cooldown = Math.Max(time, 0); if (cooldown == 0 && skillInfo?.CanUse == false) skillInfo.CanUse = true; } - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = cooldown != 0 ? $"{player.GetTranslation("hud_info", $"{cooldown}")}
" : ""; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + if (cooldown == 0) + playerInfo.PrintHTML = null; + else + playerInfo.PrintHTML = $"{player.GetTranslation("hud_info", $"{cooldown}")}"; } public static void UseSkill(CCSPlayerController player) @@ -112,7 +113,7 @@ namespace jRandomSkills public DateTime Cooldown { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#e3ed8c", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float cooldown = 5f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#e3ed8c", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float cooldown = 5f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float Cooldown { get; set; } = cooldown; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Spectator.cs b/jRandomSkills - SRC Files/src/player/skills/Spectator.cs index 7fa842d..75774c2 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Spectator.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Spectator.cs @@ -4,18 +4,18 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using System.Drawing; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class Spectator : ISkill { private const Skills skillName = Skills.Spectator; - private static readonly float distance = Config.GetValue(skillName, "distance"); - private static readonly Dictionary cameras = []; + private static readonly ConcurrentDictionary cameras = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -58,14 +58,7 @@ namespace jRandomSkills { var pawn = cameraInfo.Item3; if (pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) - { ChangeCamera(player, true); - continue; - } - if (pawn.AbsOrigin == null) continue; - var pos = pawn.AbsOrigin - SkillUtils.GetForwardVector(pawn.EyeAngles) * distance; - pos.Z += pawn.ViewOffset.Z; - cameraInfo.Item2.Teleport(pos, pawn.V_angle); } } @@ -106,15 +99,20 @@ namespace jRandomSkills var enemy = enemies[Instance.Random.Next(enemies.Count)]; var pawn = enemy.PlayerPawn.Value; - if (pawn == null || !pawn.IsValid || pawn.CameraServices == null) return 0; + if (pawn == null || !pawn.IsValid || pawn.CameraServices == null || pawn.AbsOrigin == null) return 0; + + var pos = pawn.AbsOrigin - SkillUtils.GetForwardVector(pawn.EyeAngles) * SkillsInfo.GetValue(skillName, "distance"); + pos.Z += pawn.ViewOffset.Z; camera.Render = Color.FromArgb(0, 255, 255, 255); - camera.Teleport(pawn!.AbsOrigin, pawn.EyeAngles); + camera.Teleport(pos, new QAngle(0, pawn.EyeAngles.Y, 0)); camera.DispatchSpawn(); + camera.AcceptInput("SetParent", pawn, pawn, "!activator"); + if (cameras.TryGetValue(player.SteamID, out var cameraInfo)) - cameras[player.SteamID] = (cameraInfo.Item1, camera, pawn); + cameras.AddOrUpdate(player.SteamID, (cameraInfo.Item1, camera, pawn), (k, v) => (cameraInfo.Item1, camera, pawn)); else - cameras[player.SteamID] = (pawn.CameraServices.ViewEntity.Raw, camera, pawn); + cameras.AddOrUpdate(player.SteamID, (pawn.CameraServices.ViewEntity.Raw, camera, pawn), (k, v) => (pawn.CameraServices.ViewEntity.Raw, camera, pawn)); return camera.EntityHandle.Raw; } @@ -135,7 +133,7 @@ namespace jRandomSkills } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#42f5da", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float distance = 100f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#42f5da", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float distance = 100f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float Distance { get; set; } = distance; } diff --git a/jRandomSkills - SRC Files/src/player/skills/SwapPosition.cs b/jRandomSkills - SRC Files/src/player/skills/SwapPosition.cs index 6fcafd1..d9c3ea5 100644 --- a/jRandomSkills - SRC Files/src/player/skills/SwapPosition.cs +++ b/jRandomSkills - SRC Files/src/player/skills/SwapPosition.cs @@ -4,27 +4,30 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class SwapPosition : ISkill { private const Skills skillName = Skills.SwapPosition; - private static readonly float timerCooldown = Config.GetValue(skillName, "cooldown"); - private static readonly Dictionary SkillPlayerInfo = []; + private static readonly ConcurrentDictionary SkillPlayerInfo = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - SkillPlayerInfo.Clear(); + lock (setLock) + SkillPlayerInfo.Clear(); } public static void OnTick() { + if (SkillUtils.IsFreezeTime()) return; foreach (var player in Utilities.GetPlayers()) { var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); @@ -39,19 +42,21 @@ namespace jRandomSkills public static void EnableSkill(CCSPlayerController player) { - SkillPlayerInfo[player.SteamID] = new ZamianaMiejsc_PlayerInfo + var cooldownBeforeUse = SkillsInfo.GetValue(skillName, "cooldownBeforeUse"); + SkillPlayerInfo.TryAdd(player.SteamID, new ZamianaMiejsc_PlayerInfo { SteamID = player.SteamID, - CanUse = true, - Cooldown = DateTime.MinValue, + CanUse = false, + Cooldown = cooldownBeforeUse <= 0 ? DateTime.MinValue : Event.freezeTimeEnd.AddSeconds(cooldownBeforeUse - SkillsInfo.GetValue(skillName, "cooldown")), LastClick = DateTime.MinValue, FindedEnemy = false, - }; + }); } public static void DisableSkill(CCSPlayerController player) { - SkillPlayerInfo.Remove(player.SteamID); + SkillPlayerInfo.TryRemove(player.SteamID, out _); + SkillUtils.ResetPrintHTML(player); } private static void UpdateHUD(CCSPlayerController player, ZamianaMiejsc_PlayerInfo skillInfo, bool showInfo) @@ -59,29 +64,27 @@ namespace jRandomSkills float cooldown = 0; if (skillInfo != null) { - float time = (int)(skillInfo.Cooldown.AddSeconds(timerCooldown) - DateTime.Now).TotalSeconds; + float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue(skillName, "cooldown")) - DateTime.Now).TotalSeconds); cooldown = Math.Max(time, 0); if (cooldown == 0 && skillInfo?.CanUse == false) skillInfo.CanUse = true; } - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = ""; + if (cooldown == 0) + { + if (showInfo) + playerInfo.PrintHTML = skillInfo != null && !skillInfo.FindedEnemy + ? $"{player.GetTranslation("hud_info_no_enemy")}" : null; + else + SkillUtils.ResetPrintHTML(player); + return; + } - if (showInfo) - remainingLine = cooldown != 0 ? $"{player.GetTranslation("hud_info", $"{cooldown}")}
" - : skillInfo != null && !skillInfo.FindedEnemy ? $"{player.GetTranslation("hud_info_no_enemy")}
" - : ""; - else - remainingLine = cooldown != 0 ? $"{player.GetTranslation("hud_info", $"{cooldown}")}
" : ""; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + playerInfo.PrintHTML = $"{player.GetTranslation("hud_info", $"{cooldown}")}"; } public static void UseSkill(CCSPlayerController player) @@ -141,9 +144,10 @@ namespace jRandomSkills public bool FindedEnemy { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1466F5", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float cooldown = 30f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1466F5", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = true, bool needsTeammates = false, float cooldown = 30f, float cooldownBeforeUse = 10f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float Cooldown { get; set; } = cooldown; + public float CooldownBeforeUse { get; set; } = cooldownBeforeUse; } } } \ No newline at end of file diff --git a/jRandomSkills - SRC Files/src/player/skills/Teleporter.cs b/jRandomSkills - SRC Files/src/player/skills/Teleporter.cs index 0a32317..6919582 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Teleporter.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Teleporter.cs @@ -12,7 +12,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerHurt(EventPlayerHurt @event) @@ -46,7 +46,7 @@ namespace jRandomSkills attackerPawn.Teleport(victimPosition, victimAngles, victimVelocity); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8A2BE2", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#8A2BE2", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Thief.cs b/jRandomSkills - SRC Files/src/player/skills/Thief.cs index 7d49d7b..ceb88e0 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Thief.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Thief.cs @@ -4,6 +4,7 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { @@ -13,7 +14,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color"), false); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color"), false); } public static void OnTick() @@ -27,7 +28,7 @@ namespace jRandomSkills if (playerInfo == null || playerInfo.Skill != skillName) continue; var enemies = Utilities.GetPlayers().Where(p => p.PawnIsAlive && p.Team != player.Team && p.IsValid && !p.IsBot && !p.IsHLTV && p.Team != CsTeam.Spectator && p.Team != CsTeam.None).ToArray(); - HashSet<(string, string)> menuItems = []; + ConcurrentBag<(string, string)> menuItems = []; foreach (var enemy in enemies) { var enemyInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == enemy.SteamID); @@ -75,7 +76,7 @@ namespace jRandomSkills if (enemies.Length > 0) { List skills = []; - HashSet<(string, string)> menuItems = []; + ConcurrentBag<(string, string)> menuItems = []; foreach (var enemy in enemies) { var enemyInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == enemy.SteamID); @@ -137,6 +138,14 @@ namespace jRandomSkills SkillUtils.CloseMenu(player); Instance.SkillAction(enemySkill.ToString(), "EnableSkill", [player]); player.PrintToChat($" {ChatColors.Green}" + player.GetTranslation("thief_player_info", enemy.PlayerName)); + + if (SkillsInfo.GetValue(enemySkill, "disableOnFreezeTime") && SkillUtils.IsFreezeTime()) + Instance?.AddTimer(Math.Max((float)(Event.freezeTimeEnd - DateTime.Now).TotalSeconds, 0), () => { + if (Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID && p.Skill == enemySkill) == null) return; + Instance?.SkillAction(enemySkill.ToString(), "EnableSkill", new[] { player }); + }); + else + Instance?.SkillAction(enemySkill.ToString(), "EnableSkill", new[] { player }); }); Instance.AddTimer(.1f, () => @@ -148,7 +157,7 @@ namespace jRandomSkills }); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#adaec7", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#adaec7", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/ThirdEye.cs b/jRandomSkills - SRC Files/src/player/skills/ThirdEye.cs index 190f8ba..d093621 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ThirdEye.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ThirdEye.cs @@ -3,18 +3,19 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using System.Drawing; +using System.Collections.Concurrent; namespace jRandomSkills { public class ThirdEye : ISkill { private const Skills skillName = Skills.ThirdEye; - private static readonly float distance = Config.GetValue(skillName, "distance"); - private static readonly Dictionary cameras = []; + private static readonly ConcurrentDictionary cameras = []; + private static readonly object setLock = new(); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -22,7 +23,8 @@ namespace jRandomSkills foreach (var camera in cameras) if (camera.Value.Item2 != null && camera.Value.Item2.IsValid) camera.Value.Item2.AcceptInput("Kill"); - cameras.Clear(); + lock (setLock) + cameras.Clear(); } public static void UseSkill(CCSPlayerController player) @@ -49,9 +51,13 @@ namespace jRandomSkills ChangeCamera(player, true); continue; } - var pos = pawn.AbsOrigin - SkillUtils.GetForwardVector(pawn.EyeAngles) * distance; + + var pos = pawn.AbsOrigin - SkillUtils.GetForwardVector(pawn.EyeAngles) * SkillsInfo.GetValue(skillName, "distance"); pos.Z += pawn.ViewOffset.Z; - cameraInfo.Item2.Teleport(pos, pawn.V_angle); + + var cam = cameraInfo.Item2; + if (cam == null || cam.AbsOrigin == null || cam.AbsRotation == null) continue; + cam.Teleport(pos, pawn.V_angle); } } @@ -92,11 +98,12 @@ namespace jRandomSkills camera.Render = Color.FromArgb(0, 255, 255, 255); camera.Teleport(pawn!.AbsOrigin, pawn.EyeAngles); camera.DispatchSpawn(); - cameras[player.SteamID] = (pawn.CameraServices!.ViewEntity.Raw, camera); + + cameras.AddOrUpdate(player.SteamID, (pawn.CameraServices!.ViewEntity.Raw, camera), (v,k) => (pawn.CameraServices!.ViewEntity.Raw, camera)); return camera.EntityHandle.Raw; } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1b04cc", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float distance = 100f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#1b04cc", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float distance = 100f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float Distance { get; set; } = distance; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Thorns.cs b/jRandomSkills - SRC Files/src/player/skills/Thorns.cs index 2c09c30..2e13ede 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Thorns.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Thorns.cs @@ -8,11 +8,10 @@ namespace jRandomSkills public class Thorns : ISkill { private const Skills skillName = Skills.Thorns; - private static readonly float healthTakenScale = Config.GetValue(skillName, "healthTakenScale"); public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void PlayerHurt(EventPlayerHurt @event) @@ -24,12 +23,12 @@ namespace jRandomSkills var victimInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == victim?.SteamID); if (victimInfo?.Skill == skillName && victim!.PawnIsAlive && attacker!.PawnIsAlive) { - SkillUtils.TakeHealth(attacker.PlayerPawn.Value, (int)(@event.DmgHealth * healthTakenScale)); + SkillUtils.TakeHealth(attacker.PlayerPawn.Value, (int)(@event.DmgHealth * SkillsInfo.GetValue(skillName, "healthTakenScale"))); attacker.EmitSound("Player.DamageBody.Onlooker"); } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#962631", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float healthTakenScale = .3f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#962631", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float healthTakenScale = .3f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float HealthTakenScale { get; set; } = healthTakenScale; } diff --git a/jRandomSkills - SRC Files/src/player/skills/ToxicSmoke.cs b/jRandomSkills - SRC Files/src/player/skills/ToxicSmoke.cs index 151124a..366b9b4 100644 --- a/jRandomSkills - SRC Files/src/player/skills/ToxicSmoke.cs +++ b/jRandomSkills - SRC Files/src/player/skills/ToxicSmoke.cs @@ -10,13 +10,11 @@ namespace jRandomSkills public class ToxicSmoke : ISkill { private const Skills skillName = Skills.ToxicSmoke; - private static readonly int smokeDamage = Config.GetValue(skillName, "smokeDamage"); - private static readonly float smokeRadius = Config.GetValue(skillName, "smokeRadius"); private static readonly List smokes = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -93,11 +91,11 @@ namespace jRandomSkills if (Server.TickCount % 17 == 0) if (player != null && player.IsValid && player.PlayerPawn.Value != null && player.PlayerPawn.Value.IsValid) if (player.PlayerPawn.Value.LifeState == (byte)LifeState_t.LIFE_ALIVE && player.PlayerPawn.Value.AbsOrigin != null) - if (SkillUtils.GetDistance(smokePos, player.PlayerPawn.Value.AbsOrigin) <= smokeRadius) - AddHealth(player.PlayerPawn.Value, -smokeDamage); + if (SkillUtils.GetDistance(smokePos, player.PlayerPawn.Value.AbsOrigin) <= SkillsInfo.GetValue(skillName, "smokeRadius")) + AddHealth(player.PlayerPawn.Value, -SkillsInfo.GetValue(skillName, "smokeDamage")); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#507529", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, int smokeDamage = 2, float smokeRadius = 180) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#507529", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, int smokeDamage = 2, float smokeRadius = 180) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int SmokeDamage { get; set; } = smokeDamage; public float SmokeRadius { get; set; } = smokeRadius; diff --git a/jRandomSkills - SRC Files/src/player/skills/Wallhack.cs b/jRandomSkills - SRC Files/src/player/skills/Wallhack.cs index 77e9977..406055b 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Wallhack.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Wallhack.cs @@ -11,11 +11,11 @@ namespace jRandomSkills public class Wallhack : ISkill { private const Skills skillName = Skills.Wallhack; - private static readonly List<(CDynamicProp, CDynamicProp)> glows = []; + private static readonly List<(CDynamicProp, CDynamicProp, CsTeam)> glows = []; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void CheckTransmit([CastFrom(typeof(nint))] CCheckTransmitInfoList infoList) @@ -28,11 +28,11 @@ namespace jRandomSkills var observedPlayer = Utilities.GetPlayers().FirstOrDefault(p => p?.Pawn?.Value?.Handle == player?.Pawn?.Value?.ObserverServices?.ObserverTarget?.Value?.Handle); var observerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == observedPlayer?.SteamID); - if (playerInfo?.Skill == skillName || (observerInfo != null && observerInfo?.Skill == skillName)) - continue; - foreach (var glow in glows) { + if (glow.Item3 != player.Team && (playerInfo?.Skill == skillName || (observerInfo != null && observerInfo?.Skill == skillName))) + continue; + info.TransmitEntities.Remove(glow.Item1); info.TransmitEntities.Remove(glow.Item2); } @@ -51,10 +51,11 @@ namespace jRandomSkills glows.Clear(); } - public static void EnableSkill(CCSPlayerController player) + public static void EnableSkill(CCSPlayerController _) { SkillUtils.EnableTransmit(); - SetGlowEffectForEnemies(player); + if (glows.Count == 0) + SetGlowEffectForAll(); } public static void DisableSkill(CCSPlayerController player) @@ -67,10 +68,9 @@ namespace jRandomSkills NewRound(); } - private static void SetGlowEffectForEnemies(CCSPlayerController player) + private static void SetGlowEffectForAll() { - CsTeam originalTeam = player.Team; - foreach (var enemy in Utilities.GetPlayers().FindAll(p => p.Team != originalTeam && p.PawnIsAlive)) + foreach (var enemy in Utilities.GetPlayers().FindAll(p => p.PawnIsAlive && p.Team is CsTeam.Terrorist or CsTeam.CounterTerrorist)) { var enemyInfo = Instance.SkillPlayer.FirstOrDefault(e => e.SteamID == enemy.SteamID); if (enemyInfo?.Skill == Skills.Ghost) @@ -103,11 +103,11 @@ namespace jRandomSkills modelRelay.AcceptInput("FollowEntity", enemyPawn, modelRelay, "!activator"); modelGlow.AcceptInput("FollowEntity", modelRelay, modelGlow, "!activator"); - glows.Add((modelRelay, modelGlow)); + glows.Add((modelRelay, modelGlow, enemy.Team)); } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#5d00ff", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#5d00ff", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/player/skills/Watchmaker.cs b/jRandomSkills - SRC Files/src/player/skills/Watchmaker.cs index abe7823..5c0bdb1 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Watchmaker.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Watchmaker.cs @@ -10,12 +10,11 @@ namespace jRandomSkills public class Watchmaker : ISkill { private const Skills skillName = Skills.Watchmaker; - private static readonly int roundTime = Config.GetValue(skillName, "changeRoundTime"); private static bool bombPlanted = false; public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() @@ -23,6 +22,11 @@ namespace jRandomSkills bombPlanted = false; } + public static void DisableSkill(CCSPlayerController player) + { + SkillUtils.ResetPrintHTML(player); + } + public static void BombPlanted(EventBombPlanted _) { bombPlanted = true; @@ -45,6 +49,7 @@ namespace jRandomSkills var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); if (playerInfo?.Skill != skillName || Instance.GameRules == null) return; + var roundTime = SkillsInfo.GetValue(skillName, "changeRoundTime"); Instance.GameRules.RoundTime += player.Team == CsTeam.Terrorist ? roundTime : -roundTime; if (player.Team == CsTeam.Terrorist) Localization.PrintTranslationToChatAll($" {ChatColors.Orange}{{0}}", ["watchmaker_tt"], [roundTime]); @@ -55,6 +60,7 @@ namespace jRandomSkills public static void OnTick() { if (bombPlanted) return; + if (SkillUtils.IsFreezeTime()) return; foreach (var player in Utilities.GetPlayers()) { var playerInfo = Instance.SkillPlayer.FirstOrDefault(p => p.SteamID == player.SteamID); @@ -69,16 +75,13 @@ namespace jRandomSkills if (skillData == null || Instance?.GameRules == null || Instance?.GameRules?.RoundTime == null || Instance.GameRules?.RoundStartTime == null) return; int seconds = 1 + (int)(Instance.GameRules.RoundTime - (Server.CurrentTime - Instance.GameRules.RoundStartTime)); - - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = $"{SkillUtils.SecondsToTimer(seconds)}
"; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; + playerInfo.PrintHTML = SkillUtils.SecondsToTimer(seconds); } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff462e", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, int changeRoundTime = 10) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#ff462e", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, int changeRoundTime = 10) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public int ChangeRoundTime { get; set; } = changeRoundTime; } diff --git a/jRandomSkills - SRC Files/src/player/skills/WeaponsSwap.cs b/jRandomSkills - SRC Files/src/player/skills/WeaponsSwap.cs index c55bcd1..f6370df 100644 --- a/jRandomSkills - SRC Files/src/player/skills/WeaponsSwap.cs +++ b/jRandomSkills - SRC Files/src/player/skills/WeaponsSwap.cs @@ -4,14 +4,15 @@ using CounterStrikeSharp.API.Modules.Utils; using jRandomSkills.src.player; using jRandomSkills.src.utils; using static jRandomSkills.jRandomSkills; +using System.Collections.Concurrent; namespace jRandomSkills { public class WeaponsSwap : ISkill { private const Skills skillName = Skills.WeaponsSwap; - private static readonly float timerCooldown = Config.GetValue(skillName, "cooldown"); - private static readonly Dictionary SkillPlayerInfo = []; + private static readonly ConcurrentDictionary SkillPlayerInfo = []; + private static readonly object setLock = new(); private static readonly string[] weapons = [ "weapon_deagle", "weapon_revolver", "weapon_glock", "weapon_usp_silencer", "weapon_cz75a", "weapon_fiveseven", "weapon_p250", "weapon_tec9", "weapon_elite", "weapon_hkp2000", @@ -22,17 +23,18 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void NewRound() { - SkillPlayerInfo.Clear(); + lock (setLock) + SkillPlayerInfo.Clear(); } public static void EnableSkill(CCSPlayerController player) { - SkillPlayerInfo[player.SteamID] = new PlayerSkillInfo + SkillPlayerInfo.TryAdd(player.SteamID, new PlayerSkillInfo { SteamID = player.SteamID, CanUse = true, @@ -40,12 +42,13 @@ namespace jRandomSkills LastClick = DateTime.MinValue, FindedEnemy = true, HaveWeapon = true, - }; + }); } public static void DisableSkill(CCSPlayerController player) { - SkillPlayerInfo.Remove(player.SteamID); + SkillPlayerInfo.TryRemove(player.SteamID, out _); + SkillUtils.ResetPrintHTML(player); } public static void OnTick() @@ -67,30 +70,29 @@ namespace jRandomSkills float cooldown = 0; if (skillInfo != null) { - float time = (int)(skillInfo.Cooldown.AddSeconds(timerCooldown) - DateTime.Now).TotalSeconds; + float time = (int)Math.Ceiling((skillInfo.Cooldown.AddSeconds(SkillsInfo.GetValue(skillName, "cooldown")) - DateTime.Now).TotalSeconds); cooldown = Math.Max(time, 0); if (cooldown == 0 && skillInfo?.CanUse == false) skillInfo.CanUse = true; } - var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == skillName); - if (skillData == null) return; + var playerInfo = Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; - string remainingLine = ""; + if (cooldown == 0) + { + if (showInfo) + playerInfo.PrintHTML = + skillInfo != null && !skillInfo.FindedEnemy + ? $"{player.GetTranslation("hud_info_no_enemy")}" + : skillInfo != null && !skillInfo.HaveWeapon ? $"{player.GetTranslation("weaponsswap_hud_info2")}" : null; + else + SkillUtils.ResetPrintHTML(player); + return; + } - if (showInfo) - remainingLine = cooldown != 0 ? $"{player.GetTranslation("hud_info", $"{cooldown}")}
" - : skillInfo != null && !skillInfo.FindedEnemy ? $"{player.GetTranslation("hud_info_no_enemy")}
" - : skillInfo != null && !skillInfo.HaveWeapon ? $"{player.GetTranslation("weaponsswap_hud_info2")}
" - : ""; - else - remainingLine = cooldown != 0 ? $"{player.GetTranslation("hud_info", $"{cooldown}")}
" : ""; - - var hudContent = infoLine + skillLine + remainingLine; - player.PrintToCenterHtml(hudContent); + playerInfo.PrintHTML = $"{player.GetTranslation("hud_info", $"{cooldown}")}"; } public static void UseSkill(CCSPlayerController player) @@ -193,7 +195,7 @@ namespace jRandomSkills public bool HaveWeapon { get; set; } } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#c7e03a", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false, float cooldown = 30f) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#c7e03a", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false, float cooldown = 30f) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { public float Cooldown { get; set; } = cooldown; } diff --git a/jRandomSkills - SRC Files/src/player/skills/Zeus.cs b/jRandomSkills - SRC Files/src/player/skills/Zeus.cs index 80c182f..2547b3b 100644 --- a/jRandomSkills - SRC Files/src/player/skills/Zeus.cs +++ b/jRandomSkills - SRC Files/src/player/skills/Zeus.cs @@ -12,7 +12,7 @@ namespace jRandomSkills public static void LoadSkill() { - SkillUtils.RegisterSkill(skillName, Config.GetValue(skillName, "color")); + SkillUtils.RegisterSkill(skillName, SkillsInfo.GetValue(skillName, "color")); } public static void EnableSkill(CCSPlayerController player) @@ -48,7 +48,7 @@ namespace jRandomSkills } - public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#fbff00", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) : Config.DefaultSkillInfo(skill, active, color, onlyTeam, needsTeammates) + public class SkillConfig(Skills skill = skillName, bool active = true, string color = "#fbff00", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) : SkillsInfo.DefaultSkillInfo(skill, active, color, onlyTeam, disableOnFreezeTime, needsTeammates) { } } diff --git a/jRandomSkills - SRC Files/src/utils/Config.cs b/jRandomSkills - SRC Files/src/utils/Config.cs index e04aec4..be089e9 100644 --- a/jRandomSkills - SRC Files/src/utils/Config.cs +++ b/jRandomSkills - SRC Files/src/utils/Config.cs @@ -1,9 +1,5 @@ -using CounterStrikeSharp.API.Modules.Utils; -using jRandomSkills.src.player; -using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging; using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Reflection; using static jRandomSkills.jRandomSkills; namespace jRandomSkills @@ -12,111 +8,67 @@ namespace jRandomSkills { private static readonly string configsFolder = Path.Combine(Instance.ModuleDirectory, "configs"); private static readonly string configPath = Path.Combine(configsFolder, "config.json"); - private static ConfigModel config = LoadConfig(); + private static readonly object fileLock = new(); - public static ConfigModel LoadedConfig => config; + private static SettingsModel config = LoadConfig(); + public static SettingsModel LoadedConfig => config; - public static ConfigModel LoadConfig() + public static SettingsModel LoadConfig() { - if (!File.Exists(configPath)) + lock (fileLock) { - Instance.Logger.LogInformation("Config file does not exist. Create a new config file..."); - var defaultConfig = new ConfigModel(); - SaveConfig(defaultConfig); - return defaultConfig; - } + var newConfig = new SettingsModel(); - string json = File.ReadAllText(configPath); - var config = new ConfigModel(); + if (!File.Exists(configPath)) + { + Instance.Logger.LogInformation("Config file does not exist. Create a new config file..."); + SaveConfig(newConfig); + return config = newConfig; + } - try - { - var root = JsonConvert.DeserializeObject(json); - if (root == null) return config; + try + { + string json; + using (var fs = new FileStream(configPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + using (var sr = new StreamReader(fs)) + json = sr.ReadToEnd(); + newConfig = JsonConvert.DeserializeObject(json) ?? new SettingsModel(); + } + catch + { + Instance.Logger.LogError("Error when loading the config file."); + } - var settings = root["Settings"]; - if (settings != null) JsonConvert.PopulateObject(settings.ToString(), config.Settings); - - var skillsArray = (JArray?)root["SkillsInfo"]; - if (skillsArray != null) - foreach (var skillObj in skillsArray) - { - var name = skillObj["Name"]; - if (name == null) continue; - var instance = config.SkillsInfo.FirstOrDefault(x => x.Name == name.ToString()); - if (instance != null) JsonConvert.PopulateObject(skillObj.ToString(), instance); - } - } - catch - { - Instance.Logger.LogError("Error when loading the config file."); - } - - return config; - } - - public static void SaveConfig(ConfigModel config) - { - try - { - Directory.CreateDirectory(configsFolder); - string json = JsonConvert.SerializeObject(config, Formatting.Indented); - File.WriteAllText(configPath, json); - } - catch - { - Instance.Logger.LogError("Error when saving the config file."); + if (newConfig.DisplayAlwaysDescription) + newConfig.SkillDescriptionDuration = 9999; + return config = newConfig; } } - public static T GetValue(object skill, string key) + public static void SaveConfig(SettingsModel config) { - var skillConfig = config.SkillsInfo.FirstOrDefault(s => s.Name == skill.ToString()); - if (skillConfig == null) return default!; - - var prop = skillConfig.GetType().GetProperty(key, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); - if (prop != null) + lock (fileLock) { - var value = prop.GetValue(skillConfig); - if (value == null) return default!; - else return (T)Convert.ChangeType(value, typeof(T)); - } + try + { + Directory.CreateDirectory(configsFolder); + string json = JsonConvert.SerializeObject(config, Formatting.Indented); - var field = skillConfig.GetType().GetField(key, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); - if (field != null) - { - var value = field.GetValue(skillConfig); - if (value == null) return default!; - else return (T)Convert.ChangeType(value, typeof(T)); - } + string tempPath = $"{configPath}.temp"; + File.WriteAllText(tempPath, json); - return default!; - } - - public class ConfigModel - { - public Settings Settings { get; set; } = new Settings(); - public DefaultSkillInfo[] SkillsInfo { get; set; } - - public ConfigModel() - { - SkillsInfo = Assembly.GetExecutingAssembly().GetTypes() - .Where(t => typeof(DefaultSkillInfo).IsAssignableFrom(t) && t.Name == "SkillConfig") - .Select(t => - { - var ctor = t.GetConstructors().FirstOrDefault(c => c.GetParameters().All(p => p.IsOptional)); - if (ctor == null) return null; - var args = ctor.GetParameters().Select(p => Type.Missing).ToArray(); - return ctor.Invoke(args) as DefaultSkillInfo; - }) - .Where(instance => instance != null) - .ToArray()!; + File.Copy(tempPath, configPath, overwrite: true); + File.Delete(tempPath); + } + catch + { + Instance.Logger.LogError("Error when saving the config file."); + } } } - public class Settings + public class SettingsModel { - public string LangCode { get; set; } public int GameMode { get; set; } public bool KillerSkillInfo { get; set; } public bool TeamMateSkillInfo { get; set; } @@ -125,28 +77,17 @@ namespace jRandomSkills public string? AlternativeSkillButton { get; set; } public float SkillTimeBeforeStart { get; set; } public float SkillDescriptionDuration { get; set; } + public bool DisplayAlwaysDescription { get; set; } public bool DisableSpectateHUD { get; set; } public bool FlashingHtmlHudFix { get; set; } public bool CS2TraceRayDebug { get; set; } + public LanguageSystem LanguageSystem { get; set; } + public HtmlHudCustomisation HtmlHudCustomisation { get; set; } + public NormalCommands NormalCommands { get; set; } + public VotingCommands VotingCommands { get; set; } - public NormalCommand SetSkillCommands { get; set; } - public NormalCommand SkillsListCommands { get; set; } - public NormalCommand UseSkillCommands { get; set; } - public NormalCommand HealCommands { get; set; } - public NormalCommand ConsoleCommands { get; set; } - public NormalCommand SetStaticSkillCommands { get; set; } - public NormalCommand ChangeLanguageCommands { get; set; } - public NormalCommand ReloadCommands { get; set; } - public VotingCommand StartGameCommands { get; set; } - public VotingCommand ChangeMapCommands { get; set; } - public VotingCommand SwapCommands { get; set; } - public VotingCommand ShuffleCommands { get; set; } - public VotingCommand PauseCommands { get; set; } - public VotingCommand SetScoreCommands { get; set; } - - public Settings() + public SettingsModel() { - LangCode = "en"; GameMode = (int)GameModes.NoRepeat; KillerSkillInfo = true; TeamMateSkillInfo = true; @@ -155,27 +96,100 @@ namespace jRandomSkills AlternativeSkillButton = null; SkillTimeBeforeStart = 7; SkillDescriptionDuration = 7; + DisplayAlwaysDescription = false; FlashingHtmlHudFix = true; CS2TraceRayDebug = false; DisableSpectateHUD = false; - SetSkillCommands = new NormalCommand("ustawskill, ustaw_skill, setskill, set_skill, definirhabilidade, configurarhabilidade, 设置技能, 配置技能", "@jRandmosSkills/admin"); - SkillsListCommands = new NormalCommand("supermoc, skille, listamocy, supermoce, skills, listaHabilidades, habilidades, 技能列表, 超能力列表", "@jRandmosSkills/admin"); - UseSkillCommands = new NormalCommand("t, useSkill, usarHabilidade, 技能使用, 使用技能", "@jRandmosSkills/admin"); - HealCommands = new NormalCommand("heal, ulecz, curar, tratar, 治疗, 治愈", "@jRandmosSkills/admin"); - ConsoleCommands = new NormalCommand("console, sv, 控制台, 服务器", "@jRandmosSkills/root"); - SetStaticSkillCommands = new NormalCommand("ustawstatycznyskill, ustaw_statyczny_skill, setstaticskill, set_static_skill", "@jRandmosSkills/admin"); - ChangeLanguageCommands = new NormalCommand("lang, language, changelang, change_lang, jezyk, język", ""); - ReloadCommands = new NormalCommand("reload, refresh", "@jRandmosSkills/admin"); + LanguageSystem = new LanguageSystem + { + DefaultLangCode = "en", + DisableGeoLite = false, + LanguageInfos = + [ + new LanguageInfo("CN, TW, HK, MO, SG", "pt-br"), + new LanguageInfo("PT, BR, AO, CV, GW, MZ, ST, TL", "zh"), + new LanguageInfo("FR, MC, HT", "fr"), + new LanguageInfo("PL", "pl"), + new LanguageInfo("GB, US", "en") + ] + }; - StartGameCommands = new VotingCommand(true, "start, go, começar, iniciar, 开始, 启动", "@jRandmosSkills/admin", 15, 60, 15, 500, 2); - ChangeMapCommands = new VotingCommand( true, "map, mapa, changemap, zmienmape, zmienmape, mudarMapa, trocarMapa, 更换地图, 更改地图", "@jRandmosSkills/admin", 25, 90, 15, 500, 2); - SwapCommands = new VotingCommand(true, "swap, zmiana, trocar, 交换, 切换", "@jRandmosSkills/admin", 15, 90, 15, 20, 2); - ShuffleCommands = new VotingCommand(true, "shuffle, embaralhar, 随机排序, 洗牌", "@jRandmosSkills/admin", 15, 90, 15, 20, 2); - PauseCommands = new VotingCommand(true, "pause, unpause, pausar, despausar, 暂停, 恢复", "@jRandmosSkills/admin", 15, 60, 15, 2, 2); - SetScoreCommands = new VotingCommand(true, "setscore, wynik, definirPontuacao, configurarPontos, 设置分数, 调整分数", "@jRandmosSkills/root", 15, 90, 15, 90, 2); + HtmlHudCustomisation = new HtmlHudCustomisation + { + HeaderLineColor = "#FFFFFF", + HeaderLineSize = "ml", + SkillLineSize = "l", + InfoLineColor = "#FFFFFF", + InfoLineSize = "sm", + SkillDescriptionLineColor = "#999999", + SkillDescriptionLineSize = "sm", + WSADMenuSelectInfoLineColor = "#999999", + WSADMenuSelectInfoLineSize = "sm", + WSADMenuItemLineColor = "white", + WSADMenuItemHoverLineColor = "orange", + WSADMenuItemLineSize = "sm", + WSADMenuControllsLineSize = "sm", + WSADMenuControllsLineColor1 = "cyan", + WSADMenuControllsLineColor2 = "white", + WSADMenuControllsLineColor3 = "green", + }; + + NormalCommands = new NormalCommands + { + SetSkillCommand = new NormalCommand("ustawskill, ustaw_skill, setskill, set_skill, definirhabilidade, configurarhabilidade, 设置技能, 配置技能", "@jRandmosSkills/admin"), + SkillsListCommand = new NormalCommand("supermoc, skille, listamocy, supermoce, skills, listaHabilidades, habilidades, 技能列表, 超能力列表", "@jRandmosSkills/admin"), + UseSkillCommand = new NormalCommand("t, useSkill, usarHabilidade, 技能使用, 使用技能", "@jRandmosSkills/admin"), + HealCommand = new NormalCommand("heal, ulecz, curar, tratar, 治疗, 治愈", "@jRandmosSkills/admin"), + ConsoleCommand = new NormalCommand("console, sv, 控制台, 服务器", "@jRandmosSkills/root"), + SetStaticSkillCommand = new NormalCommand("ustawstatycznyskill, ustaw_statyczny_skill, setstaticskill, set_static_skill", "@jRandmosSkills/admin"), + ChangeLanguageCommand = new NormalCommand("lang, language, changelang, change_lang, jezyk, język", ""), + ReloadCommand = new NormalCommand("reload, refresh", "@jRandmosSkills/admin"), + }; + + VotingCommands = new VotingCommands + { + StartGameCommand = new StartGameCommand(true, "start, go, começar, iniciar, 开始, 启动", "@jRandmosSkills/admin", "mp_freezetime 15; mp_forcecamera 0; mp_overtime_enable 1; sv_cheats 0", "mp_freezetime 0; mp_forcecamera 0; mp_overtime_enable 1; sv_cheats 1", 15, 60, 15, 500, 2), + ChangeMapCommand = new VotingCommand(true, "map, mapa, changemap, zmienmape, zmienmape, mudarMapa, trocarMapa, 更换地图, 更改地图", "@jRandmosSkills/admin", 25, 90, 15, 500, 2), + SwapCommand = new VotingCommand(true, "swap, zmiana, trocar, 交换, 切换", "@jRandmosSkills/admin", 15, 90, 15, 20, 2), + ShuffleCommand = new VotingCommand(true, "shuffle, embaralhar, 随机排序, 洗牌", "@jRandmosSkills/admin", 15, 90, 15, 20, 2), + PauseCommand = new VotingCommand(true, "pause, unpause, pausar, despausar, 暂停, 恢复", "@jRandmosSkills/admin", 15, 60, 15, 2, 2), + SetScoreCommand = new VotingCommand(true, "setscore, wynik, definirPontuacao, configurarPontos, 设置分数, 调整分数", "@jRandmosSkills/root", 15, 90, 15, 90, 2), + }; } + } + public class HtmlHudCustomisation + { + public required string HeaderLineColor { get; set; } + public required string HeaderLineSize { get; set; } + public required string SkillLineSize { get; set; } + public required string InfoLineColor { get; set; } + public required string InfoLineSize { get; set; } + public required string SkillDescriptionLineColor { get; set; } + public required string SkillDescriptionLineSize { get; set; } + public required string WSADMenuSelectInfoLineColor { get; set; } + public required string WSADMenuSelectInfoLineSize { get; set; } + public required string WSADMenuItemLineColor { get; set; } + public required string WSADMenuItemHoverLineColor { get; set; } + public required string WSADMenuItemLineSize { get; set; } + public required string WSADMenuControllsLineSize { get; set; } + public required string WSADMenuControllsLineColor1 { get; set; } + public required string WSADMenuControllsLineColor2 { get; set; } + public required string WSADMenuControllsLineColor3 { get; set; } + } + + public class LanguageSystem + { + public required string DefaultLangCode { get; set; } + public required bool DisableGeoLite { get; set; } + public required LanguageInfo[] LanguageInfos { get; set; } + } + + public class LanguageInfo(string isoCodes, string fileName) + { + public string IsoCodes { get; set; } = isoCodes; + public string FileName { get; set; } = fileName; } public class NormalCommand(string alias, string permissions) @@ -184,6 +198,18 @@ namespace jRandomSkills public string Permissions { get; set; } = permissions; } + public class NormalCommands + { + public required NormalCommand SetSkillCommand { get; set; } + public required NormalCommand SkillsListCommand { get; set; } + public required NormalCommand UseSkillCommand { get; set; } + public required NormalCommand HealCommand { get; set; } + public required NormalCommand ConsoleCommand { get; set; } + public required NormalCommand SetStaticSkillCommand { get; set; } + public required NormalCommand ChangeLanguageCommand { get; set; } + public required NormalCommand ReloadCommand { get; set; } + } + public class VotingCommand(bool enableVoting, string alias, string permissions, float timeToVote, float percentagesToSuccess, float timeToNextVoting, float timeToNextSameVoting, int minimumPlayersToStartVoting) : NormalCommand(alias, permissions) { public bool EnableVoting { get; set; } = enableVoting; @@ -194,13 +220,28 @@ namespace jRandomSkills public int MinimumPlayersToStartVoting { get; set; } = minimumPlayersToStartVoting; } - public class DefaultSkillInfo(Skills skill, bool active = true, string color = "#ffffff", CsTeam onlyTeam = CsTeam.None, bool needsTeammates = false) + public class StartGameCommand(bool enableVoting, string alias, string permissions, string startParams, string svStartParams, float timeToVote, float percentagesToSuccess, float timeToNextVoting, float timeToNextSameVoting, int minimumPlayersToStartVoting) { - public bool NeedsTeammates { get; set; } = needsTeammates; - public int OnlyTeam { get; set; } = (int)onlyTeam; - public string Color { get; set; } = color; - public bool Active { get; set; } = active; - public string Name { get; set; } = skill.ToString(); + public bool EnableVoting { get; set; } = enableVoting; + public string Alias { get; set; } = alias; + public string Permissions { get; set; } = permissions; + public string StartParams { get; set; } = startParams; + public string SVStartParams { get; set; } = svStartParams; + public float TimeToVote { get; set; } = timeToVote; + public float PercentagesToSuccess { get; set; } = percentagesToSuccess; + public float TimeToNextVoting { get; set; } = timeToNextVoting; + public float TimeToNextSameVoting { get; set; } = timeToNextSameVoting; + public int MinimumPlayersToStartVoting { get; set; } = minimumPlayersToStartVoting; + } + + public class VotingCommands + { + public required StartGameCommand StartGameCommand { get; set; } + public required VotingCommand ChangeMapCommand { get; set; } + public required VotingCommand SwapCommand { get; set; } + public required VotingCommand ShuffleCommand { get; set; } + public required VotingCommand PauseCommand { get; set; } + public required VotingCommand SetScoreCommand { get; set; } } public enum GameModes diff --git a/jRandomSkills - SRC Files/src/utils/Localization.cs b/jRandomSkills - SRC Files/src/utils/Localization.cs index 8d6d276..55e38d2 100644 --- a/jRandomSkills - SRC Files/src/utils/Localization.cs +++ b/jRandomSkills - SRC Files/src/utils/Localization.cs @@ -5,21 +5,20 @@ using jRandomSkills.src.player; using MaxMind.Db; using Newtonsoft.Json; using System.Net; +using System.Collections.Concurrent; +using System.Text.RegularExpressions; namespace jRandomSkills.src.utils { public static class Localization { private static readonly string languagesFolderPath = Path.Combine(jRandomSkills.Instance.ModuleDirectory, "languages"); - private static readonly Dictionary> _translations = []; + private static readonly ConcurrentDictionary> _translations = []; private static readonly string playersLanguageFileName = "playersLanguage.json"; private static readonly string configsFolderPath = Path.Combine(jRandomSkills.Instance.ModuleDirectory, "configs"); - private static Dictionary _playersLanguage = []; + private static ConcurrentDictionary _playersLanguage = []; private static readonly string geoliteFilePath = Path.Combine(jRandomSkills.Instance.ModuleDirectory, "packages", "GeoLite2-Country.mmdb"); - public static readonly string[] chinaIsoCodes = ["CN", "TW", "HK", "MO", "SG"]; - public static readonly string[] portugalIsoCodes = ["PT", "BR", "AO", "CV", "GW", "MZ", "ST", "TL"]; - public static readonly string[] frenchIsoCodes = ["FR", "MC", "HT"]; private static string defaultLangCode = "en"; @@ -34,7 +33,7 @@ namespace jRandomSkills.src.utils private static void SetLangCode() { - defaultLangCode = Config.LoadedConfig.Settings.LangCode; + defaultLangCode = Config.LoadedConfig.LanguageSystem.DefaultLangCode; } private static void LoadAllLanguages() @@ -51,29 +50,67 @@ namespace jRandomSkills.src.utils if (!File.Exists(langPath)) return; - var code = Path.GetFileNameWithoutExtension(langPath); + var code = Path.GetFileNameWithoutExtension(langPath).ToLower(); var jsonText = File.ReadAllText(langPath); - var translations = JsonConvert.DeserializeObject>(jsonText); + var translations = JsonConvert.DeserializeObject>(jsonText); if (translations != null) - _translations[code] = translations; + _translations.AddOrUpdate(code, translations, (k, v) => translations); } public static bool HasTranslation(string code) { - return _translations.ContainsKey(code); + return _translations.ContainsKey(code.ToLower()); } - public static string GetSkillName(this CCSPlayerController player, Skills skill) + public static string GetSkillName(this CCSPlayerController player, Skills skill, float? chance = null) { string langCode = GetLangCode(player); - return GetTranslation(skill.ToString().ToLower(), langCode); + if (chance == null) + { + var translation = GetTranslation(skill.ToString().ToLower(), langCode); + if (!translation.Contains("{0}")) + return translation; + + if (!translation.Contains(' ')) + return translation.Replace("{0}", "").Trim(); + + var parts = translation.Split(' ', StringSplitOptions.RemoveEmptyEntries); + var filtered = parts.Where(p => !p.Contains("{0}")); + return string.Join(' ', filtered); + } + + var value = Math.Round((double)(chance ?? 1), 2); + var skillNameText = GetTranslation(skill.ToString().ToLower(), langCode, value); + if (skillNameText.Contains('%')) skillNameText = skillNameText.Replace(value.ToString(), Math.Round(value * 100, 0).ToString()); + return skillNameText; } - public static string GetSkillDescription(this CCSPlayerController player, Skills skill) + public static string GetSkillDescription(this CCSPlayerController player, Skills skill, float? chance = null) { string langCode = GetLangCode(player); - return GetTranslation($"{skill.ToString().ToLower()}_desc", langCode); + if (chance == null) + { + var translation = GetTranslation($"{skill.ToString().ToLower()}_desc", langCode); + if (!translation.Contains("{0}")) + return translation; + + if (!translation.Contains(' ')) + return translation.Replace("{0}", "").Trim(); + + var parts = translation.Split(' ', StringSplitOptions.RemoveEmptyEntries); + var filtered = parts.Where(p => !p.Contains("{0}")); + return string.Join(' ', filtered); + } + + var skillName = $"{skill.ToString().ToLower()}_desc2"; + var value = Math.Round((double)(chance ?? 1), 2); + var desc2 = GetTranslation(skillName, langCode, value); + + var skilLDescription = desc2 == skillName + ? player.GetTranslation($"{skill.ToString().ToLower()}_desc") + : (desc2.Contains('%') ? desc2.Replace(value.ToString(), Math.Round(value * 100, 0).ToString()) : desc2); + return skilLDescription; } public static void PrintTranslationToChatAll(string message, string[]? key, params object[][]? args) @@ -112,9 +149,10 @@ namespace jRandomSkills.src.utils return translation; else { - string output = string.Format(translation, args).Replace("CHATCOLORS.RED", ChatColors.Red.ToString()); - if (Config.LoadedConfig.Settings.AlternativeSkillButton != null) - output = output.Replace("css_useSkill", $"css_useSkill/{Config.LoadedConfig.Settings.AlternativeSkillButton}"); + string output = args.Length == 0 ? translation : string.Format(translation, args); + output = output.Replace("CHATCOLORS.RED", ChatColors.Red.ToString()); + if (Config.LoadedConfig.AlternativeSkillButton != null) + output = output.Replace("css_useSkill", $"css_useSkill/{Config.LoadedConfig.AlternativeSkillButton}"); return output; } @@ -129,6 +167,9 @@ namespace jRandomSkills.src.utils if (!string.IsNullOrEmpty(fileLangCode)) return fileLangCode; + if (Config.LoadedConfig.LanguageSystem.DisableGeoLite == true) + return defaultLangCode; + string? geoliteLandCode = GetLangCodeFromDatabase(GetPlayerIP(player)) ?? defaultLangCode; ChangePlayerLanguage(player, geoliteLandCode); return geoliteLandCode; @@ -149,21 +190,18 @@ namespace jRandomSkills.src.utils if (!File.Exists(geoliteFilePath)) return null; using var reader = new Reader(geoliteFilePath); var ip = IPAddress.Parse(playerIP); - var data = reader.Find>(ip); - if (data == null || data.Count == 0) return null; + var data = reader.Find>(ip); + if (data == null || data.IsEmpty) return null; - if (data.TryGetValue("country", out var _country) && _country is Dictionary country) + if (data.TryGetValue("country", out var _country) && _country is ConcurrentDictionary country) if (country.TryGetValue("iso_code", out var _isoCode) && _isoCode is string isoCode) { - if (portugalIsoCodes.Contains(isoCode)) - isoCode = "pt-br"; - if (chinaIsoCodes.Contains(isoCode)) - isoCode = "zh"; - if (frenchIsoCodes.Contains(isoCode)) - isoCode = "fr"; - isoCode = isoCode.ToLower(); - if (_translations.ContainsKey(isoCode)) - return isoCode; + string fileName = Config.LoadedConfig.LanguageSystem.DefaultLangCode; + foreach (var langInfo in Config.LoadedConfig.LanguageSystem.LanguageInfos) + if (langInfo.IsoCodes.Contains(isoCode)) + fileName = langInfo.FileName; + if (_translations.ContainsKey(fileName)) + return fileName; } return null; } @@ -175,7 +213,7 @@ namespace jRandomSkills.src.utils return; var jsonText = File.ReadAllText(filePath); - _playersLanguage = JsonConvert.DeserializeObject>(jsonText) ?? []; + _playersLanguage = JsonConvert.DeserializeObject>(jsonText) ?? []; } private static void SavePlayersLanguage() @@ -198,7 +236,7 @@ namespace jRandomSkills.src.utils public static void ChangePlayerLanguage(CCSPlayerController? player, string language) { if (player == null || !player.IsValid) return; - _playersLanguage[player.SteamID] = language; + _playersLanguage.AddOrUpdate(player.SteamID, language, (k,v) => language); SavePlayersLanguage(); } } diff --git a/jRandomSkills - SRC Files/src/utils/SkillUtils.cs b/jRandomSkills - SRC Files/src/utils/SkillUtils.cs index 08bba31..6f7d2e4 100644 --- a/jRandomSkills - SRC Files/src/utils/SkillUtils.cs +++ b/jRandomSkills - SRC Files/src/utils/SkillUtils.cs @@ -11,17 +11,27 @@ using System.Runtime.InteropServices; using WASDMenuAPI.Classes; using WASDSharedAPI; using static CounterStrikeSharp.API.Core.Listeners; +using System.Collections.Concurrent; +using System; +using static System.Net.Mime.MediaTypeNames; +using System.Drawing; namespace jRandomSkills { public static class SkillUtils { + private static readonly MemoryFunctionWithReturn HEGrenadeProjectile_CreateFunc = new(GameData.GetSignature("HEGrenadeProjectile_CreateFunc")); private static readonly MemoryFunctionVoid TerminateRoundFunc = new(GameData.GetSignature("CCSGameRules_TerminateRound")); public static void PrintToChat(CCSPlayerController player, string msg, bool isError) { string checkIcon = isError ? $"{ChatColors.DarkRed}✖{ChatColors.LightRed}" : $"{ChatColors.Green}✔{ChatColors.Lime}"; - player.PrintToChat($" {ChatColors.DarkRed}► {ChatColors.Green}[{ChatColors.DarkRed} jRadnomSkills {ChatColors.Green}] {checkIcon} {msg}"); + player.PrintToChat($" {ChatColors.DarkRed}► {ChatColors.Green}[{ChatColors.DarkRed} {jRandomSkills.Tag} {ChatColors.Green}] {checkIcon} {msg}"); + } + + public static bool IsFreezeTime() + { + return jRandomSkills.Instance?.GameRules?.FreezePeriod == true; } public static void RegisterSkill(Skills skill, string color, bool display = true) @@ -76,10 +86,14 @@ namespace jRandomSkills var playerPawn = player.PlayerPawn.Value; if (playerPawn == null || !playerPawn.IsValid || playerPawn.CBodyComponent == null || playerPawn.CBodyComponent.SceneNode == null) return; - playerPawn.CBodyComponent.SceneNode.Scale = scale; playerPawn.CBodyComponent.SceneNode.GetSkeletonInstance().Scale = scale; - playerPawn.AcceptInput("SetScale", null, null, scale.ToString()); - Server.NextFrame(() => Utilities.SetStateChanged(playerPawn, "CBaseEntity", "m_CBodyComponent")); + Utilities.SetStateChanged(playerPawn, "CBaseEntity", "m_CBodyComponent"); + Server.NextFrame(() => playerPawn.AcceptInput("SetScale", playerPawn, playerPawn, scale.ToString())); + } + + public static void CreateHEGrenadeProjectile(Vector pos, QAngle angle, Vector vel, int teamNum) + { + HEGrenadeProjectile_CreateFunc.Invoke(pos.Handle, angle.Handle, vel.Handle, vel.Handle, IntPtr.Zero, 44, teamNum); } public static void TakeHealth(CCSPlayerPawn? pawn, int damage) @@ -98,6 +112,13 @@ namespace jRandomSkills }); } + public static void ResetPrintHTML(CCSPlayerController? player) + { + var playerInfo = jRandomSkills.Instance.SkillPlayer.FirstOrDefault(s => s.SteamID == player?.SteamID); + if (playerInfo == null) return; + playerInfo.PrintHTML = null; + } + public static void AddHealth(CCSPlayerPawn? pawn, int extraHealth, int maxHealth = 100) { if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) @@ -159,7 +180,7 @@ namespace jRandomSkills return manager.HasMenu(player); } - public static void UpdateMenu(CCSPlayerController? player, HashSet<(string, string)> items) + public static void UpdateMenu(CCSPlayerController? player, ConcurrentBag<(string, string)> items) { if (player == null) return; @@ -171,7 +192,7 @@ namespace jRandomSkills Dictionary> list = []; foreach (var item in items) - list.Add(item.Item1, (p, option) => + list.TryAdd(item.Item1, (p, option) => { jRandomSkills.Instance.SkillAction(playerInfo.Skill.ToString(), "TypeSkill", [p, new[] { item.Item2 }]); manager.CloseMenu(p); @@ -180,7 +201,7 @@ namespace jRandomSkills manager.UpdateActiveMenu(player, list); } - public static void CreateMenu(CCSPlayerController? player, HashSet<(string, string)> enemies) + public static void CreateMenu(CCSPlayerController? player, ConcurrentBag<(string, string)> enemies, (string, string)? lastElement = null) { if (player == null || !player.IsValid) return; @@ -190,20 +211,47 @@ namespace jRandomSkills var skillData = SkillData.Skills.FirstOrDefault(s => s.Skill == playerInfo.Skill); if (skillData == null) return; - string infoLine = $"{player.GetTranslation("your_skill")}:
"; - string skillLine = $"{player.GetSkillName(skillData.Skill)}
" - + $"{player.GetTranslation($"{playerInfo.Skill.ToString().ToLower()}_select_info")}"; - var manager = GetMenuManager(); if (manager == null) return; - IWasdMenu menu = manager.CreateMenu(infoLine + skillLine, "W/S - Scroll | E - Select
"); + var config = Config.LoadedConfig.HtmlHudCustomisation; + var your_skill = player.GetTranslation("your_skill"); + var emptySymbol = $" "; + + string infoLine = string.IsNullOrEmpty(your_skill) + ? "" + : $"{your_skill}:
"; + + string skillLine = $"{player.GetSkillName(skillData.Skill)}
"; + + var skill_select_info = player.GetTranslation($"{playerInfo.Skill.ToString().ToLower()}_select_info"); + string remainingLine = string.IsNullOrWhiteSpace(skill_select_info) + ? "" + : $"{skill_select_info}
"; + + var hudContent = infoLine + skillLine + remainingLine; + + string controllsLine = + $"{emptySymbol}{player.GetTranslation($"menu_controlls_scroll")}" + + $"{player.GetTranslation($"menu_controlls_padding")}" + + $"{player.GetTranslation($"menu_controlls_select")}{emptySymbol}"; + + string itemText = $"{{0}}
"; + string itemHoverText = $"[ {{0}} ]
"; + + IWasdMenu menu = manager.CreateMenu(hudContent, itemText, itemHoverText, controllsLine); foreach (var enemy in enemies) menu.Add(enemy.Item1, (p, option) => { jRandomSkills.Instance.SkillAction(playerInfo.Skill.ToString(), "TypeSkill", [p, new[] { enemy.Item2 }]); manager.CloseMenu(p); }); + if (lastElement != null) + menu.Add(lastElement.Value.Item1, (p, option) => + { + jRandomSkills.Instance.SkillAction(playerInfo.Skill.ToString(), "TypeSkill", [p, new[] { lastElement.Value.Item2 }]); + manager.CloseMenu(p); + }); manager.OpenMainMenu(player, menu); } diff --git a/jRandomSkills - SRC Files/src/utils/SkillsInfo.cs b/jRandomSkills - SRC Files/src/utils/SkillsInfo.cs new file mode 100644 index 0000000..d803fbb --- /dev/null +++ b/jRandomSkills - SRC Files/src/utils/SkillsInfo.cs @@ -0,0 +1,139 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Modules.Utils; +using jRandomSkills.src.player; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System.Reflection; +using static jRandomSkills.jRandomSkills; + +namespace jRandomSkills +{ + public static class SkillsInfo + { + private static readonly string configsFolder = Path.Combine(Instance.ModuleDirectory, "configs"); + private static readonly string configPath = Path.Combine(configsFolder, "skillsInfo.json"); + private static readonly object fileLock = new(); + + private static SkillsInfoModel config = LoadSkillsInfo(); + public static SkillsInfoModel LoadedConfig => config; + + public static SkillsInfoModel LoadSkillsInfo() + { + lock (fileLock) + { + var newConfig = new SkillsInfoModel(); + + if (!File.Exists(configPath)) + { + Instance.Logger.LogInformation("Config file does not exist. Create a new skills info file..."); + SaveConfig(newConfig); + return config = newConfig; + } + + try + { + string json; + using (var fs = new FileStream(configPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + using (var sr = new StreamReader(fs)) + json = sr.ReadToEnd(); + + var root = JsonConvert.DeserializeObject(json); + if (root != null) + foreach (var skillObj in root) + { + var name = skillObj["Name"]?.ToString(); + if (string.IsNullOrEmpty(name)) continue; + + var instance = newConfig.FirstOrDefault(x => x.Name == name.ToString()); + if (instance != null) JsonConvert.PopulateObject(skillObj.ToString(), instance); + } + } + catch + { + Instance.Logger.LogError("Error when loading the skills info file."); + } + + return config = newConfig; + } + } + + public static void SaveConfig(SkillsInfoModel config) + { + lock (fileLock) + { + try + { + Directory.CreateDirectory(configsFolder); + string json = JsonConvert.SerializeObject(config, Formatting.Indented); + + string tempPath = $"{configPath}.temp"; + File.WriteAllText(tempPath, json); + + File.Copy(tempPath, configPath, overwrite: true); + File.Delete(tempPath); + } + catch + { + Instance.Logger.LogError("Error when saving the skills info file."); + } + } + } + + public static T GetValue(object skill, string key) + { + if (config == null) return default!; + + var skillConfig = LoadedConfig.FirstOrDefault(s => s.Name == skill.ToString()); + if (skillConfig == null) return default!; + + var prop = skillConfig.GetType().GetProperty(key, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); + if (prop != null) + { + var value = prop.GetValue(skillConfig); + if (value == null) return default!; + else return (T)Convert.ChangeType(value, typeof(T)); + } + + var field = skillConfig.GetType().GetField(key, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase); + if (field != null) + { + var value = field.GetValue(skillConfig); + if (value == null) return default!; + else return (T)Convert.ChangeType(value, typeof(T)); + } + + return default!; + } + + public class SkillsInfoModel : List + { + public SkillsInfoModel() + { + AddRange( + Assembly.GetExecutingAssembly().GetTypes() + .Where(t => typeof(DefaultSkillInfo).IsAssignableFrom(t) && t.Name == "SkillConfig") + .Select(t => + { + var ctor = t.GetConstructors().FirstOrDefault(c => c.GetParameters().All(p => p.IsOptional)); + if (ctor == null) return null; + var args = ctor.GetParameters().Select(p => Type.Missing).ToArray(); + return ctor.Invoke(args) as DefaultSkillInfo; + }) + .Where(instance => instance != null) + .Cast() + ); + } + } + + public class DefaultSkillInfo(Skills skill, bool active = true, string color = "#ffffff", CsTeam onlyTeam = CsTeam.None, bool disableOnFreezeTime = false, bool needsTeammates = false) + { + public bool NeedsTeammates { get; set; } = needsTeammates; + public bool DisableOnFreezeTime { get; set; } = disableOnFreezeTime; + public int OnlyTeam { get; set; } = (int)onlyTeam; + public string Color { get; set; } = color; + public bool Active { get; set; } = active; + public string Name { get; set; } = skill.ToString(); + } + } +} \ No newline at end of file diff --git a/jRandomSkills - Server Files/gamedata/jRandomSkills.gamedata.json b/jRandomSkills - Server Files/gamedata/jRandomSkills.gamedata.json new file mode 100644 index 0000000..d5325b2 --- /dev/null +++ b/jRandomSkills - Server Files/gamedata/jRandomSkills.gamedata.json @@ -0,0 +1,16 @@ +{ + "HEGrenadeProjectile_CreateFunc": { + "signatures": { + "library": "server", + "windows": "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 48 83 EC 40 48 8B 6C 24", + "linux": "55 4C 89 C1 48 89 E5 41 57 49 89 FF 41 56 49 89" + } + }, + "Shoot_Secondary": { + "signatures": { + "library": "server", + "windows": "48 89 5C 24 ? 57 48 83 EC 20 44 0F BF C2", + "linux": "55 48 89 E5 41 54 53 48 89 FB 66 83 FE 65" + } + } +} \ No newline at end of file diff --git a/jRandomSkills - Server Files/plugins/!jRandomSkills/!jRandomSkills.dll b/jRandomSkills - Server Files/plugins/!jRandomSkills/!jRandomSkills.dll index 0b4edf6bcda6103407aa4cb439191b02a2326fe2..d58ab4f5877f73d2eb577ff98a9ffd0ecbb5f2ed 100644 GIT binary patch literal 401920 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

T(VNqK*MRPWMmi7;;~tBoi(d`Nf_NKN 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 J|Ibn2e*-*z<|P0C literal 373760 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%0Aj@R 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 g?1bH$Qxc2|r!2W+cZzTf%1xxc22><{9 delta 7504 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 diff --git a/jRandomSkills - Server Files/plugins/!jRandomSkills/configs/config.json b/jRandomSkills - Server Files/plugins/!jRandomSkills/configs/config.json index 13349a6..bc28c42 100644 --- a/jRandomSkills - Server Files/plugins/!jRandomSkills/configs/config.json +++ b/jRandomSkills - Server Files/plugins/!jRandomSkills/configs/config.json @@ -1,60 +1,108 @@ { - "Settings": { - "LangCode": "en", - "GameMode": 3, - "KillerSkillInfo": true, - "TeamMateSkillInfo": true, - "SummaryAfterTheRound": true, - "DebugMode": true, - "AlternativeSkillButton": null, - "SkillTimeBeforeStart": 7.0, - "SkillDescriptionDuration": 7.0, - "DisableSpectateHUD": false, - "FlashingHtmlHudFix": true, - "CS2TraceRayDebug": false, - "SetSkillCommands": { + "GameMode": 3, + "KillerSkillInfo": true, + "TeamMateSkillInfo": true, + "SummaryAfterTheRound": true, + "DebugMode": true, + "AlternativeSkillButton": null, + "SkillTimeBeforeStart": 7.0, + "SkillDescriptionDuration": 7.0, + "DisplayAlwaysDescription": false, + "DisableSpectateHUD": false, + "FlashingHtmlHudFix": true, + "CS2TraceRayDebug": false, + "LanguageSystem": { + "DefaultLangCode": "en", + "DisableGeoLite": false, + "LanguageInfos": [ + { + "IsoCodes": "CN, TW, HK, MO, SG", + "FileName": "pt-br" + }, + { + "IsoCodes": "PT, BR, AO, CV, GW, MZ, ST, TL", + "FileName": "zh" + }, + { + "IsoCodes": "FR, MC, HT", + "FileName": "fr" + }, + { + "IsoCodes": "PL", + "FileName": "pl" + }, + { + "IsoCodes": "GB, US", + "FileName": "en" + } + ] + }, + "HtmlHudCustomisation": { + "HeaderLineColor": "#FFFFFF", + "HeaderLineSize": "ml", + "SkillLineSize": "l", + "InfoLineColor": "#FFFFFF", + "InfoLineSize": "sm", + "SkillDescriptionLineColor": "#999999", + "SkillDescriptionLineSize": "sm", + "WSADMenuSelectInfoLineColor": "#999999", + "WSADMenuSelectInfoLineSize": "sm", + "WSADMenuItemLineColor": "white", + "WSADMenuItemHoverLineColor": "orange", + "WSADMenuItemLineSize": "sm", + "WSADMenuControllsLineSize": "sm", + "WSADMenuControllsLineColor1": "cyan", + "WSADMenuControllsLineColor2": "white", + "WSADMenuControllsLineColor3": "green" + }, + "NormalCommands": { + "SetSkillCommand": { "Alias": "ustawskill, ustaw_skill, setskill, set_skill, definirhabilidade, configurarhabilidade, 设置技能, 配置技能", "Permissions": "@jRandmosSkills/admin" }, - "SkillsListCommands": { + "SkillsListCommand": { "Alias": "supermoc, skille, listamocy, supermoce, skills, listaHabilidades, habilidades, 技能列表, 超能力列表", "Permissions": "@jRandmosSkills/admin" }, - "UseSkillCommands": { + "UseSkillCommand": { "Alias": "t, useSkill, usarHabilidade, 技能使用, 使用技能", "Permissions": "@jRandmosSkills/admin" }, - "HealCommands": { + "HealCommand": { "Alias": "heal, ulecz, curar, tratar, 治疗, 治愈", "Permissions": "@jRandmosSkills/admin" }, - "ConsoleCommands": { + "ConsoleCommand": { "Alias": "console, sv, 控制台, 服务器", "Permissions": "@jRandmosSkills/root" }, - "SetStaticSkillCommands": { + "SetStaticSkillCommand": { "Alias": "ustawstatycznyskill, ustaw_statyczny_skill, setstaticskill, set_static_skill", "Permissions": "@jRandmosSkills/admin" }, - "ChangeLanguageCommands": { + "ChangeLanguageCommand": { "Alias": "lang, language, changelang, change_lang, jezyk, język", "Permissions": "" }, - "ReloadCommands": { + "ReloadCommand": { "Alias": "reload, refresh", "Permissions": "@jRandmosSkills/admin" - }, - "StartGameCommands": { + } + }, + "VotingCommands": { + "StartGameCommand": { "EnableVoting": true, + "Alias": "start, go, começar, iniciar, 开始, 启动", + "Permissions": "@jRandmosSkills/admin", + "StartParams": "mp_freezetime 15; mp_forcecamera 0; mp_overtime_enable 1; sv_cheats 0", + "SVStartParams": "mp_freezetime 0; mp_forcecamera 0; mp_overtime_enable 1; sv_cheats 1", "TimeToVote": 15.0, "PercentagesToSuccess": 60.0, "TimeToNextVoting": 15.0, "TimeToNextSameVoting": 500.0, - "MinimumPlayersToStartVoting": 2, - "Alias": "start, go, começar, iniciar, 开始, 启动", - "Permissions": "@jRandmosSkills/admin" + "MinimumPlayersToStartVoting": 2 }, - "ChangeMapCommands": { + "ChangeMapCommand": { "EnableVoting": true, "TimeToVote": 25.0, "PercentagesToSuccess": 90.0, @@ -64,7 +112,7 @@ "Alias": "map, mapa, changemap, zmienmape, zmienmape, mudarMapa, trocarMapa, 更换地图, 更改地图", "Permissions": "@jRandmosSkills/admin" }, - "SwapCommands": { + "SwapCommand": { "EnableVoting": true, "TimeToVote": 15.0, "PercentagesToSuccess": 90.0, @@ -74,7 +122,7 @@ "Alias": "swap, zmiana, trocar, 交换, 切换", "Permissions": "@jRandmosSkills/admin" }, - "ShuffleCommands": { + "ShuffleCommand": { "EnableVoting": true, "TimeToVote": 15.0, "PercentagesToSuccess": 90.0, @@ -84,7 +132,7 @@ "Alias": "shuffle, embaralhar, 随机排序, 洗牌", "Permissions": "@jRandmosSkills/admin" }, - "PauseCommands": { + "PauseCommand": { "EnableVoting": true, "TimeToVote": 15.0, "PercentagesToSuccess": 60.0, @@ -94,7 +142,7 @@ "Alias": "pause, unpause, pausar, despausar, 暂停, 恢复", "Permissions": "@jRandmosSkills/admin" }, - "SetScoreCommands": { + "SetScoreCommand": { "EnableVoting": true, "TimeToVote": 15.0, "PercentagesToSuccess": 90.0, @@ -104,904 +152,5 @@ "Alias": "setscore, wynik, definirPontuacao, configurarPontos, 设置分数, 调整分数", "Permissions": "@jRandmosSkills/root" } - }, - "SkillsInfo": [ - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#ff0000", - "Active": true, - "Name": "Aimbot" - }, - { - "SecondsInBack": 5, - "Cooldown": 15.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#a86eff", - "Active": true, - "Name": "Anomaly" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#D6E6FF", - "Active": true, - "Name": "AntyFlash" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#8B4513", - "Active": true, - "Name": "AntyHead" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 3, - "Color": "#edf5b5", - "Active": true, - "Name": "AreaReaper" - }, - { - "ChanceFrom": 0.65, - "ChanceTo": 0.85, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#d1430a", - "Active": true, - "Name": "Armored" - }, - { - "DamageMultiplier": 2.0, - "ToleranceDeg": 45.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#d9d9d9", - "Active": true, - "Name": "Assassin" - }, - { - "ChanceFrom": 0.1, - "ChanceTo": 0.7, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#7E10AD", - "Active": true, - "Name": "Astronaut" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#abab33", - "Active": true, - "Name": "Bankrupt" - }, - { - "SpeedMultipier": 2.0, - "MaxSpeed": 900.0, - "DamageDeal": 9999.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#2effc7", - "Active": true, - "Name": "Baseball" - }, - { - "ChanceFrom": 0.2, - "ChanceTo": 0.4, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#00FF00", - "Active": true, - "Name": "Behind" - }, - { - "TorseReflectionChance": 0.95, - "LegReflectionChance": 0.8, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#cc7504", - "Active": true, - "Name": "BladeMaster" - }, - { - "MaxSpeed": 500.0, - "JumpVelocity": 300.0, - "JumpBoost": 2.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#d1430a", - "Active": true, - "Name": "BunnyHop" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 2, - "Color": "#00911f", - "Active": true, - "Name": "C4Camouflage" - }, - { - "ChanceFrom": 0.2, - "ChanceTo": 0.4, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#FF4500", - "Active": true, - "Name": "Catapult" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#FF8B42", - "Active": true, - "Name": "Chicken" - }, - { - "BombArmedTime": 10.0, - "NeedsTeammates": false, - "OnlyTeam": 3, - "Color": "#343deb", - "Active": true, - "Name": "ChillOut" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#88a31a", - "Active": true, - "Name": "Cutter" - }, - { - "Brightness": 0.01, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#383838", - "Active": true, - "Name": "Darkness" - }, - { - "JumpVelocity": 150.0, - "PushVelocity": 600.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#42bbfc", - "Active": true, - "Name": "Dash" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#919191", - "Active": true, - "Name": "Deactivator" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#dae01f", - "Active": true, - "Name": "Deaf" - }, - { - "ChanceFrom": 0.2, - "ChanceTo": 0.5, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#FF4500", - "Active": true, - "Name": "Disarmament" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#00f2ff", - "Active": true, - "Name": "Distancer" - }, - { - "HealthRegainScale": 0.3, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#FA050D", - "Active": true, - "Name": "Dracula" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#ffb73b", - "Active": true, - "Name": "Duplicator" - }, - { - "MinScale": 0.6, - "MaxScale": 0.95, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#ffff00", - "Active": true, - "Name": "Dwarf" - }, - { - "Cooldown": 16.0, - "Amplitude": 15.0, - "Frequency": 500.0, - "Duration": 8.0, - "Radius": 50.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#42f59b", - "Active": false, - "Name": "Earthquake" - }, - { - "Cooldown": 15.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#ff8c92", - "Active": true, - "Name": "EnemySpawn" - }, - { - "Damage": 25.0, - "DamageRadius": 210.0, - "ChanceFrom": 0.15, - "ChanceTo": 0.3, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#9c0000", - "Active": true, - "Name": "ExplosiveShot" - }, - { - "Distance": 1000.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#d1f542", - "Active": true, - "Name": "FalconEye" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#ffc061", - "Active": true, - "Name": "FastReload" - }, - { - "ChanceFrom": 1.2, - "ChanceTo": 3.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#A31912", - "Active": true, - "Name": "Flash" - }, - { - "Cooldown": 2.0, - "BarricadeHealth": 115, - "PropModel": "models/props/de_aztec/hr_aztec/aztec_scaffolding/aztec_scaffold_wall_support_128.vmdl", - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#1b04cc", - "Active": true, - "Name": "Fortnite" - }, - { - "MaxBombHealth": 1000, - "NeedsTeammates": false, - "OnlyTeam": 3, - "Color": "#5d00ff", - "Active": true, - "Name": "FragileBomb" - }, - { - "HealthMultiplier": 1.5, - "NeedsTeammates": true, - "OnlyTeam": 0, - "Color": "#ff0000", - "Active": true, - "Name": "FriendlyFire" - }, - { - "TriggerRadius": 180.0, - "SlownessMultiplier": 5, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#00eaff", - "Active": true, - "Name": "FrozenDecoy" - }, - { - "RefreshPrice": 150, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#7eff47", - "Active": true, - "Name": "Gambler" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#FFFFFF", - "Active": true, - "Name": "Ghost" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#5d00ff", - "Active": true, - "Name": "Glaz" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#f542ef", - "Active": true, - "Name": "Glitch" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#fff52e", - "Active": true, - "Name": "Glue" - }, - { - "Cooldown": 30.0, - "Duration": 2.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#e0d83a", - "Active": true, - "Name": "GodMode" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#4a6e21", - "Active": true, - "Name": "Grenadier" - }, - { - "SmokeHeal": 1, - "SmokeRadius": 180.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#1fe070", - "Active": true, - "Name": "HealingSmoke" - }, - { - "HealthToAdd": 25, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#ded678", - "Active": true, - "Name": "Hermit" - }, - { - "DamageMultiplier": 2.0, - "DamageRadiusMultiplier": 2.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#ffdd00", - "Active": true, - "Name": "HolyHandGrenade" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#99140B", - "Active": true, - "Name": "Impostor" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#0000FF", - "Active": true, - "Name": "InfiniteAmmo" - }, - { - "MaxStepBeam": 100, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#f542ef", - "Active": true, - "Name": "Jackal" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#42f5a7", - "Active": true, - "Name": "Jammer" - }, - { - "MinTime": 10.0, - "MaxTime": 25.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#8f108f", - "Active": true, - "Name": "Jester" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#b01e5d", - "Active": true, - "Name": "JumpBan" - }, - { - "HealthToAdd": 3, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#a86eff", - "Active": true, - "Name": "JumpingJack" - }, - { - "FlashDuration": 1.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#57bcff", - "Active": true, - "Name": "KillerFlash" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#a3651a", - "Active": true, - "Name": "LifeSwap" - }, - { - "MaxDistance": 4096.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#c9f8ff", - "Active": true, - "Name": "LongKnife" - }, - { - "MaxDistance": 4096.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#6effc7", - "Active": true, - "Name": "LongZeus" - }, - { - "CustomFOV": 50, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#9ba882", - "Active": true, - "Name": "Magnifier" - }, - { - "HealthToAdd": 50, - "HealthShotLimit": 3, - "Cooldown": 1.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#10c212", - "Active": true, - "Name": "Medic" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#52f54c", - "Active": true, - "Name": "MoneySwap" - }, - { - "ExplosionRadius": 500.0, - "ExplosionDamage": 999, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#F5CB42", - "Active": true, - "Name": "Muhammed" - }, - { - "IdlePercentInvisibility": 0.3, - "DuckPercentInvisibility": 0.3, - "KnifePercentInvisibility": 0.3, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#dedede", - "Active": true, - "Name": "Ninja" - }, - { - "Cooldown": 30.0, - "Duration": 2.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#44ebd4", - "Active": true, - "Name": "Noclip" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#a38c1a", - "Active": true, - "Name": "NoNades" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#FFFFFF", - "Active": true, - "Name": "None" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#8a42f5", - "Active": true, - "Name": "NoRecoil" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#ff5CD9", - "Active": true, - "Name": "OneShot" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#3c47de", - "Active": true, - "Name": "OnlyHead" - }, - { - "ExtraJumpsMin": 1, - "ExtraJumpsMax": 4, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#FFA500", - "Active": true, - "Name": "PawelJumper" - }, - { - "ChanceFrom": 0.2, - "ChanceTo": 0.4, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#ff5C0A", - "Active": true, - "Name": "Phoenix" - }, - { - "MaximumFuel": 150.0, - "FuelConsumption": 0.64, - "Refuelling": 0.1, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#1466F5", - "Active": true, - "Name": "Pilot" - }, - { - "ExtraC4BlowTime": 60, - "NeedsTeammates": false, - "OnlyTeam": 2, - "Color": "#7d7d7d", - "Active": true, - "Name": "Planter" - }, - { - "Damage": 1, - "Cooldown": 1.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#902eff", - "Active": true, - "Name": "Poison" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#ffc061", - "Active": true, - "Name": "PrimaryBan" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#9c9c9c", - "Active": true, - "Name": "Prosthesis" - }, - { - "MaxDefusingRange": 80.0, - "DefusingTime": 10.0, - "NeedsTeammates": false, - "OnlyTeam": 3, - "Color": "#507529", - "Active": true, - "Name": "PsychicDefusing" - }, - { - "ChanceFrom": 1.0, - "ChanceTo": 1.0, - "JumpVelocity": 300.0, - "PushVelocity": 400.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#1e9ab0", - "Active": true, - "Name": "Push" - }, - { - "RegenerationMultiplier": 1.5, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#3c47de", - "Active": true, - "Name": "Pyro" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#8a42f5", - "Active": true, - "Name": "QuickShot" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#2effcb", - "Active": true, - "Name": "RadarHack" - }, - { - "MinExtraHealth": 50, - "MaxExtraHealth": 501, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#009905", - "Active": true, - "Name": "Rambo" - }, - { - "Cooldown": 15.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#e0873a", - "Active": true, - "Name": "RandomWeapon" - }, - { - "Cooldown": 15.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#3cded3", - "Active": true, - "Name": "ReactiveArmor" - }, - { - "HealthToAdd": 1, - "Cooldown": 0.25, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#ff462e", - "Active": true, - "Name": "Regeneration" - }, - { - "Cooldown": 15.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#a3000b", - "Active": true, - "Name": "Replicator" - }, - { - "Cooldown": 15.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#a86eff", - "Active": true, - "Name": "Retreat" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#a68132", - "Active": true, - "Name": "ReturnToSender" - }, - { - "ZombieHealth": 200, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#ff5C0A", - "Active": true, - "Name": "ReZombie" - }, - { - "MinMoney": 5000, - "MaxMoney": 15000, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#D4AF37", - "Active": true, - "Name": "RichBoy" - }, - { - "MoneyMultiplier": 35, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#119125", - "Active": true, - "Name": "RobinHood" - }, - { - "SlownessTime": 2.0, - "SlownessModifier": 0.2, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#8B4513", - "Active": true, - "Name": "Rubber" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#8A2BE2", - "Active": true, - "Name": "Saper" - }, - { - "StartHealth": 50, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#d41c1c", - "Active": true, - "Name": "SecondLife" - }, - { - "TeleportDistance": 100.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#4d4d4d", - "Active": true, - "Name": "Shade" - }, - { - "DetonationTime": 20, - "NeedsTeammates": false, - "OnlyTeam": 2, - "Color": "#f5b74c", - "Active": true, - "Name": "ShortBomb" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#333333", - "Active": true, - "Name": "Silent" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#e0873a", - "Active": true, - "Name": "SniperElite" - }, - { - "ChanceFrom": 1.15, - "ChanceTo": 1.35, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#09ba00", - "Active": true, - "Name": "Soldier" - }, - { - "Cooldown": 5.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#e3ed8c", - "Active": true, - "Name": "SoundMaker" - }, - { - "Distance": 100.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#42f5da", - "Active": true, - "Name": "Spectator" - }, - { - "Cooldown": 30.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#1466F5", - "Active": true, - "Name": "SwapPosition" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#8A2BE2", - "Active": true, - "Name": "Teleporter" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#adaec7", - "Active": true, - "Name": "Thief" - }, - { - "Distance": 100.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#1b04cc", - "Active": true, - "Name": "ThirdEye" - }, - { - "HealthTakenScale": 0.3, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#962631", - "Active": true, - "Name": "Thorns" - }, - { - "SmokeDamage": 2, - "SmokeRadius": 180.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#507529", - "Active": true, - "Name": "ToxicSmoke" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#5d00ff", - "Active": true, - "Name": "Wallhack" - }, - { - "ChangeRoundTime": 10, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#ff462e", - "Active": true, - "Name": "Watchmaker" - }, - { - "Cooldown": 30.0, - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#c7e03a", - "Active": true, - "Name": "WeaponsSwap" - }, - { - "NeedsTeammates": false, - "OnlyTeam": 0, - "Color": "#fbff00", - "Active": true, - "Name": "Zeus" - } - ] + } } \ No newline at end of file diff --git a/jRandomSkills - Server Files/plugins/!jRandomSkills/configs/skillsInfo.json b/jRandomSkills - Server Files/plugins/!jRandomSkills/configs/skillsInfo.json new file mode 100644 index 0000000..1b2a293 --- /dev/null +++ b/jRandomSkills - Server Files/plugins/!jRandomSkills/configs/skillsInfo.json @@ -0,0 +1,1014 @@ +[ + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#ff0000", + "Active": true, + "Name": "Aimbot" + }, + { + "SecondsInBack": 5, + "Cooldown": 15.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": true, + "OnlyTeam": 0, + "Color": "#a86eff", + "Active": true, + "Name": "Anomaly" + }, + { + "FlashDuration": 7.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#D6E6FF", + "Active": true, + "Name": "AntyFlash" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#8B4513", + "Active": true, + "Name": "AntyHead" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 3, + "Color": "#edf5b5", + "Active": true, + "Name": "AreaReaper" + }, + { + "ChanceFrom": 0.65, + "ChanceTo": 0.85, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#d1430a", + "Active": true, + "Name": "Armored" + }, + { + "DamageMultiplier": 2.0, + "ToleranceDeg": 45.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#d9d9d9", + "Active": true, + "Name": "Assassin" + }, + { + "ChanceFrom": 0.1, + "ChanceTo": 0.7, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#7E10AD", + "Active": true, + "Name": "Astronaut" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#abab33", + "Active": true, + "Name": "Bankrupt" + }, + { + "SpeedMultipier": 2.0, + "MaxSpeed": 900.0, + "DamageDeal": 9999.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#2effc7", + "Active": true, + "Name": "Baseball" + }, + { + "ChanceFrom": 0.2, + "ChanceTo": 0.4, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#00FF00", + "Active": true, + "Name": "Behind" + }, + { + "TorseReflectionChance": 0.95, + "LegReflectionChance": 0.8, + "VelocityModifier": 0.85, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#cc7504", + "Active": true, + "Name": "BladeMaster" + }, + { + "MaxSpeed": 500.0, + "JumpVelocity": 300.0, + "JumpBoost": 2.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#d1430a", + "Active": true, + "Name": "BunnyHop" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 2, + "Color": "#00911f", + "Active": true, + "Name": "C4Camouflage" + }, + { + "ChanceFrom": 0.2, + "ChanceTo": 0.4, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#FF4500", + "Active": true, + "Name": "Catapult" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#FF8B42", + "Active": true, + "Name": "Chicken" + }, + { + "BombArmedTime": 10.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 3, + "Color": "#343deb", + "Active": true, + "Name": "ChillOut" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#88a31a", + "Active": true, + "Name": "Cutter" + }, + { + "Brightness": 0.01, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#383838", + "Active": true, + "Name": "Darkness" + }, + { + "JumpVelocity": 150.0, + "PushVelocity": 600.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#42bbfc", + "Active": true, + "Name": "Dash" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#919191", + "Active": true, + "Name": "Deactivator" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#dae01f", + "Active": true, + "Name": "Deaf" + }, + { + "ChanceFrom": 0.2, + "ChanceTo": 0.35, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#FF4500", + "Active": true, + "Name": "Disarmament" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#00f2ff", + "Active": true, + "Name": "Distancer" + }, + { + "HealthRegainScale": 0.3, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#FA050D", + "Active": true, + "Name": "Dracula" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#ffb73b", + "Active": true, + "Name": "Duplicator" + }, + { + "MinScale": 0.6, + "MaxScale": 0.95, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#ffff00", + "Active": true, + "Name": "Dwarf" + }, + { + "Cooldown": 16.0, + "Amplitude": 15.0, + "Frequency": 500.0, + "Duration": 8.0, + "Radius": 50.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#42f59b", + "Active": false, + "Name": "Earthquake" + }, + { + "Cooldown": 15.0, + "CooldownBeforeUse": 10.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": true, + "OnlyTeam": 0, + "Color": "#ff8c92", + "Active": true, + "Name": "EnemySpawn" + }, + { + "Damage": 25.0, + "DamageRadius": 210.0, + "ChanceFrom": 0.15, + "ChanceTo": 0.3, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#9c0000", + "Active": true, + "Name": "ExplosiveShot" + }, + { + "Distance": 1000.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#d1f542", + "Active": true, + "Name": "FalconEye" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#ffc061", + "Active": true, + "Name": "FastReload" + }, + { + "ChanceFrom": 1.2, + "ChanceTo": 3.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#A31912", + "Active": true, + "Name": "Flash" + }, + { + "Cooldown": 2.0, + "BarricadeHealth": 115, + "PropModel": "models/props/de_aztec/hr_aztec/aztec_scaffolding/aztec_scaffold_wall_support_128.vmdl", + "NeedsTeammates": false, + "DisableOnFreezeTime": true, + "OnlyTeam": 0, + "Color": "#1b04cc", + "Active": true, + "Name": "Fortnite" + }, + { + "MaxBombHealth": 1000, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 3, + "Color": "#5d00ff", + "Active": true, + "Name": "FragileBomb" + }, + { + "HealthMultiplier": 1.5, + "NeedsTeammates": true, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#ff0000", + "Active": true, + "Name": "FriendlyFire" + }, + { + "TriggerRadius": 180.0, + "SlownessMultiplier": 5, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#00eaff", + "Active": true, + "Name": "FrozenDecoy" + }, + { + "RefreshPrice": 150, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#7eff47", + "Active": true, + "Name": "Gambler" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#FFFFFF", + "Active": true, + "Name": "Ghost" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#5d00ff", + "Active": true, + "Name": "Glaz" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#f542ef", + "Active": true, + "Name": "Glitch" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#fff52e", + "Active": true, + "Name": "Glue" + }, + { + "Cooldown": 30.0, + "Duration": 2.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": true, + "OnlyTeam": 0, + "Color": "#e0d83a", + "Active": true, + "Name": "GodMode" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#4a6e21", + "Active": true, + "Name": "Grenadier" + }, + { + "SmokeHeal": 1, + "SmokeRadius": 180.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#1fe070", + "Active": true, + "Name": "HealingSmoke" + }, + { + "HealthToAdd": 25, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#ded678", + "Active": true, + "Name": "Hermit" + }, + { + "DamageMultiplier": 2.0, + "DamageRadiusMultiplier": 2.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#ffdd00", + "Active": true, + "Name": "HolyHandGrenade" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#99140B", + "Active": true, + "Name": "Impostor" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#0000FF", + "Active": true, + "Name": "InfiniteAmmo" + }, + { + "MaxStepBeam": 100, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#f542ef", + "Active": true, + "Name": "Jackal" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#42f5a7", + "Active": true, + "Name": "Jammer" + }, + { + "MinTime": 10.0, + "MaxTime": 25.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#8f108f", + "Active": true, + "Name": "Jester" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#b01e5d", + "Active": true, + "Name": "JumpBan" + }, + { + "HealthToAdd": 3, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#a86eff", + "Active": true, + "Name": "JumpingJack" + }, + { + "FlashDuration": 1.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#57bcff", + "Active": true, + "Name": "KillerFlash" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#a3651a", + "Active": true, + "Name": "LifeSwap" + }, + { + "MaxDistance": 4096.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#c9f8ff", + "Active": true, + "Name": "LongKnife" + }, + { + "MaxDistance": 4096.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#6effc7", + "Active": true, + "Name": "LongZeus" + }, + { + "CustomFOV": 50, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#9ba882", + "Active": true, + "Name": "Magnifier" + }, + { + "HealthToAdd": 50, + "HealthShotLimit": 3, + "Cooldown": 1.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": true, + "OnlyTeam": 0, + "Color": "#10c212", + "Active": true, + "Name": "Medic" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#52f54c", + "Active": true, + "Name": "MoneySwap" + }, + { + "ExplosionRadius": 500.0, + "ExplosionDamage": 999, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#F5CB42", + "Active": true, + "Name": "Muhammed" + }, + { + "IdlePercentInvisibility": 0.3, + "DuckPercentInvisibility": 0.3, + "KnifePercentInvisibility": 0.3, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#dedede", + "Active": true, + "Name": "Ninja" + }, + { + "Cooldown": 30.0, + "Duration": 2.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": true, + "OnlyTeam": 0, + "Color": "#44ebd4", + "Active": true, + "Name": "Noclip" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#a38c1a", + "Active": true, + "Name": "NoNades" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#FFFFFF", + "Active": true, + "Name": "None" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#8a42f5", + "Active": true, + "Name": "NoRecoil" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#ff5CD9", + "Active": true, + "Name": "OneShot" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#3c47de", + "Active": true, + "Name": "OnlyHead" + }, + { + "ExtraJumpsMin": 1, + "ExtraJumpsMax": 4, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#FFA500", + "Active": true, + "Name": "PawelJumper" + }, + { + "ChanceFrom": 0.2, + "ChanceTo": 0.4, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#ff5C0A", + "Active": true, + "Name": "Phoenix" + }, + { + "MaximumFuel": 150.0, + "FuelConsumption": 0.64, + "Refuelling": 0.1, + "NeedsTeammates": false, + "DisableOnFreezeTime": true, + "OnlyTeam": 0, + "Color": "#1466F5", + "Active": true, + "Name": "Pilot" + }, + { + "ExtraC4BlowTime": 60, + "NeedsTeammates": false, + "DisableOnFreezeTime": true, + "OnlyTeam": 2, + "Color": "#7d7d7d", + "Active": true, + "Name": "Planter" + }, + { + "Damage": 1, + "Cooldown": 1.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": true, + "OnlyTeam": 0, + "Color": "#902eff", + "Active": true, + "Name": "Poison" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#ffc061", + "Active": true, + "Name": "PrimaryBan" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#9c9c9c", + "Active": true, + "Name": "Prosthesis" + }, + { + "MaxDefusingRange": 80.0, + "DefusingTime": 10.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 3, + "Color": "#507529", + "Active": true, + "Name": "PsychicDefusing" + }, + { + "ChanceFrom": 1.0, + "ChanceTo": 1.0, + "JumpVelocity": 300.0, + "PushVelocity": 400.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#1e9ab0", + "Active": true, + "Name": "Push" + }, + { + "RegenerationMultiplier": 1.5, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#3c47de", + "Active": true, + "Name": "Pyro" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#8a42f5", + "Active": true, + "Name": "QuickShot" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#2effcb", + "Active": true, + "Name": "RadarHack" + }, + { + "MinExtraHealth": 50, + "MaxExtraHealth": 501, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#009905", + "Active": true, + "Name": "Rambo" + }, + { + "Cooldown": 15.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#e0873a", + "Active": true, + "Name": "RandomWeapon" + }, + { + "Cooldown": 15.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#3cded3", + "Active": true, + "Name": "ReactiveArmor" + }, + { + "HealthToAdd": 1, + "Cooldown": 0.25, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#ff462e", + "Active": true, + "Name": "Regeneration" + }, + { + "Cooldown": 15.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": true, + "OnlyTeam": 0, + "Color": "#a3000b", + "Active": true, + "Name": "Replicator" + }, + { + "Cooldown": 15.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": true, + "OnlyTeam": 0, + "Color": "#a86eff", + "Active": true, + "Name": "Retreat" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#a68132", + "Active": true, + "Name": "ReturnToSender" + }, + { + "ZombieHealth": 200, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#ff5C0A", + "Active": true, + "Name": "ReZombie" + }, + { + "MinMoney": 5000, + "MaxMoney": 15000, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#D4AF37", + "Active": true, + "Name": "RichBoy" + }, + { + "MoneyMultiplier": 35, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#119125", + "Active": true, + "Name": "RobinHood" + }, + { + "SlownessTime": 2.0, + "SlownessModifier": 0.2, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#8B4513", + "Active": true, + "Name": "Rubber" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#8A2BE2", + "Active": true, + "Name": "Saper" + }, + { + "StartHealth": 50, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#d41c1c", + "Active": true, + "Name": "SecondLife" + }, + { + "TeleportDistance": 100.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#4d4d4d", + "Active": true, + "Name": "Shade" + }, + { + "DetonationTime": 20, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 2, + "Color": "#f5b74c", + "Active": true, + "Name": "ShortBomb" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#333333", + "Active": true, + "Name": "Silent" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#e0873a", + "Active": true, + "Name": "SniperElite" + }, + { + "ChanceFrom": 1.15, + "ChanceTo": 1.35, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#09ba00", + "Active": true, + "Name": "Soldier" + }, + { + "Cooldown": 5.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#e3ed8c", + "Active": true, + "Name": "SoundMaker" + }, + { + "Distance": 100.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#42f5da", + "Active": true, + "Name": "Spectator" + }, + { + "Cooldown": 30.0, + "CooldownBeforeUse": 10.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": true, + "OnlyTeam": 0, + "Color": "#1466F5", + "Active": true, + "Name": "SwapPosition" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#8A2BE2", + "Active": true, + "Name": "Teleporter" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#adaec7", + "Active": true, + "Name": "Thief" + }, + { + "Distance": 100.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#1b04cc", + "Active": true, + "Name": "ThirdEye" + }, + { + "HealthTakenScale": 0.3, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#962631", + "Active": true, + "Name": "Thorns" + }, + { + "SmokeDamage": 2, + "SmokeRadius": 180.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#507529", + "Active": true, + "Name": "ToxicSmoke" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#5d00ff", + "Active": true, + "Name": "Wallhack" + }, + { + "ChangeRoundTime": 10, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#ff462e", + "Active": true, + "Name": "Watchmaker" + }, + { + "Cooldown": 30.0, + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#c7e03a", + "Active": true, + "Name": "WeaponsSwap" + }, + { + "NeedsTeammates": false, + "DisableOnFreezeTime": false, + "OnlyTeam": 0, + "Color": "#fbff00", + "Active": true, + "Name": "Zeus" + } +] \ No newline at end of file diff --git a/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/en.json b/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/en.json index 1cb54d3..d3b93e4 100644 --- a/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/en.json +++ b/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/en.json @@ -215,7 +215,7 @@ "lifeswap_select_info": "Swap health with player:", "longknife": "Long Knife", - "longknife_desc": "A primary knife attack deals damage regardless of distance", + "longknife_desc": "A knife attack deals damage regardless of distance", "longzeus": "Long Zeus", "longzeus_desc": "Zeus deals damage regardless of distance", @@ -237,6 +237,7 @@ "muhammed": "Muhammed", "muhammed_desc": "You explode upon death, killing nearby players", + "muhammed_explosion": "ALLAHU AKBAR!!!", "ninja": "Ninja", "ninja_desc": "Standing still increases your invisibility by 33%, crouching by 33%, and holding a knife by 33%", @@ -452,5 +453,7 @@ "summary_start": "======SUMMARY=OF=THE=LAST=ROUND======", "summary_end": "=======================================", - "menu_controlls": "W/S - Scroll | E - Select
" + "menu_controlls_scroll": "W/S - Scroll", + "menu_controlls_padding": " | ", + "menu_controlls_select": "E - Select" } \ No newline at end of file diff --git a/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/fr.json b/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/fr.json index 8131605..d5548be 100644 --- a/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/fr.json +++ b/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/fr.json @@ -215,7 +215,7 @@ "lifeswap_select_info": "Échanger la santé avec le joueur :", "longknife": "Couteau Long", - "longknife_desc": "Une attaque primaire au couteau inflige des dégâts quelle que soit la distance", + "longknife_desc": "Une attaque au couteau inflige des dégâts quelle que soit la distance", "longzeus": "Zeus Longue Portée", "longzeus_desc": "Le Zeus inflige des dégâts quelle que soit la distance", @@ -237,6 +237,7 @@ "muhammed": "Bombe", "muhammed_desc": "Vous explosez à votre mort, tuant les joueurs proches", + "muhammed_explosion": "ALLAHU AKBAR!!!", "ninja": "Ninja", "ninja_desc": "Rester immobile augmente votre invisibilité de 33%, s’accroupir de 33%, et tenir un couteau de 33%", @@ -452,5 +453,7 @@ "summary_start": "======RÉSUMÉ=DU=DERNIER=ROUND======", "summary_end": "===================================", - "menu_controlls": "W/S - Défilement | E - Sélectionner
" -} \ No newline at end of file + "menu_controlls_scroll": "W/S - Défilement", + "menu_controlls_padding": " | ", + "menu_controlls_select": "E - Sélectionner" +} diff --git a/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/pl.json b/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/pl.json index b84c5ac..60a9b5f 100644 --- a/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/pl.json +++ b/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/pl.json @@ -215,7 +215,7 @@ "lifeswap_select_info": "Zamień się zdrowiem z graczem:", "longknife": "Długi Nóż", - "longknife_desc": "Podstawowy atak nożem zadaje obrażenia niezależnie od odległości", + "longknife_desc": "Atak nożem zadaje obrażenia niezależnie od odległości", "longzeus": "Długi Zeus", "longzeus_desc": "Zeus zadaje obrażenia niezależnie od odległości", @@ -237,6 +237,7 @@ "muhammed": "Muhammed", "muhammed_desc": "Po śmierci eksplodujesz i zabijasz graczy znajdujących się w zasięgu", + "muhammed_explosion": "ALLAHU AKBAR!!!", "ninja": "Ninja", "ninja_desc": "Stojąc nieruchomo zwiększasz swoją niewidzialność o 33%, kucając o 33%, a trzymając nóż o 33%", @@ -452,5 +453,7 @@ "summary_start": "======PODSUMOWANIE=Z=OSTATNIEJ=RUNDY======", "summary_end": "============================================", - "menu_controlls": "W/S – Przewijanie | E - Wybierz
" + "menu_controlls_scroll": "W/S – Przewijanie", + "menu_controlls_padding": " | ", + "menu_controlls_select": "E - Wybierz" } \ No newline at end of file diff --git a/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/pt-br.json b/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/pt-br.json index fb05ea3..180a374 100644 --- a/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/pt-br.json +++ b/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/pt-br.json @@ -215,7 +215,7 @@ "lifeswap_select_info": "Trocar vida com jogador:", "longknife": "Faca Longa", - "longknife_desc": "Um ataque com faca primária causa dano independentemente da distância", + "longknife_desc": "Um ataque com faca causa danos independentemente da distância", "longzeus": "Zeus Longo", "longzeus_desc": "Zeus causa dano independentemente da distância", @@ -237,6 +237,7 @@ "muhammed": "Muhammed", "muhammed_desc": "Você explode ao morrer, matando jogadores próximos", + "muhammed_explosion": "ALLAHU AKBAR!!!", "ninja": "Ninja", "ninja_desc": "Ficar parado aumenta sua invisibilidade em 33%, agachar em 33%, e segurar uma faca em 33%", @@ -452,5 +453,7 @@ "summary_start": "=======RESUMO=DA=ÚLTIMA=RODADA=======", "summary_end": "=======================================", - "menu_controlls": "W/S - Rolar | E - Selecionar
" + "menu_controlls_scroll": "W/S - Rolar", + "menu_controlls_padding": " | ", + "menu_controlls_select": "E - Selecionar" } \ No newline at end of file diff --git a/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/test.json b/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/test.json new file mode 100644 index 0000000..98be7e8 --- /dev/null +++ b/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/test.json @@ -0,0 +1,458 @@ +{ + "none": "Brak", + "none_desc": "Nie posiadasz żadnej supermocy", + + "aimbot": "Aimbot", + "aimbot_desc": "Każdy twój trafiony pocisk liczy się jako strzał w głowę", + + "anomaly": "Anomalia", + "anomaly_desc": "Cofasz się o kilka sekund do tyłu", + + "antyflash": "Anty Flash", + "antyflash_desc": "Posiadasz odporność na flash'e, a twoje flash'e trwają 7 sekund", + + "antyhead": "Żelazna Głowa", + "antyhead_desc": "Nie otrzymujesz obrażeń w głowę", + + "areareaper": "Niszczyciel Stref", + "areareaper_desc": "Możesz wybrać strefę detonacji, którą chcesz dezaktywować", + "areareaper_incorrect_site": "Nie znaleziono takiej strefy detonacji.", + "areareaper_no_site": "Nie znaleziono żadnej strefy detonacji.", + "areareaper_select_info": "Wybierz strefę do dezaktyw.:", + "areareaper_site_disabled": "Strefa detonacji {0} została dezaktywowana - nie można na niej podłożyć bomby!", + "areareaper_used_info": "Twoja moc została już wykorzystana.", + + "armored": "Gruby", + "armored_desc": "Masz losowy mnożnik otrzymywanych obrażeń", + "armored_desc2": "Twój mnożnik otrzymywanych obrażeń wynosi: {0}x", + + "assassin": "Skrytobójca", + "assassin_desc": "Zadajesz większe obrażenia przeciwnikowi od tyłu", + + "astronaut": "Astronauta", + "astronaut_desc": "Na początku rundy otrzymujesz losową wartość grawitacji", + "astronaut_desc2": "Twoja losowa grawitacja wynosi: {0}x", + + "bankrupt": "Bankrut", + "bankrupt_desc": "Wybierz gracza, który straci wszystkie swoje pieniądze", + "bankrupt_enemy_info": "Stałeś się bankrutem.", + "bankrupt_player_info": "Gracz '{0}' zbankrutował.", + "bankrupt_select_info": "Wybierz gracza do bankrucji:", + + "baseball": "Bejsbolista", + "baseball_desc": "Twój wabik odbija się od ścian i natychmiastowo zabija wroga po trafieniu", + + "behind": "Obrót Wroga", + "behind_desc": "Masz losową szansę na obrócenie wroga o 180 stopni po trafieniu go", + "behind_desc2": "Twoje szanse na obrócenie wroga po trafieniu to: {0}%", + + "blademaster": "Mistrz Ostrza", + "blademaster_desc": "Trzymając nóż, masz duże szanse na odparcie strzału", + + "bunnyhop": "Królik", + "bunnyhop_desc": "Otrzymujesz auto \"BunnyHopa\"", + + "c4camouflage": "C4 Kamuflaż", + "c4camouflage_desc": "Kiedy trzymasz bombę, jesteś niewidzialny", + + "catapult": "Katapulta", + "catapult_desc": "Masz losową szanse na podrzucenie wroga", + "catapult_desc2": "Twoje szanse na podrzucenie wroga po trafieniu to: {0}%", + + "chicken": "Kurczak", + "chicken_desc": "Otrzymujesz model kurczaka + jesteś o 10% szybszy - 50hp", + + "chillout": "Wyluzowany", + "chillout_desc": "Podłożenie bomby zajmuje znacznie więcej czasu", + + "cutter": "Scyzoryk", + "cutter_desc": "Natychmiastowe zabójstwo nożem", + + "darkness": "Mrok", + "darkness_desc": "Nadaje efekt ciemności wybranemu przeciwnikowi", + "darkness_enemy_info": "Niech zgasną światła.", + "darkness_player_info": "Ciemność opanowała gracza '{0}'.", + "darkness_select_info": "Nałóż efekt ciemności:", + + "deactivator": "Deaktywator", + "deactivator_desc": "Wybierasz gracza, którego supermoc chcesz wyłączyć", + "deactivator_enemy_info": "Twoja supermoc została wyłączona.", + "deactivator_player_info": "Moc gracza '{0}' została wyłączona.", + "deactivator_select_info": "Wyłącz moc gracza:", + + "deaf": "Głuchy", + "deaf_desc": "Wybierasz gracza, dla którego chcesz wyłączyć wszystkie dźwięki", + "deaf_enemy_info": "Słuchawki opuściły grę.", + "deaf_player_info": "Dźwięk został wyłączony dla gracza '{0}'.", + "deaf_select_info": "Wycisz dźwięki gracza:", + + "disarmament": "Rozbrojenie", + "disarmament_desc": "Masz losową szanse na wyrzucenie broni wroga po trafieniu", + "disarmament_desc2": "Twoje szanse na wyrzucenie broni wroga to: {0}%", + + "distancer": "Odległościomierz", + "distancer_desc": "Możesz zobaczyć odległość do najbliższego przeciwnika", + + "dash": "Doskok", + "dash_desc": "Wykonaj drugi skok, aby wykonać doskok", + + "dracula": "Drakula", + "dracula_desc": "Po trafieniu ofiary odzyskujesz zdrowie równe pewnemu procentowi zadanych obrażeń", + + "duplicator": "Duplikator", + "duplicator_desc": "Wybierasz gracza, od którego chcesz skopiować supermoc", + "duplicator_player_info": "Moc gracza '{0}' została skopiowana.", + "duplicator_select_info": "Skopiuj moc gracza:", + + "dwarf": "Mini Majk ({0}%)", + "dwarf_desc": "Losowa wielkość postaci na początku rundy", + "dwarf_desc2": "Twój mnożnik wielkości to {0}%", + + "enemyspawn": "Resp Wroga", + "enemyspawn_desc": "Kliknij [css_useSkill], aby teleportować się na resp wroga", + + "explosiveshot": "Strzał Wybuchowy", + "explosiveshot_desc": "Losowa szansa wystrzelenia pocisku wybuchowego podczas strzelania", + "explosiveshot_desc2": "Twoja szansa na wystrzelenie pocisku wybuchowego: {0}%", + + "falconeye": "Oko Sokoła", + "falconeye_desc": "Kliknij [css_useSkill], aby aktywować kamerę z lotu ptaka", + + "fastreload": "Szybkie Rączki", + "fastreload_desc": "Kliknij [css_useSkill], aby przeładować broń, którą obecnie trzymasz", + + "flash": "Flash", + "flash_desc": "Losowa prędkośc postaci na początku rundy", + "flash_desc2": "Twój mnożnik prędkości to {0}x", + + "fortnite": "Fortnite", + "fortnite_desc": "Kliknij [css_useSkill], aby stworzyć barykadę, którą można zniszczyć", + + "fragilebomb": "Krucha Bomba", + "fragilebomb_desc": "Strzelanie do bomby powoduje jej uszkodzenie", + "fragilebomb_bomb_health": "Zdrowie bomby", + + "friendlyfire": "Ogień Przyjacielski", + "friendlyfire_desc": "Strzelanie do członków drużyny leczy ich", + + "frozendecoy": "Zamrażający Wabik", + "frozendecoy_desc": "Twój decoy zamraża wszystkich graczy w pobliżu", + + "gambler": "Hazardzista", + "gambler_desc": "Wybierz umiejętność z podanej listy.", + "gambler_select_info": "Wybierz umiejętność:", + "gambler_more": "#00FF00|Odśwież za ${0}", + + "ghost": "Duszek", + "ghost_desc": "Jesteś całkowicie niewidzialny", + + "glaz": "Glaz", + "glaz_desc": "Nie widzisz granatów dymnych", + + "glitch": "Glitch", + "glitch_desc": "Wyłączasz radar wybranemu przeciwnikowi", + "glitch_enemy_info": "Twój radar został wyłączony.", + "glitch_player_info": "Radar gracza '{0}' został wyłączony.", + "glitch_select_info": "Wyłącz radar dla gracza:", + + "glue": "Klej", + "glue_desc": "Twoje granaty przyklejają się do ścian", + + "godmode": "Nieśmiertelność", + "godmode_desc": "Kliknij [css_useSkill], aby stać się nieśmiertelnym na krótką chwilę", + "godmode_off": "Nieśmiertelność wyłączona", + "godmode_on": "Nieśmiertelność włączona", + + "grenadier": "Granatnik", + "grenadier_desc": "Masz nieskończone granaty odłamkowe", + + "healingsmoke": "Leczący Dym", + "healingsmoke_desc": "Twoje granaty dymne leczą", + + "hermit": "Pustelnik", + "hermit_desc": "Zabijanie przywraca amunicję i część zdrowia", + + "holyhandgrenade": "Święty Granat Ręczny", + "holyhandgrenade_desc": "Twoje granaty uderzeniowe zadają podwójne obrażenia i mają podwójny zasięg", + + "impostor": "Szpieg", + "impostor_desc": "Na początku rundy otrzymujesz model postaci przeciwnika", + + "infiniteammo": "Nieskończone Ammo", + "infiniteammo_desc": "Otrzymujesz nieskończoną ilość amunicji do wszystkich broni", + + "jackal": "Stópkarz", + "jackal_desc": "Każdy przeciwnik pozostawia za sobą ślad", + + "jammer": "Zakłócacz", + "jammer_desc": "Wybierasz gracza, dla którego chcesz wyłączyć celownik", + "jammer_enemy_info": "Twój celownik został wyłączony.", + "jammer_player_info": "Celownik gracza '{0}' została wyłączona.", + "jammer_select_info": "Wyłącz celownik dla gracza:", + + "jester": "Błazen", + "jester_desc": "W trybie błazna nie możesz zadawać ani otrzymywać obrażeń. Tryb zmienia się co kilka sekund.", + "jester_mode": "Tryb Błazna", + "jester_on": "WŁ.", + "jester_off": "WYŁ.", + + "jumpban": "Beznogi", + "jumpban_desc": "Wybierasz gracza, który nie będzie mógł skakać", + "jumpban_enemy_info": "Ktoś odciął ci nogi.", + "jumpban_player_info": "Gracz '{0}' nie może teraz skakać.", + "jumpban_select_info": "Zablokuj skok dla gracza:", + + "jumpingjack": "Pajacyk", + "jumpingjack_desc": "Skakanie przywraca zdrowie", + + "killerflash": "Zabójczy Flash", + "killerflash_desc": "Każdy całkowicie oślepiony twoim granatem umiera (również ty)", + + "lifeswap": "Zamiana Żyć", + "lifeswap_desc": "Wybierasz gracza, z którym chcesz wymienić się zdrowiem", + "lifeswap_enemy_info": "Ktoś pożyczył sobie twoje zdrowie.", + "lifeswap_player_info": "Wymieniłeś się zdrowiem z graczem '{0}'.", + "lifeswap_select_info": "Zamień się zdrowiem z graczem:", + + "longknife": "Długi Nóż", + "longknife_desc": "Podstawowy atak nożem zadaje obrażenia niezależnie od odległości", + + "longzeus": "Długi Zeus", + "longzeus_desc": "Zeus zadaje obrażenia niezależnie od odległości", + + "magnifier": "Lupa", + "magnifier_desc": "Przybiżasz ekran wybranemu przeciwnikowi", + "magnifier_enemy_info": "Twoje pole widzenia zostało zniekształcone!", + "magnifier_player_info": "Gracz '{0}' został zmuszony do przejścia w tryb powiększenia.", + "magnifier_select_info": "Powiększ ekran gracza:", + + "medic": "Medyk", + "medic_desc": "Kliknij [css_useSkill], aby użyć ładunku leczniczego, który przywraca 50 punktów zdrowia", + + "moneyswap": "Skarbówka", + "moneyswap_desc": "Wybierasz gracza, z którym chcesz zamienić się pieniędzmi", + "moneyswap_enemy_info": "Urząd skarbowy cię dopadł.", + "moneyswap_player_info": "Wymieniłeś swoje pieniądze z graczem '{0}'.", + "moneyswap_select_info": "Zamień się kasą z graczem:", + + "muhammed": "Muhammed", + "muhammed_desc": "Po śmierci eksplodujesz i zabijasz graczy znajdujących się w zasięgu", + + "ninja": "Ninja", + "ninja_desc": "Stojąc nieruchomo zwiększasz swoją niewidzialność o 33%, kucając o 33%, a trzymając nóż o 33%", + + "nonades": "Pancernik", + "nonades_desc": "Granaty nie zadają Ci obrażeń", + + "norecoil": "Skupienie", + "norecoil_desc": "Brak odrzutu podczas strzelania", + + "noclip": "NoClip", + "noclip_desc": "Kliknij [css_useSkill], aby włączyć noclip na krótki czas", + + "oneshot": "Jednostrzałowiec", + "oneshot_desc": "Po trafieniu natychmiast zabijasz przeciwnika", + + "onlyhead": "Tylko Głowa", + "onlyhead_desc": "Otrzymujesz obrażenia tylko w głowę", + + "paweljumper": "Pawel Jumper", + "paweljumper_desc": "Otrzymujesz losową liczbę dodatkowych skoków", + "paweljumper_desc2": "Otrzymujesz {0} dodatkowych skoków", + + "phoenix": "Feniks", + "phoenix_desc": "Masz losową szans na odrodzenie się po śmierci", + "phoenix_desc2": "Twoje szanse na odrodzenie się po śmierci: {0}%", + "phoenix_respawn": "Zostałeś odrodzony z popiołów dzięki mocy: CHATCOLORS.REDPhoenix", + + "psychicdefusing": "Zdalne Rozbrajanie", + "psychicdefusing_desc": "Kiedy jesteś blisko bomby, zaczynasz ją rozbrajać", + "psychicdefusing_hud_info": "Pozostało {0} sekund do rozbrojenia", + + "pilot": "Pilot", + "pilot_desc": "Latanie przez określony czas. Przytrzymaj [USE - E], aby latać", + "pilot_hud_info": "Paliwo", + + "planter": "Samowolka", + "planter_desc": "Bombę można podłożyć w dowolnym miejscu, a czas detonacji wynosi 60 sekund.", + + "poison": "Trutka", + "poison_desc": "Wybierasz gracza, który co kilka sekund będzie otrzymywał obrażenia", + "poison_enemy_info": "Zostałeś otruty.", + "poison_player_info": "Gracz '{0}' został otruty.", + "poison_select_info": "Zatruj gracza:", + + "primaryban": "Brak Karabinów", + "primaryban_desc": "Wybierasz gracza, który nie może używać karabinów", + "primaryban_enemy_info": "Nie możesz już używać karabinów.", + "primaryban_player_info": "Gracz '{0}' nie może już używać karabinów.", + "primaryban_select_info": "Zablokuj karabiny dla gracza:", + + "prosthesis": "Proteza", + "prosthesis_desc": "Ramiona i nogi są kuloodporne", + + "push": "Odpychacz", + "push_desc": "Masz losową szansę na odepchnięcie wroga po trafieniu go", + "push_desc2": "Twoje szanse na odepchnięcie wroga to: {0}%", + + "pyro": "Pyro", + "pyro_desc": "Molotow przywraca zdrowie", + + "quickshot": "Szybkostrzelność", + "quickshot_desc": "Wszystkie pociski są wystrzeliwane bardzo szybko", + + "radarhack": "Radarowiec", + "radarhack_desc": "Na radarze widać wrogów", + + "rambo": "Rambo", + "rambo_desc": "Na początku rundy otrzymujesz losową ilość zdrowia", + + "randomweapon": "Losowa Broń", + "randomweapon_desc": "Kliknij [css_useSkill], aby otrzymać losową broń", + + "rezombie": "Re-Zombie", + "rezombie_desc": "Po śmierci odradzasz się jako zombie z większym zdrowiem i bez broni", + + "reactivearmor": "Pancerz Reaktywny", + "reactivearmor_desc": "Pancerz pochłania pierwsze otrzymane obrażenia", + + "regeneration": "Regeneracja", + "regeneration_desc": "Co kilka sekund odnawiasz zdrowie", + + "replicator": "Replikator", + "replicator_desc": "Kliknij [css_useSkill], aby stworzyć swoją replikę, która zadaje obrażenia po trafieniu", + + "retreat": "Odwrót", + "retreat_desc": "Kliknij [css_useSkill], aby powrócić na resp", + + "returntosender": "Zwrot do Nadawcy", + "returntosender_desc": "Pierwsze trafienie wroga powoduje, że wraca on na swój resp", + + "richboy": "Bogacz", + "richboy_desc": "Na początku rundy otrzymujesz losową kwotę gotówki", + + "robinhood": "Robin Hood", + "robinhood_desc": "Zadanie obrażeń przeciwnikowi powoduje kradzież jego pieniędzy", + + "rubber": "Gumowe Kule", + "rubber_desc": "Twoje pociski znacznie spowalniają graczy", + + "saper": "Saper", + "saper_desc": "Możesz szybciej podłożyć bombę i ją rozbroić", + + "secondlife": "Druga Szansa", + "secondlife_desc": "Po śmierci odradzasz się z taką samą ilością zdrowia", + + "shade": "Cień", + "shade_desc": "Teleportujesz się za plecy trafionego wroga", + "shade_nospace": "Brak miejsca", + + "shortbomb": "Krótka Bomba", + "shortbomb_desc": "Bomba wybucha znacznie szybciej", + + "silent": "Cichociemny", + "silent_desc": "Twoje kroki i skoki są niesłyszalne dla INNYCH graczy", + + "sniperelite": "SzeliS", + "sniperelite_desc": "Kliknij [css_useSkill], aby zamienić aktualną broń na AWP", + + "soldier": "Żołnierz {0}%", + "soldier_desc": "Masz losowy mnożnik obrażeń", + "soldier_desc2": "A AAAAAAAAAAAAAAAAAAAAAA", + + "soundmaker": "Dźwiękowiec", + "soundmaker_desc": "Kliknij [css_useSkill], aby wywołać dzwięk u każdego przeciwnika", + + "spectator": "Obserwator", + "spectator_desc": "Kliknij [css_useSkill], aby obserwować losowego przeciwnika", + + "swapposition": "Zamiana Miejsc", + "swapposition_desc": "Kliknij [css_useSkill], aby zamienić się miejscami z losowym przeciwnikiem", + + "teleporter": "Teleporter", + "teleporter_desc": "Zamieniasz się miejscami z trafionym przeciwnikiem", + + "thief": "Złodziej", + "thief_desc": "Możesz ukraść supermoc wybranemu graczowi", + "thief_enemy_info": "Twoja supermoc została skradziona.", + "thief_player_info": "Moc gracza '{0}' została skradziona.", + "thief_select_info": "Ukradnij moc gracza:", + "thief_incorrect_skill": "Tej umiejętności nie można wybrać!", + + "thirdeye": "Trzecie Oko", + "thirdeye_desc": "Kliknij [css_useSkill], aby aktywować trzecią osobę", + + "thorns": "Ciernie", + "thorns_desc": "Twój przeciwnik otrzymuje część obrażeń, które zadał ci", + + "toxicsmoke": "Toksyczny Dym", + "toxicsmoke_desc": "Twoje granaty dymny zadają obrażenia", + + "wallhack": "Wallhack", + "wallhack_desc": "Widzisz wrogów przez ściany", + + "watchmaker": "Zegarmistrz", + "watchmaker_desc": "Każdy rzut granatem zmienia czas rundy", + "watchmaker_ct": "Czas rundy został skrócony o {0} sekund.", + "watchmaker_tt": "Czas rundy został wydłużony o {0} sekund", + + "weaponsswap": "Zamiana Broni", + "weaponsswap_desc": "Kliknij [css_useSkill], aby zamienić się bronią z losowym przeciwnikiem", + "weaponsswap_hud_info2": "Nie posiadasz broni na zamiane", + + "zeus": "Zeus", + "zeus_desc": "Zeus x27 natychmiastowo się odnawia", + + "your_skill": "", + "enemy_skill": "Supermoc przeciwnika", + "observer_skill": "Moc gracza", + "welcome_message": "Witaj {PLAYER} na serwerze {SERVER_NAME}!\nAktualna wersja jRandomSkills: {VERSION} ({SKILLS_COUNT} supermocy).\n\nPierwotnie plugin stworzona przez:\n{AUTHOR1}\nZmodyfikowany i ulepszony przez:\n{AUTHOR2}\nOficjalny discord: https://discord.gg/9H8EZYBpPF", + "drawing_skill": "", + "disabled_weapon": "Nie możesz używać tej broni", + + "hud_info": "Poczekaj jeszcze {0} sekund", + "hud_info_no_enemy": "Nie znaleziono przeciwnika", + "active_hud_info": "Działa jeszcze przez {0} milisekund", + + "skills_menu": "Lista Super Mocy", + "no_player": "Nie znaleziono pasującego gracza.", + "duplicate_player": "Znaleziono więcej niż jednego gracza o tej samej nazwie.", + "selectplayerskill_command": "Wpisz /t", + "selectplayerskill_incorrect_enemy_index": "Nie znaleziono graczy do wyboru.", + + "skill_not_found_setskill": "Nie znaleziono takiej CHATCOLORS.REDsupermocy", + "player_not_found_setskill": "Nie znaleziono takiego CHATCOLORS.REDgracza", + "correct_form_setskill": "Poprawne użycie: CHATCOLORS.RED!setskill ", + "correct_form_setscore": "Poprawne użycie: CHATCOLORS.RED!setscore ", + "done_setskill": "Ustawiono Supermoc", + "error_setskill": "Nie udało się ustawić CHATCOLORS.REDsupermocy", + "for_setskill": "dla", + + "invalid_map": "Nazwa mapy została wprowadzona nieprawidłowo!", + "loading_map": "Ładowanie nowej mapy", + + "pause": "Mecz został wstrzymany.", + "unpause": "Mecz został wznowiony.", + "healed": "Zostałeś uleczony.", + "game_start": "Gra rozpoczęta!", + "reload": "Tłumaczenia i konfigi zostały ponownie załadowane.", + + "vote_started": "Głosowanie rozpoczęte: '{0}'", + "vote_timeout": "Głosowanie '{0}' przekroczyło limit czasu!", + "vote_wait": "Musisz poczekać, zanim rozpoczniesz kolejne głosowanie!", + "vote_same_wait": "Musisz poczekać, zanim ponownie rozpoczniesz to samo głosowanie!", + "vote_not_enough_players": "Za mało graczy, aby rozpocząć głosowanie!", + "vote_alredy_voted": "Już zagłosowałeś!", + "vote_vote": "Głosowanie", + + "bombsite_a": "Strefa detonacji A", + "bombsite_b": "Strefa detonacji B", + + "teammate_skills": "Supermoce twoich sojuszników", + "summary_start": "======PODSUMOWANIE=Z=OSTATNIEJ=RUNDY======", + "summary_end": "============================================", + + "menu_controlls_scroll": "W/S - Scroll", + "menu_controlls_padding": " | ", + "menu_controlls_select": "E - Select" +} \ No newline at end of file diff --git a/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/zh.json b/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/zh.json index b6263c9..7b51cba 100644 --- a/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/zh.json +++ b/jRandomSkills - Server Files/plugins/!jRandomSkills/languages/zh.json @@ -215,7 +215,7 @@ "lifeswap_select_info": "选择你想与之交换生命值的玩家:", "longknife": "长刀", - "longknife_desc": "近战刀具攻击无论距离远近均可造成伤害", + "longknife_desc": "刀具攻击造成的伤害与距离无关", "longzeus": "长宙斯", "longzeus_desc": "宙斯电击枪无论距离多远都能造成伤害", @@ -237,6 +237,7 @@ "muhammed": "穆罕默德", "muhammed_desc": "你死亡时会爆炸,杀死附近的玩家", + "muhammed_explosion": "真主至大!!!", "ninja": "忍者", "ninja_desc": "站立不动增加33%隐形,蹲下增加33%,持刀增加33%", @@ -452,5 +453,7 @@ "summary_start": "=============上一回合总结==============", "summary_end": "=======================================", - "menu_controlls": "W/S - 滚动 | E - 选择
" + "menu_controlls_scroll": "W/S - 滚动", + "menu_controlls_padding": " | ", + "menu_controlls_select": "E - 选择" } \ No newline at end of file