From 01542a7dc61c95be28a17cb58952a484668ea6d8 Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Sat, 25 Nov 2023 23:12:17 +0100 Subject: [PATCH 01/47] Fixees --- Utility.cs | 55 ++++++++++ WeaponPaints.cs | 260 ++++++++++++++++++++++++------------------------ 2 files changed, 183 insertions(+), 132 deletions(-) create mode 100644 Utility.cs diff --git a/Utility.cs b/Utility.cs new file mode 100644 index 00000000..8b8011fb --- /dev/null +++ b/Utility.cs @@ -0,0 +1,55 @@ +using CounterStrikeSharp.API.Modules.Utils; +using System.Reflection; + +namespace WeaponPaints +{ + public static class Utility + { + public static WeaponPaintsConfig? Config { get; set; } + + public static string ReplaceTags(string message) + { + if (message.Contains('{')) + { + string modifiedValue = message; + if (Config != null) + { + modifiedValue = modifiedValue.Replace("{WEBSITE}", Config.Website); + } + foreach (FieldInfo field in typeof(ChatColors).GetFields()) + { + string pattern = $"{{{field.Name}}}"; + if (message.Contains(pattern, StringComparison.OrdinalIgnoreCase)) + { + modifiedValue = modifiedValue.Replace(pattern, field.GetValue(null)!.ToString(), StringComparison.OrdinalIgnoreCase); + } + } + return modifiedValue; + } + + return message; + } + + public static void Log(string message) + { + Console.BackgroundColor = ConsoleColor.DarkGray; + Console.ForegroundColor = ConsoleColor.Cyan; + Console.WriteLine("[WeaponPaints] " + message); + Console.ResetColor(); + } + public static void ShowAd(string moduleVersion) + { + Console.WriteLine(" "); + Console.WriteLine(" _ _ _______ _______ _______ _______ __ _ _______ _______ ___ __ _ _______ _______ "); + Console.WriteLine("| | _ | || || _ || || || | | || || _ || | | | | || || |"); + Console.WriteLine("| || || || ___|| |_| || _ || _ || |_| || _ || |_| || | | |_| ||_ _|| _____|"); + Console.WriteLine("| || |___ | || |_| || | | || || |_| || || | | | | | | |_____ "); + Console.WriteLine("| || ___|| || ___|| |_| || _ || ___|| || | | _ | | | |_____ |"); + Console.WriteLine("| _ || |___ | _ || | | || | | || | | _ || | | | | | | | _____| |"); + Console.WriteLine("|__| |__||_______||__| |__||___| |_______||_| |__||___| |__| |__||___| |_| |__| |___| |_______|"); + Console.WriteLine(" >> Version: " + moduleVersion); + Console.WriteLine(" >> GitHub: https://github.com/Nereziel/cs2-WeaponPaints"); + Console.WriteLine(" "); + } + } +} \ No newline at end of file diff --git a/WeaponPaints.cs b/WeaponPaints.cs index 04b221b0..b456565b 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -21,7 +21,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig public override string ModuleName => "WeaponPaints"; public override string ModuleDescription => "Skin and knife selector, standalone and web-based"; public override string ModuleAuthor => "Nereziel & daffyy"; - public override string ModuleVersion => "1.2"; + public override string ModuleVersion => "1.2a"; public WeaponPaintsConfig Config { get; set; } = new(); private string DatabaseConnectionString = string.Empty; @@ -37,19 +37,6 @@ public class WeaponPaints : BasePlugin, IPluginConfig private Dictionary g_playersKnife = new(); private static List skinsList = new List(); - private static readonly Dictionary knifeTypes = new() - { - { "m9", "weapon_knife_m9_bayonet" }, { "karambit", "weapon_knife_karambit" }, - { "bayonet", "weapon_bayonet" }, { "bowie", "weapon_knife_survival_bowie" }, - { "butterfly", "weapon_knife_butterfly" }, { "falchion", "weapon_knife_falchion" }, - { "flip", "weapon_knife_flip" }, { "gut", "weapon_knife_gut" }, - { "tactical", "weapon_knife_tactical" }, { "shadow", "weapon_knife_push" }, - { "navaja", "weapon_knife_gypsy_jackknife" }, { "stiletto", "weapon_knife_stiletto" }, - { "talon", "weapon_knife_widowmaker" }, { "ursus", "weapon_knife_ursus" }, - { "css", "weapon_knife_css" }, { "paracord", "weapon_knife_cord" }, - { "survival", "weapon_knife_canis" }, { "nomad", "weapon_knife_outdoor" }, - { "skeleton", "weapon_knife_skeleton" }, { "default", "weapon_knife" } - }; private static readonly Dictionary weaponList = new() { {"weapon_deagle", "Desert Eagle"}, @@ -86,9 +73,28 @@ public class WeaponPaints : BasePlugin, IPluginConfig {"weapon_usp_silencer", "USP-S"}, {"weapon_cz75a", "CZ75-Auto"}, {"weapon_revolver", "R8 Revolver"}, - {"weapon_bayonet", "Bayonet Knife"}, - {"weapon_knife", "Default Knife"} + { "weapon_knife", "Default Knife" }, + { "weapon_knife_m9_bayonet", "M9 Bayonet" }, + { "weapon_knife_karambit", "Karambit" }, + { "weapon_bayonet", "Bayonet" }, + { "weapon_knife_survival_bowie", "Bowie Knife" }, + { "weapon_knife_butterfly", "Butterfly Knife" }, + { "weapon_knife_falchion", "Falchion Knife" }, + { "weapon_knife_flip", "Flip Knife" }, + { "weapon_knife_gut", "Gut Knife" }, + { "weapon_knife_tactical", "Huntsman Knife" }, + { "weapon_knife_push", "Shadow Daggers" }, + { "weapon_knife_gypsy_jackknife", "Navaja Knife" }, + { "weapon_knife_stiletto", "Stiletto Knife" }, + { "weapon_knife_widowmaker", "Talon Knife" }, + { "weapon_knife_ursus", "Ursus Knife" }, + { "weapon_knife_css", "Classic Knife" }, + { "weapon_knife_cord", "Paracord Knife" }, + { "weapon_knife_canis", "Survival Knife" }, + { "weapon_knife_outdoor", "Nomad Knife" }, + { "weapon_knife_skeleton", "Skeleton Knife" } }; + public override void Load(bool hotReload) { if (!Config.GlobalShare) @@ -105,6 +111,8 @@ public class WeaponPaints : BasePlugin, IPluginConfig RegisterEventHandler(OnRoundStart, HookMode.Pre); RegisterEventHandler(OnItemPickup, HookMode.Pre); + OnMapStart(string.Empty); + if (hotReload) { OnMapStart(string.Empty); @@ -141,8 +149,8 @@ public class WeaponPaints : BasePlugin, IPluginConfig } Config = config; - - ShowAd(); + Utility.Config = config; + Utility.ShowAd(ModuleVersion); } private void BuildDatabaseConnectionString() @@ -222,19 +230,27 @@ public class WeaponPaints : BasePlugin, IPluginConfig } private void RegisterCommands() { - AddCommand($"css_{Config.Additional.CommandSkin}", "Skins info", (player, info) => { if (player == null) return; OnCommandWS(player, info); }); - AddCommand($"css_{Config.Additional.CommandRefresh}", "Skins refresh", (player, info) => { if (player == null) return; OnCommandRefresh(player, info); }); + AddCommand($"css_{Config.Additional.CommandSkin}", "Skins info", (player, info) => + { + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; + OnCommandWS(player, info); + }); + AddCommand($"css_{Config.Additional.CommandRefresh}", "Skins refresh", (player, info) => + { + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; + OnCommandRefresh(player, info); + }); if (Config.Additional.CommandKillEnabled) { AddCommand($"css_{Config.Additional.CommandKill}", "kill yourself", (player, info) => { - if (player == null || !player.IsValid || !player.PlayerPawn.IsValid) - return; + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || !player.PlayerPawn.IsValid) return; player.PlayerPawn.Value.CommitSuicide(true, false); }); } } + private void IncompatibilityCheck() { // MatchZy @@ -307,10 +323,8 @@ public class WeaponPaints : BasePlugin, IPluginConfig } private void OnClientDisconnect(int playerSlot) { - int playerIndex = playerSlot + 1; CCSPlayerController player = Utilities.GetPlayerFromSlot(playerSlot); - if (player == null || !player.IsValid || player.IsBot) return; - // TODO: Clean up after player + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; if (Config.Additional.KnifeEnabled) g_playersKnife.Remove((int)player.EntityIndex!.Value.Value); if (Config.Additional.SkinEnabled) @@ -349,7 +363,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig if (@event.Defindex == 42 || @event.Defindex == 59) { CCSPlayerController? player = @event.Userid; - if (player == null || !player.IsValid || player.IsBot || !player.PawnIsAlive) return HookResult.Continue; + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || !player.PawnIsAlive) return HookResult.Continue; if (g_playersKnife.ContainsKey((int)player.EntityIndex!.Value.Value) && @@ -357,7 +371,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig { RemoveKnifeFromPlayer(player); - AddTimer(0.1f, () => + AddTimer(0.3f, () => { if (!PlayerHasKnife(player)) GiveKnifeToPlayer(player); @@ -396,7 +410,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig if (!pawn.IsValid) return; var playerIndex = (int)pawn.Controller.Value.EntityIndex!.Value.Value; var player = Utilities.GetPlayerFromIndex(playerIndex); - if (player == null || !player.IsValid || player.IsBot) return; + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; // TODO: Remove knife crashes here, needs another solution /*if (isKnife && g_playersKnife[(int)player.EntityIndex!.Value.Value] != "weapon_knife" && (weapon.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.AttributeManager.Item.ItemDefinitionIndex == 59)) { @@ -410,11 +424,15 @@ public class WeaponPaints : BasePlugin, IPluginConfig } private void ChangeWeaponAttributes(CBasePlayerWeapon? weapon, CCSPlayerController? player, bool isKnife = false) { - if (weapon == null || !weapon.IsValid || player == null || player.IsBot) return; + if (weapon == null || !weapon.IsValid || player == null || player.IsBot || player.IsHLTV) return; int playerIndex = (int)player.EntityIndex!.Value.Value; + if (!gPlayerWeaponPaints.ContainsKey(playerIndex)) return; - if (Config.Additional.GiveRandomSkin && !gPlayerWeaponPaints[playerIndex].ContainsKey(weapon.AttributeManager.Item.ItemDefinitionIndex)) + if (isKnife && !g_playersKnife.ContainsKey(playerIndex) || isKnife && g_playersKnife[playerIndex] == "weapon_knife") return; + + if (Config.Additional.GiveRandomSkin && + !gPlayerWeaponPaints[playerIndex].ContainsKey(weapon.AttributeManager.Item.ItemDefinitionIndex)) { // Random skins weapon.AttributeManager.Item.ItemID = 16384; @@ -467,10 +485,13 @@ public class WeaponPaints : BasePlugin, IPluginConfig } else if (Config.Additional.GiveRandomKnife) { - Random random = new Random(); + var knifeTypes = weaponList.Where(pair => pair.Key.StartsWith("weapon_knife")).ToDictionary(pair => pair.Key, pair => pair.Value); + + Random random = new(); int index = random.Next(knifeTypes.Count); - var randomKnife = knifeTypes.Values.ElementAt(index); - player.GiveNamedItem(randomKnife); + var randomKnifeClass = knifeTypes.Keys.ElementAt(index); + + player.GiveNamedItem(randomKnifeClass); } else { @@ -489,7 +510,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig if (weapon.IsValid && weapon.Value.IsValid) { //if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59) - if (weapon.Value.DesignerName.Contains("knife")) + if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) { weapon.Value.Remove(); return; @@ -522,7 +543,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig */ private void RefreshSkins(CCSPlayerController? player) { - if (player == null || !player.IsValid || player.IsBot || !player.PawnIsAlive) return; + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || !player.PawnIsAlive) return; AddTimer(0.18f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3")); AddTimer(0.25f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot2")); @@ -543,7 +564,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig { if (weapon.IsValid && weapon.Value.IsValid) { - if (weapon.Value.DesignerName.Contains("knife")) + if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) { return true; } @@ -554,58 +575,82 @@ public class WeaponPaints : BasePlugin, IPluginConfig private void SetupKnifeMenu() { if (!Config.Additional.KnifeEnabled) return; - var giveItemMenu = new ChatMenu(ReplaceTags(Config.Messages.KnifeMenuTitle)); + + var knivesOnly = weaponList + .Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")) + .ToDictionary(pair => pair.Key, pair => pair.Value); + + var giveItemMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.KnifeMenuTitle}")); var handleGive = (CCSPlayerController? player, ChatMenuOption option) => { - if (player != null && player.IsValid) + if (player != null && player.IsValid && !player.IsBot && !player.IsHLTV) { - string temp = ""; - if (knifeTypes.TryGetValue(option.Text, out var knife)) + var knifeName = option.Text; + var knifeKey = knivesOnly.FirstOrDefault(x => x.Value == knifeName).Key; + if (!string.IsNullOrEmpty(knifeKey)) { - g_playersKnife[(int)player.EntityIndex!.Value.Value] = knifeTypes[option.Text]; + string temp = ""; + if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenu)) { - temp = $"{Config.Prefix} {Config.Messages.ChosenKnifeMenu}".Replace("{KNIFE}", option.Text); - player.PrintToChat(ReplaceTags(temp)); + temp = $" {Config.Prefix} {Config.Messages.ChosenKnifeMenu}".Replace("{KNIFE}", knifeName); + player.PrintToChat(Utility.ReplaceTags(temp)); } + if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenuKill) && Config.Additional.CommandKillEnabled) { - temp = $"{Config.Prefix} {Config.Messages.ChosenKnifeMenuKill}"; - player.PrintToChat(ReplaceTags(temp)); + temp = $" {Config.Prefix} {Config.Messages.ChosenKnifeMenuKill}"; + player.PrintToChat(Utility.ReplaceTags(temp)); } + + g_playersKnife[(int)player.EntityIndex!.Value.Value] = knifeKey; + if (player.PawnIsAlive) { RemoveKnifeFromPlayer(player); - AddTimer(0.2f, () => + AddTimer(0.5f, () => { GiveKnifeToPlayer(player); }); } - Task.Run(() => SyncKnifeToDatabase((int)player.EntityIndex!.Value.Value, knife)); - /* Old way - RemoveKnifeFromPlayer(player); - AddTimer(0.1f, () => GiveKnifeToPlayer(player)); - */ + Task.Run(() => SyncKnifeToDatabase((int)player.EntityIndex!.Value.Value, knifeKey)); + } } }; - foreach (var knife in knifeTypes) + foreach (var knifePair in knivesOnly) { - giveItemMenu.AddMenuOption(knife.Key, handleGive); + giveItemMenu.AddMenuOption(knifePair.Value, handleGive); } - AddCommand($"css_{Config.Additional.CommandKnife}", "Knife Menu", (player, info) => { if (player == null) return; ChatMenus.OpenMenu(player, giveItemMenu); }); + AddCommand($"css_{Config.Additional.CommandKnife}", "Knife Menu", (player, info) => + { + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; + int playerIndex = (int)player.EntityIndex!.Value.Value; + + if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds) && playerIndex > 0 && playerIndex < commandCooldown.Length) + { + commandCooldown[playerIndex] = DateTime.UtcNow; + ChatMenus.OpenMenu(player, giveItemMenu); + return; + } + if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) + { + string temp = $" {Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; + player.PrintToChat(Utility.ReplaceTags(temp)); + } + }); } private void SetupSkinsMenu() { var classNamesByWeapon = weaponList.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); - var weaponSelectionMenu = new ChatMenu(ReplaceTags(Config.Messages.WeaponMenuTitle)); + var weaponSelectionMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.WeaponMenuTitle}")); // Function to handle skin selection for a specific weapon var handleWeaponSelection = (CCSPlayerController? player, ChatMenuOption option) => { - if (player == null || !player.IsValid) return; + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; int playerIndex = (int)player.EntityIndex!.Value.Value; string selectedWeapon = option.Text; @@ -618,7 +663,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig weaponName?.ToString() == selectedWeaponClassname )?.ToList(); - var skinSubMenu = new ChatMenu(ReplaceTags(Config.Messages.SkinMenuTitle).Replace("{WEAPON}", selectedWeapon)); + var skinSubMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.SkinMenuTitle}").Replace("{WEAPON}", selectedWeapon)); // Function to handle skin selection for the chosen weapon var handleSkinSelection = (CCSPlayerController? p, ChatMenuOption opt) => @@ -643,8 +688,8 @@ public class WeaponPaints : BasePlugin, IPluginConfig int.TryParse(weaponDefIndexObj.ToString(), out var weaponDefIndex) && int.TryParse(selectedPaintID, out var paintID)) { - string temp = $"{Config.Prefix} {Config.Messages.ChosenSkinMenu}".Replace("{SKIN}", selectedSkin); - p.PrintToChat(ReplaceTags(temp)); + string temp = $" {Config.Prefix} {Config.Messages.ChosenSkinMenu}".Replace("{SKIN}", selectedSkin); + p.PrintToChat(Utility.ReplaceTags(temp)); gPlayerWeaponPaints[playerIndex][weaponDefIndex] = paintID; gPlayerWeaponWear[playerIndex][weaponDefIndex] = 0.0f; gPlayerWeaponSeed[playerIndex][weaponDefIndex] = 0; @@ -685,20 +730,13 @@ public class WeaponPaints : BasePlugin, IPluginConfig string weaponName = weaponList[weaponClass]; weaponSelectionMenu.AddMenuOption(weaponName, handleWeaponSelection); } - - foreach (var knifeClass in knifeTypes.Keys) - { - string knifeName = knifeTypes[knifeClass]; - weaponSelectionMenu.AddMenuOption(knifeName, handleWeaponSelection); - } - // Command to open the weapon selection menu for players AddCommand($"css_{Config.Additional.CommandSkinSelection}", "Skins selection menu", (player, info) => { - if (player == null || !player.IsValid) return; + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; int playerIndex = (int)player.EntityIndex!.Value.Value; - if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds)) + if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds) && playerIndex > 0 && playerIndex < commandCooldown.Length) { commandCooldown[playerIndex] = DateTime.UtcNow; ChatMenus.OpenMenu(player, weaponSelectionMenu); @@ -707,16 +745,18 @@ public class WeaponPaints : BasePlugin, IPluginConfig if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) { string temp = $"{Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; - player.PrintToChat(ReplaceTags(temp)); + player.PrintToChat(Utility.ReplaceTags(temp)); } }); } + // [ConsoleCommand($"css_{Config.Additional.CommandRefresh}", "refreshskins")] private void OnCommandRefresh(CCSPlayerController? player, CommandInfo command) { if (!Config.Additional.CommandWpEnabled || !Config.Additional.SkinEnabled) return; - if (player == null || !player.IsValid || player.IsBot) return; + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; + string temp = ""; int playerIndex = (int)player.EntityIndex!.Value.Value; if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds)) @@ -726,7 +766,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig if (Config.Additional.KnifeEnabled) { RemoveKnifeFromPlayer(player); - AddTimer(0.2f, () => + AddTimer(0.3f, () => { GiveKnifeToPlayer(player); }); @@ -739,40 +779,40 @@ public class WeaponPaints : BasePlugin, IPluginConfig } if (!string.IsNullOrEmpty(Config.Messages.SuccessRefreshCommand)) { - temp = $"{Config.Prefix} {Config.Messages.SuccessRefreshCommand}"; - player.PrintToChat(ReplaceTags(temp)); + temp = $" {Config.Prefix} {Config.Messages.SuccessRefreshCommand}"; + player.PrintToChat(Utility.ReplaceTags(temp)); } return; } if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) { - temp = $"{Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; - player.PrintToChat(ReplaceTags(temp)); + temp = $" {Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; + player.PrintToChat(Utility.ReplaceTags(temp)); } } // [ConsoleCommand($"css_{Config.Additional.CommandSkin}", "weaponskins")] private void OnCommandWS(CCSPlayerController? player, CommandInfo command) { if (!Config.Additional.SkinEnabled) return; - if (player == null) return; + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; string temp = ""; if (!string.IsNullOrEmpty(Config.Messages.WebsiteMessageCommand)) { - temp = $"{Config.Prefix} {Config.Messages.WebsiteMessageCommand}"; - player.PrintToChat(ReplaceTags(temp)); + temp = $" {Config.Prefix} {Config.Messages.WebsiteMessageCommand}"; + player.PrintToChat(Utility.ReplaceTags(temp)); } if (!string.IsNullOrEmpty(Config.Messages.SynchronizeMessageCommand)) { - temp = $"{Config.Prefix} {Config.Messages.SynchronizeMessageCommand}"; - player.PrintToChat(ReplaceTags(temp)); + temp = $" {Config.Prefix} {Config.Messages.SynchronizeMessageCommand}"; + player.PrintToChat(Utility.ReplaceTags(temp)); } if (!Config.Additional.KnifeEnabled) return; if (!string.IsNullOrEmpty(Config.Messages.KnifeMessageCommand)) { - temp = $"{Config.Prefix} {Config.Messages.KnifeMessageCommand}"; - player.PrintToChat(ReplaceTags(temp)); + temp = $" {Config.Prefix} {Config.Messages.KnifeMessageCommand}"; + player.PrintToChat(Utility.ReplaceTags(temp)); } } private static CSkeletonInstance GetSkeletonInstance(CGameSceneNode node) @@ -785,7 +825,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig if (!Config.Additional.SkinEnabled) return; CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); - if (player == null || !player.IsValid || player.IsBot) return; + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; var steamId = new SteamID(player.SteamID); @@ -873,7 +913,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig } catch (Exception e) { - Log(e.Message); + Utility.Log(e.Message); return; } } @@ -883,7 +923,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig try { CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); - if (player == null || !player.IsValid || player.IsBot) return; + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; var steamId = new SteamID(player.SteamID); if (Config.GlobalShare) @@ -944,7 +984,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig } catch (Exception e) { - Log(e.Message); + Utility.Log(e.Message); return; } } @@ -965,14 +1005,14 @@ public class WeaponPaints : BasePlugin, IPluginConfig } catch (Exception e) { - Log(e.Message); + Utility.Log(e.Message); return; } } private async Task SyncWeaponPaintsToDatabase(CCSPlayerController? player) { - if (player == null || !player.IsValid || player.IsBot) return; + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; int playerIndex = (int)player.EntityIndex!.Value.Value; @@ -986,7 +1026,6 @@ public class WeaponPaints : BasePlugin, IPluginConfig foreach (var weaponDefIndex in gPlayerWeaponPaints[playerIndex].Keys) { - Console.WriteLine("WeaponDEFINDEX : " + weaponDefIndex); int paintId = gPlayerWeaponPaints[playerIndex][weaponDefIndex]; float wear = gPlayerWeaponWear.TryGetValue(playerIndex, out var wearDictionary) && wearDictionary.TryGetValue(weaponDefIndex, out var retrievedWear) @@ -1018,26 +1057,6 @@ public class WeaponPaints : BasePlugin, IPluginConfig await connection.CloseAsync(); } - private string ReplaceTags(string message) - { - if (message.Contains('{')) - { - string modifiedValue = message; - modifiedValue = modifiedValue.Replace("{WEBSITE}", Config.Website); - foreach (FieldInfo field in typeof(ChatColors).GetFields()) - { - string pattern = $"{{{field.Name}}}"; - if (message.Contains(pattern, StringComparison.OrdinalIgnoreCase)) - { - modifiedValue = modifiedValue.Replace(pattern, field.GetValue(null)!.ToString(), StringComparison.OrdinalIgnoreCase); - } - } - return modifiedValue; - } - - return message; - } - private static int GetRandomPaint(int defindex) { Random rnd = new Random(); @@ -1077,27 +1096,4 @@ public class WeaponPaints : BasePlugin, IPluginConfig throw new FileNotFoundException("File not found.", filePath); } } - - private static void Log(string message) - { - Console.BackgroundColor = ConsoleColor.DarkGray; - Console.ForegroundColor = ConsoleColor.Cyan; - Console.WriteLine("[WeaponPaints] " + message); - Console.ResetColor(); - } - private void ShowAd() - { - Console.WriteLine(" "); - Console.WriteLine(" _ _ _______ _______ _______ _______ __ _ _______ _______ ___ __ _ _______ _______ "); - Console.WriteLine("| | _ | || || _ || || || | | || || _ || | | | | || || |"); - Console.WriteLine("| || || || ___|| |_| || _ || _ || |_| || _ || |_| || | | |_| ||_ _|| _____|"); - Console.WriteLine("| || |___ | || |_| || | | || || |_| || || | | | | | | |_____ "); - Console.WriteLine("| || ___|| || ___|| |_| || _ || ___|| || | | _ | | | |_____ |"); - Console.WriteLine("| _ || |___ | _ || | | || | | || | | _ || | | | | | | | _____| |"); - Console.WriteLine("|__| |__||_______||__| |__||___| |_______||_| |__||___| |__| |__||___| |_| |__| |___| |_______|"); - Console.WriteLine(" >> Version: " + ModuleVersion); - Console.WriteLine(" >> GitHub: https://github.com/Nereziel/cs2-WeaponPaints"); - Console.WriteLine(" "); - - } } From fe5cc9a82a00ccf3d101a4cf17c62b647d2a37b2 Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Sat, 25 Nov 2023 23:17:56 +0100 Subject: [PATCH 02/47] Update WeaponPaints.cs --- WeaponPaints.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/WeaponPaints.cs b/WeaponPaints.cs index b456565b..54849181 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -345,6 +345,11 @@ public class WeaponPaints : BasePlugin, IPluginConfig GiveKnifeToPlayer(player); } + if (Config.Additional.SkinVisibilityFix) + { + AddTimer(0.3f, () => RefreshSkins(player)); + } + return HookResult.Continue; } private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) From 7bb97af619a88944304a0941f1b8eb4a4db36777 Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Sun, 26 Nov 2023 15:16:01 +0100 Subject: [PATCH 03/47] IMPORTANT KNIVES FIX --- Utility.cs | 9 +++- WeaponPaints.cs | 121 +++++++++++++++++++++++++++++------------------- 2 files changed, 82 insertions(+), 48 deletions(-) diff --git a/Utility.cs b/Utility.cs index 8b8011fb..820acca6 100644 --- a/Utility.cs +++ b/Utility.cs @@ -1,4 +1,6 @@ -using CounterStrikeSharp.API.Modules.Utils; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Entities; +using CounterStrikeSharp.API.Modules.Utils; using System.Reflection; namespace WeaponPaints @@ -7,6 +9,11 @@ namespace WeaponPaints { public static WeaponPaintsConfig? Config { get; set; } + public static bool IsPlayerValid(CCSPlayerController? player) + { + return (player != null && player.IsValid && !player.IsBot && !player.IsHLTV); + } + public static string ReplaceTags(string message) { if (message.Contains('{')) diff --git a/WeaponPaints.cs b/WeaponPaints.cs index 54849181..9fb63cae 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -35,6 +35,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig private Dictionary> gPlayerWeaponSeed = new(); private Dictionary> gPlayerWeaponWear = new(); private Dictionary g_playersKnife = new(); + private List g_changedKnife = new(); private static List skinsList = new List(); private static readonly Dictionary weaponList = new() @@ -111,8 +112,6 @@ public class WeaponPaints : BasePlugin, IPluginConfig RegisterEventHandler(OnRoundStart, HookMode.Pre); RegisterEventHandler(OnItemPickup, HookMode.Pre); - OnMapStart(string.Empty); - if (hotReload) { OnMapStart(string.Empty); @@ -152,7 +151,6 @@ public class WeaponPaints : BasePlugin, IPluginConfig Utility.Config = config; Utility.ShowAd(ModuleVersion); } - private void BuildDatabaseConnectionString() { var builder = new MySqlConnectionStringBuilder @@ -232,25 +230,24 @@ public class WeaponPaints : BasePlugin, IPluginConfig { AddCommand($"css_{Config.Additional.CommandSkin}", "Skins info", (player, info) => { - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; + if (!Utility.IsPlayerValid(player)) return; OnCommandWS(player, info); }); AddCommand($"css_{Config.Additional.CommandRefresh}", "Skins refresh", (player, info) => { - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; + if (!Utility.IsPlayerValid(player)) return; OnCommandRefresh(player, info); }); if (Config.Additional.CommandKillEnabled) { AddCommand($"css_{Config.Additional.CommandKill}", "kill yourself", (player, info) => { - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || !player.PlayerPawn.IsValid) return; + if (!Utility.IsPlayerValid(player) || !player!.PlayerPawn.IsValid) return; player.PlayerPawn.Value.CommitSuicide(true, false); }); } } - private void IncompatibilityCheck() { // MatchZy @@ -324,7 +321,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig private void OnClientDisconnect(int playerSlot) { CCSPlayerController player = Utilities.GetPlayerFromSlot(playerSlot); - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; + if (!Utility.IsPlayerValid(player)) return; if (Config.Additional.KnifeEnabled) g_playersKnife.Remove((int)player.EntityIndex!.Value.Value); if (Config.Additional.SkinEnabled) @@ -368,13 +365,14 @@ public class WeaponPaints : BasePlugin, IPluginConfig if (@event.Defindex == 42 || @event.Defindex == 59) { CCSPlayerController? player = @event.Userid; - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || !player.PawnIsAlive) return HookResult.Continue; + if (!Utility.IsPlayerValid(player) || !player.PawnIsAlive) return HookResult.Continue; if (g_playersKnife.ContainsKey((int)player.EntityIndex!.Value.Value) && g_playersKnife[(int)player.EntityIndex!.Value.Value] != "weapon_knife") { - RemoveKnifeFromPlayer(player); + if (PlayerHasKnife(player)) + RemoveKnifeFromPlayer(player); AddTimer(0.3f, () => { @@ -409,13 +407,30 @@ public class WeaponPaints : BasePlugin, IPluginConfig { if (!weapon.IsValid) return; if (weapon.OwnerEntity.Value == null) return; + if (!weapon.OwnerEntity.Value.EntityIndex.HasValue) + { + for (int i = 1; i <= Server.MaxPlayers; i++) + { + CCSPlayerController? ghostPlayer = Utilities.GetPlayerFromIndex(i); + if (!Utility.IsPlayerValid(ghostPlayer)) continue; + if (g_changedKnife.Contains((int)ghostPlayer.EntityIndex!.Value.Value)) + { + ChangeWeaponAttributes(weapon, ghostPlayer, isKnife); + g_changedKnife.Remove((int)ghostPlayer.EntityIndex!.Value.Value); + break; + } + } + return; + } + if (!weapon.OwnerEntity.Value.EntityIndex.HasValue) return; int weaponOwner = (int)weapon.OwnerEntity.Value.EntityIndex.Value.Value; var pawn = new CBasePlayerPawn(NativeAPI.GetEntityFromIndex(weaponOwner)); if (!pawn.IsValid) return; var playerIndex = (int)pawn.Controller.Value.EntityIndex!.Value.Value; var player = Utilities.GetPlayerFromIndex(playerIndex); - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; + if (!Utility.IsPlayerValid(player)) return; + // TODO: Remove knife crashes here, needs another solution /*if (isKnife && g_playersKnife[(int)player.EntityIndex!.Value.Value] != "weapon_knife" && (weapon.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.AttributeManager.Item.ItemDefinitionIndex == 59)) { @@ -429,9 +444,9 @@ public class WeaponPaints : BasePlugin, IPluginConfig } private void ChangeWeaponAttributes(CBasePlayerWeapon? weapon, CCSPlayerController? player, bool isKnife = false) { - if (weapon == null || !weapon.IsValid || player == null || player.IsBot || player.IsHLTV) return; + if (weapon == null || !weapon.IsValid || !Utility.IsPlayerValid(player)) return; - int playerIndex = (int)player.EntityIndex!.Value.Value; + int playerIndex = (int)player!.EntityIndex!.Value.Value; if (!gPlayerWeaponPaints.ContainsKey(playerIndex)) return; if (isKnife && !g_playersKnife.ContainsKey(playerIndex) || isKnife && g_playersKnife[playerIndex] == "weapon_knife") return; @@ -490,7 +505,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig } else if (Config.Additional.GiveRandomKnife) { - var knifeTypes = weaponList.Where(pair => pair.Key.StartsWith("weapon_knife")).ToDictionary(pair => pair.Key, pair => pair.Value); + var knifeTypes = weaponList.Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")).ToDictionary(pair => pair.Key, pair => pair.Value); Random random = new(); int index = random.Next(knifeTypes.Count); @@ -507,18 +522,29 @@ public class WeaponPaints : BasePlugin, IPluginConfig private void RemoveKnifeFromPlayer(CCSPlayerController? player) { if (player == null || !player.IsValid || !player.PawnIsAlive) return; - var weapons = player.PlayerPawn.Value.WeaponServices!.MyWeapons; + if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return; + + var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; if (weapons != null && weapons.Count > 0) { + CCSPlayer_ItemServices service = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle); + var dropWeapon = VirtualFunction.CreateVoid(service.Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon")); + foreach (var weapon in weapons) { - if (weapon.IsValid && weapon.Value.IsValid) + if (weapon != null && weapon.IsValid && weapon.Value.IsValid) { //if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59) if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) { - weapon.Value.Remove(); - return; + NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3"); + AddTimer(0.2f, () => + { + CEntityInstance knife = new(weapon.Value.Handle); + dropWeapon(service.Handle, weapon.Handle); + AddTimer(0.3f, () => knife.Remove()); + }); + break; } } } @@ -548,7 +574,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig */ private void RefreshSkins(CCSPlayerController? player) { - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || !player.PawnIsAlive) return; + if (!Utility.IsPlayerValid(player) || !player!.PawnIsAlive) return; AddTimer(0.18f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3")); AddTimer(0.25f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot2")); @@ -567,7 +593,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig if (weapons == null || weapons.Count <= 0) return false; foreach (var weapon in weapons) { - if (weapon.IsValid && weapon.Value.IsValid) + if (weapon != null && weapon.IsValid && weapon.Value.IsValid) { if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) { @@ -588,7 +614,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig var giveItemMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.KnifeMenuTitle}")); var handleGive = (CCSPlayerController? player, ChatMenuOption option) => { - if (player != null && player.IsValid && !player.IsBot && !player.IsHLTV) + if (Utility.IsPlayerValid(player)) { var knifeName = option.Text; var knifeKey = knivesOnly.FirstOrDefault(x => x.Value == knifeName).Key; @@ -599,24 +625,24 @@ public class WeaponPaints : BasePlugin, IPluginConfig if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenu)) { temp = $" {Config.Prefix} {Config.Messages.ChosenKnifeMenu}".Replace("{KNIFE}", knifeName); - player.PrintToChat(Utility.ReplaceTags(temp)); + player!.PrintToChat(Utility.ReplaceTags(temp)); } if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenuKill) && Config.Additional.CommandKillEnabled) { temp = $" {Config.Prefix} {Config.Messages.ChosenKnifeMenuKill}"; - player.PrintToChat(Utility.ReplaceTags(temp)); + player!.PrintToChat(Utility.ReplaceTags(temp)); } - g_playersKnife[(int)player.EntityIndex!.Value.Value] = knifeKey; + g_playersKnife[(int)player!.EntityIndex!.Value.Value] = knifeKey; - if (player.PawnIsAlive) + if (player!.PawnIsAlive) { - RemoveKnifeFromPlayer(player); - AddTimer(0.5f, () => - { - GiveKnifeToPlayer(player); - }); + if (PlayerHasKnife(player)) + RemoveKnifeFromPlayer(player); + + g_changedKnife.Add((int)player.EntityIndex!.Value.Value); + GiveKnifeToPlayer(player); } Task.Run(() => SyncKnifeToDatabase((int)player.EntityIndex!.Value.Value, knifeKey)); @@ -630,8 +656,8 @@ public class WeaponPaints : BasePlugin, IPluginConfig } AddCommand($"css_{Config.Additional.CommandKnife}", "Knife Menu", (player, info) => { - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; - int playerIndex = (int)player.EntityIndex!.Value.Value; + if (!Utility.IsPlayerValid(player)) return; + int playerIndex = (int)player!.EntityIndex!.Value.Value; if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds) && playerIndex > 0 && playerIndex < commandCooldown.Length) { @@ -655,9 +681,9 @@ public class WeaponPaints : BasePlugin, IPluginConfig // Function to handle skin selection for a specific weapon var handleWeaponSelection = (CCSPlayerController? player, ChatMenuOption option) => { - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; + if (!Utility.IsPlayerValid(player)) return; - int playerIndex = (int)player.EntityIndex!.Value.Value; + int playerIndex = (int)player!.EntityIndex!.Value.Value; string selectedWeapon = option.Text; if (classNamesByWeapon.TryGetValue(selectedWeapon, out string? selectedWeaponClassname)) { @@ -738,8 +764,8 @@ public class WeaponPaints : BasePlugin, IPluginConfig // Command to open the weapon selection menu for players AddCommand($"css_{Config.Additional.CommandSkinSelection}", "Skins selection menu", (player, info) => { - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; - int playerIndex = (int)player.EntityIndex!.Value.Value; + if (!Utility.IsPlayerValid(player)) return; + int playerIndex = (int)player!.EntityIndex!.Value.Value; if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds) && playerIndex > 0 && playerIndex < commandCooldown.Length) { @@ -760,17 +786,18 @@ public class WeaponPaints : BasePlugin, IPluginConfig private void OnCommandRefresh(CCSPlayerController? player, CommandInfo command) { if (!Config.Additional.CommandWpEnabled || !Config.Additional.SkinEnabled) return; - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; + if (!Utility.IsPlayerValid(player)) return; string temp = ""; - int playerIndex = (int)player.EntityIndex!.Value.Value; + int playerIndex = (int)player!.EntityIndex!.Value.Value; if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds)) { commandCooldown[playerIndex] = DateTime.UtcNow; Task.Run(async () => await GetWeaponPaintsFromDatabase(playerIndex)); if (Config.Additional.KnifeEnabled) { - RemoveKnifeFromPlayer(player); + if (PlayerHasKnife(player)) + RemoveKnifeFromPlayer(player); AddTimer(0.3f, () => { GiveKnifeToPlayer(player); @@ -799,25 +826,25 @@ public class WeaponPaints : BasePlugin, IPluginConfig private void OnCommandWS(CCSPlayerController? player, CommandInfo command) { if (!Config.Additional.SkinEnabled) return; - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; + if (!Utility.IsPlayerValid(player)) return; string temp = ""; if (!string.IsNullOrEmpty(Config.Messages.WebsiteMessageCommand)) { temp = $" {Config.Prefix} {Config.Messages.WebsiteMessageCommand}"; - player.PrintToChat(Utility.ReplaceTags(temp)); + player!.PrintToChat(Utility.ReplaceTags(temp)); } if (!string.IsNullOrEmpty(Config.Messages.SynchronizeMessageCommand)) { temp = $" {Config.Prefix} {Config.Messages.SynchronizeMessageCommand}"; - player.PrintToChat(Utility.ReplaceTags(temp)); + player!.PrintToChat(Utility.ReplaceTags(temp)); } if (!Config.Additional.KnifeEnabled) return; if (!string.IsNullOrEmpty(Config.Messages.KnifeMessageCommand)) { temp = $" {Config.Prefix} {Config.Messages.KnifeMessageCommand}"; - player.PrintToChat(Utility.ReplaceTags(temp)); + player!.PrintToChat(Utility.ReplaceTags(temp)); } } private static CSkeletonInstance GetSkeletonInstance(CGameSceneNode node) @@ -830,7 +857,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig if (!Config.Additional.SkinEnabled) return; CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; + if (!Utility.IsPlayerValid(player)) return; var steamId = new SteamID(player.SteamID); @@ -928,7 +955,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig try { CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; + if (!Utility.IsPlayerValid(player)) return; var steamId = new SteamID(player.SteamID); if (Config.GlobalShare) @@ -1017,9 +1044,9 @@ public class WeaponPaints : BasePlugin, IPluginConfig private async Task SyncWeaponPaintsToDatabase(CCSPlayerController? player) { - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; + if (!Utility.IsPlayerValid(player)) return; - int playerIndex = (int)player.EntityIndex!.Value.Value; + int playerIndex = (int)player!.EntityIndex!.Value.Value; string steamId = new SteamID(player.SteamID).SteamId64.ToString(); From a42e9a37fcbf8e86e06ba81edcc80ebad90b68ab Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Sun, 26 Nov 2023 18:19:15 +0100 Subject: [PATCH 04/47] TEMP FIX FOR KNIVES --- WeaponPaints.cs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/WeaponPaints.cs b/WeaponPaints.cs index 9fb63cae..e713f6f2 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -111,6 +111,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig RegisterEventHandler(OnPlayerSpawn); RegisterEventHandler(OnRoundStart, HookMode.Pre); RegisterEventHandler(OnItemPickup, HookMode.Pre); + RegisterEventHandler(OnItemRemove); if (hotReload) { @@ -137,6 +138,13 @@ public class WeaponPaints : BasePlugin, IPluginConfig LoadSkinsFromFile(ModuleDirectory + "/skins.json"); } + + private HookResult OnItemRemove(EventItemRemove @event, GameEventInfo info) + { + Console.WriteLine(@event.Defindex); + return HookResult.Continue; + } + public void OnConfigParsed(WeaponPaintsConfig config) { if (!config.GlobalShare) @@ -330,8 +338,8 @@ public class WeaponPaints : BasePlugin, IPluginConfig private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) { - var player = @event.Userid; - if (!player.IsValid || !player.PlayerPawn.IsValid) + CCSPlayerController? player = @event.Userid; + if (player == null || !player.IsValid || !player.PlayerPawn.IsValid) { return HookResult.Continue; } @@ -528,7 +536,7 @@ public class WeaponPaints : BasePlugin, IPluginConfig if (weapons != null && weapons.Count > 0) { CCSPlayer_ItemServices service = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle); - var dropWeapon = VirtualFunction.CreateVoid(service.Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon")); + //var dropWeapon = VirtualFunction.CreateVoid(service.Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon")); foreach (var weapon in weapons) { @@ -538,12 +546,16 @@ public class WeaponPaints : BasePlugin, IPluginConfig if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) { NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3"); - AddTimer(0.2f, () => - { - CEntityInstance knife = new(weapon.Value.Handle); - dropWeapon(service.Handle, weapon.Handle); - AddTimer(0.3f, () => knife.Remove()); + AddTimer(0.5f, () => service.DropActivePlayerWeapon(weapon.Value)); + + /* + CEntityInstance knife = new(weapon.Value.Handle); + AddTimer(1.0f, () => { + knife.Remove(); + if (knife != null && knife.IsValid && player.PawnIsAlive) }); + */ + break; } } @@ -639,7 +651,9 @@ public class WeaponPaints : BasePlugin, IPluginConfig if (player!.PawnIsAlive) { if (PlayerHasKnife(player)) + { RemoveKnifeFromPlayer(player); + } g_changedKnife.Add((int)player.EntityIndex!.Value.Value); GiveKnifeToPlayer(player); From 7e5485b2092a49395f031237746593de972d397b Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Mon, 27 Nov 2023 01:52:23 +0100 Subject: [PATCH 05/47] Major changes --- Commands.cs | 287 +++++++++++ Events.cs | 183 +++++++ Utility.cs | 94 +++- WeaponAction.cs | 222 +++++++++ WeaponInfo.cs | 15 + WeaponPaints.cs | 1011 ++------------------------------------ WeaponSynchronization.cs | 272 ++++++++++ 7 files changed, 1103 insertions(+), 981 deletions(-) create mode 100644 Commands.cs create mode 100644 Events.cs create mode 100644 WeaponAction.cs create mode 100644 WeaponInfo.cs create mode 100644 WeaponSynchronization.cs diff --git a/Commands.cs b/Commands.cs new file mode 100644 index 00000000..9e8bc041 --- /dev/null +++ b/Commands.cs @@ -0,0 +1,287 @@ +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Commands; +using CounterStrikeSharp.API.Modules.Entities; +using CounterStrikeSharp.API.Modules.Menu; + +namespace WeaponPaints +{ + public partial class WeaponPaints + { + private void RegisterCommands() + { + AddCommand($"css_{Config.Additional.CommandSkin}", "Skins info", (player, info) => + { + if (!Utility.IsPlayerValid(player)) return; + OnCommandWS(player, info); + }); + AddCommand($"css_{Config.Additional.CommandRefresh}", "Skins refresh", (player, info) => + { + if (!Utility.IsPlayerValid(player)) return; + OnCommandRefresh(player, info); + }); + if (Config.Additional.CommandKillEnabled) + { + AddCommand($"css_{Config.Additional.CommandKill}", "kill yourself", (player, info) => + { + if (!Utility.IsPlayerValid(player) || !player!.PlayerPawn.IsValid) return; + + player.PlayerPawn.Value.CommitSuicide(true, false); + }); + } + } + private void SetupKnifeMenu() + { + if (!Config.Additional.KnifeEnabled) return; + + var knivesOnly = weaponList + .Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")) + .ToDictionary(pair => pair.Key, pair => pair.Value); + + var giveItemMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.KnifeMenuTitle}")); + var handleGive = (CCSPlayerController? player, ChatMenuOption option) => + { + if (Utility.IsPlayerValid(player)) + { + var knifeName = option.Text; + var knifeKey = knivesOnly.FirstOrDefault(x => x.Value == knifeName).Key; + if (!string.IsNullOrEmpty(knifeKey)) + { + string temp = ""; + + if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenu)) + { + temp = $" {Config.Prefix} {Config.Messages.ChosenKnifeMenu}".Replace("{KNIFE}", knifeName); + player!.PrintToChat(Utility.ReplaceTags(temp)); + } + + if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenuKill) && Config.Additional.CommandKillEnabled) + { + temp = $" {Config.Prefix} {Config.Messages.ChosenKnifeMenuKill}"; + player!.PrintToChat(Utility.ReplaceTags(temp)); + } + + g_playersKnife[(int)player!.EntityIndex!.Value.Value] = knifeKey; + + if (player!.PawnIsAlive) + { + g_changedKnife.Add((int)player.EntityIndex!.Value.Value); + if (PlayerHasKnife(player)) + { + RefreshPlayerKnife(player); + } + + /* + AddTimer(1.0f, () => GiveKnifeToPlayer(player)); + */ + } + if (weaponSync != null) + Task.Run(() => weaponSync.SyncKnifeToDatabase((int)player.EntityIndex!.Value.Value, knifeKey)); + } + } + }; + foreach (var knifePair in knivesOnly) + { + giveItemMenu.AddMenuOption(knifePair.Value, handleGive); + } + AddCommand($"css_{Config.Additional.CommandKnife}", "Knife Menu", (player, info) => + { + if (!Utility.IsPlayerValid(player)) return; + int playerIndex = (int)player!.EntityIndex!.Value.Value; + + if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds) && playerIndex > 0 && playerIndex < commandCooldown.Length) + { + commandCooldown[playerIndex] = DateTime.UtcNow; + ChatMenus.OpenMenu(player, giveItemMenu); + return; + } + if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) + { + string temp = $" {Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; + player.PrintToChat(Utility.ReplaceTags(temp)); + } + }); + } + + private void SetupSkinsMenu() + { + var classNamesByWeapon = weaponList.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); + var weaponSelectionMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.WeaponMenuTitle}")); + + // Function to handle skin selection for a specific weapon + var handleWeaponSelection = (CCSPlayerController? player, ChatMenuOption option) => + { + if (!Utility.IsPlayerValid(player)) return; + + int playerIndex = (int)player!.EntityIndex!.Value.Value; + string selectedWeapon = option.Text; + if (classNamesByWeapon.TryGetValue(selectedWeapon, out string? selectedWeaponClassname)) + { + if (selectedWeaponClassname == null) return; + var skinsForSelectedWeapon = skinsList?.Where(skin => + skin != null && + skin.TryGetValue("weapon_name", out var weaponName) && + weaponName?.ToString() == selectedWeaponClassname + )?.ToList(); + + var skinSubMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.SkinMenuTitle}").Replace("{WEAPON}", selectedWeapon)); + + // Function to handle skin selection for the chosen weapon + var handleSkinSelection = (CCSPlayerController? p, ChatMenuOption opt) => + { + if (p == null || !p.IsValid) return; + + var steamId = new SteamID(player.SteamID); + var firstSkin = skinsList?.FirstOrDefault(skin => + { + if (skin != null && skin.TryGetValue("weapon_name", out var weaponName)) + { + return weaponName?.ToString() == selectedWeaponClassname; + } + return false; + }); + string selectedSkin = opt.Text; + string selectedPaintID = selectedSkin.Split('(')[1].Trim(')').Trim(); + + if (firstSkin != null && + firstSkin.TryGetValue("weapon_defindex", out var weaponDefIndexObj) && + weaponDefIndexObj != null && + int.TryParse(weaponDefIndexObj.ToString(), out var weaponDefIndex) && + int.TryParse(selectedPaintID, out var paintID)) + { + string temp = $" {Config.Prefix} {Config.Messages.ChosenSkinMenu}".Replace("{SKIN}", selectedSkin); + p.PrintToChat(Utility.ReplaceTags(temp)); + + if (!gPlayerWeaponsInfo[playerIndex].ContainsKey(weaponDefIndex)) + { + gPlayerWeaponsInfo[playerIndex][weaponDefIndex] = new WeaponInfo(); + } + + gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Paint = paintID; + gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Wear = 0.0f; + gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Seed = 0; + + if (weaponSync == null) return; + Task.Run(async () => + { + await weaponSync.SyncWeaponPaintsToDatabase(player); + }); + } + }; + + // Add skin options to the submenu for the selected weapon + if (skinsForSelectedWeapon != null) + { + foreach (var skin in skinsForSelectedWeapon.Where(s => s != null)) + { + if (skin.TryGetValue("paint_name", out var paintNameObj) && skin.TryGetValue("paint", out var paintObj)) + { + var paintName = paintNameObj?.ToString(); + var paint = paintObj?.ToString(); + + if (!string.IsNullOrEmpty(paintName) && !string.IsNullOrEmpty(paint)) + { + skinSubMenu.AddMenuOption($"{paintName} ({paint})", handleSkinSelection); + } + } + } + } + + // Open the submenu for skin selection of the chosen weapon + ChatMenus.OpenMenu(player, skinSubMenu); + } + }; + + // Add weapon options to the weapon selection menu + foreach (var weaponClass in weaponList.Keys) + { + string weaponName = weaponList[weaponClass]; + weaponSelectionMenu.AddMenuOption(weaponName, handleWeaponSelection); + } + // Command to open the weapon selection menu for players + AddCommand($"css_{Config.Additional.CommandSkinSelection}", "Skins selection menu", (player, info) => + { + if (!Utility.IsPlayerValid(player)) return; + int playerIndex = (int)player!.EntityIndex!.Value.Value; + + if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds) && playerIndex > 0 && playerIndex < commandCooldown.Length) + { + commandCooldown[playerIndex] = DateTime.UtcNow; + ChatMenus.OpenMenu(player, weaponSelectionMenu); + return; + } + if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) + { + string temp = $"{Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; + player.PrintToChat(Utility.ReplaceTags(temp)); + } + + }); + } + + private void OnCommandRefresh(CCSPlayerController? player, CommandInfo command) + { + if (!Config.Additional.CommandWpEnabled || !Config.Additional.SkinEnabled) return; + if (!Utility.IsPlayerValid(player)) return; + string temp = ""; + int playerIndex = (int)player!.EntityIndex!.Value.Value; + if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds)) + { + commandCooldown[playerIndex] = DateTime.UtcNow; + if (weaponSync != null) + Task.Run(async () => await weaponSync.GetWeaponPaintsFromDatabase(playerIndex)); + if (Config.Additional.KnifeEnabled) + { + if (PlayerHasKnife(player)) + RefreshPlayerKnife(player); + /* + AddTimer(1.0f, () => + { + GiveKnifeToPlayer(player); + }); + */ + if (weaponSync != null) + Task.Run(async () => await weaponSync.GetKnifeFromDatabase(playerIndex)); + /* + RemoveKnifeFromPlayer(player); + AddTimer(0.2f, () => GiveKnifeToPlayer(player)); + */ + } + if (!string.IsNullOrEmpty(Config.Messages.SuccessRefreshCommand)) + { + temp = $" {Config.Prefix} {Config.Messages.SuccessRefreshCommand}"; + player.PrintToChat(Utility.ReplaceTags(temp)); + } + return; + } + if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) + { + temp = $" {Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; + player.PrintToChat(Utility.ReplaceTags(temp)); + } + } + private void OnCommandWS(CCSPlayerController? player, CommandInfo command) + { + if (!Config.Additional.SkinEnabled) return; + if (!Utility.IsPlayerValid(player)) return; + + string temp = ""; + + if (!string.IsNullOrEmpty(Config.Messages.WebsiteMessageCommand)) + { + temp = $" {Config.Prefix} {Config.Messages.WebsiteMessageCommand}"; + player!.PrintToChat(Utility.ReplaceTags(temp)); + } + if (!string.IsNullOrEmpty(Config.Messages.SynchronizeMessageCommand)) + { + temp = $" {Config.Prefix} {Config.Messages.SynchronizeMessageCommand}"; + player!.PrintToChat(Utility.ReplaceTags(temp)); + } + if (!Config.Additional.KnifeEnabled) return; + if (!string.IsNullOrEmpty(Config.Messages.KnifeMessageCommand)) + { + temp = $" {Config.Prefix} {Config.Messages.KnifeMessageCommand}"; + player!.PrintToChat(Utility.ReplaceTags(temp)); + } + } + } +} diff --git a/Events.cs b/Events.cs new file mode 100644 index 00000000..f94afe5c --- /dev/null +++ b/Events.cs @@ -0,0 +1,183 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Entities; +using CounterStrikeSharp.API.Core.Attributes; +using CounterStrikeSharp.API.Core.Attributes.Registration; +using CounterStrikeSharp.API.Modules.Utils; +using CounterStrikeSharp.API.Modules.Admin; +using CounterStrikeSharp.API.Modules.Commands; +using CounterStrikeSharp.API.Modules.Cvars; +using CounterStrikeSharp.API.Modules.Memory; +using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; + +namespace WeaponPaints +{ + public partial class WeaponPaints + { + private void RegisterEvents() + { + RegisterListener(OnEntitySpawned); + RegisterEventHandler(OnEventItemPurchasePost); + RegisterListener(OnClientAuthorized); + RegisterListener(OnClientDisconnect); + RegisterListener(OnMapStart); + RegisterEventHandler(OnPlayerSpawn); + RegisterEventHandler(OnRoundStart, HookMode.Pre); + RegisterEventHandler(OnItemPickup); + + } + private void OnMapStart(string mapName) + { + if (!Config.Additional.KnifeEnabled) return; + // TODO + // needed for now + AddTimer(2.0f, () => + { + NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); + NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); + }); + } + private void OnClientAuthorized(int playerSlot, SteamID steamID) + { + int playerIndex = playerSlot + 1; + Task.Run(async () => + { + if (Config.Additional.KnifeEnabled && weaponSync != null) + await weaponSync.GetKnifeFromDatabase(playerIndex); + if (Config.Additional.SkinEnabled && weaponSync != null) + await weaponSync.GetWeaponPaintsFromDatabase(playerIndex); + }); + } + private void OnClientDisconnect(int playerSlot) + { + CCSPlayerController player = Utilities.GetPlayerFromSlot(playerSlot); + + if (player == null || !player.IsValid || player.IsHLTV) return; + + if (Config.Additional.KnifeEnabled) + g_playersKnife.Remove((int)player.EntityIndex!.Value.Value); + if (Config.Additional.SkinEnabled) + gPlayerWeaponsInfo.Remove((int)player.EntityIndex!.Value.Value); + } + + private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) + { + CCSPlayerController? player = @event.Userid; + if (player == null || !player.IsValid || !player.PlayerPawn.IsValid) + { + return HookResult.Continue; + } + + if (Config.Additional.KnifeEnabled) + { + g_knifePickupCount[(int)player.EntityIndex!.Value.Value] = 0; + if (!PlayerHasKnife(player)) + GiveKnifeToPlayer(player); + } + + if (Config.Additional.SkinVisibilityFix) + { + AddTimer(0.3f, () => RefreshSkins(player)); + } + + return HookResult.Continue; + } + private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) + { + NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); + NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); + + return HookResult.Continue; + } + private HookResult OnItemPickup(EventItemPickup @event, GameEventInfo info) + { + if (@event.Defindex == 42 || @event.Defindex == 59) + { + CCSPlayerController? player = @event.Userid; + if (!Utility.IsPlayerValid(player) || !player.PawnIsAlive || g_knifePickupCount[(int)player.EntityIndex!.Value.Value] >= 1) return HookResult.Continue; + + if (g_playersKnife.ContainsKey((int)player.EntityIndex!.Value.Value) + && + g_playersKnife[(int)player.EntityIndex!.Value.Value] != "weapon_knife") + { + g_knifePickupCount[(int)player.EntityIndex!.Value.Value]++; + RefreshPlayerKnife(player); + /* + if (!PlayerHasKnife(player)) + GiveKnifeToPlayer(player); + */ + if (Config.Additional.SkinVisibilityFix) + { + AddTimer(0.25f, () => RefreshSkins(player)); + } + } + } + return HookResult.Continue; + } + + private void OnEntitySpawned(CEntityInstance entity) + { + if (!Config.Additional.SkinEnabled) return; + var designerName = entity.DesignerName; + if (!weaponList.ContainsKey(designerName)) return; + bool isKnife = false; + var weapon = new CBasePlayerWeapon(entity.Handle); + + if (designerName.Contains("knife") || designerName.Contains("bayonet")) + { + isKnife = true; + } + Server.NextFrame(() => + { + try + { + if (!weapon.IsValid) return; + if (weapon.OwnerEntity.Value == null) return; + if (!weapon.OwnerEntity.Value.EntityIndex.HasValue) + { + for (int i = 1; i <= Server.MaxPlayers; i++) + { + CCSPlayerController? ghostPlayer = Utilities.GetPlayerFromIndex(i); + if (!Utility.IsPlayerValid(ghostPlayer)) continue; + if (g_changedKnife.Contains((int)ghostPlayer.EntityIndex!.Value.Value)) + { + ChangeWeaponAttributes(weapon, ghostPlayer, isKnife); + g_changedKnife.Remove((int)ghostPlayer.EntityIndex!.Value.Value); + break; + } + } + return; + } + + if (!weapon.OwnerEntity.Value.EntityIndex.HasValue) return; + int weaponOwner = (int)weapon.OwnerEntity.Value.EntityIndex.Value.Value; + var pawn = new CBasePlayerPawn(NativeAPI.GetEntityFromIndex(weaponOwner)); + if (!pawn.IsValid) return; + var playerIndex = (int)pawn.Controller.Value.EntityIndex!.Value.Value; + var player = Utilities.GetPlayerFromIndex(playerIndex); + if (!Utility.IsPlayerValid(player)) return; + + // TODO: Remove knife crashes here, needs another solution + /*if (isKnife && g_playersKnife[(int)player.EntityIndex!.Value.Value] != "weapon_knife" && (weapon.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.AttributeManager.Item.ItemDefinitionIndex == 59)) + { + RemoveKnifeFromPlayer(player); + return; + }*/ + ChangeWeaponAttributes(weapon, player, isKnife); + } + catch (Exception) { } + }); + } + private HookResult OnEventItemPurchasePost(EventItemPurchase @event, GameEventInfo info) + { + CCSPlayerController? player = @event.Userid; + + if (player == null || !player.IsValid) return HookResult.Continue; + + if (Config.Additional.SkinVisibilityFix) + AddTimer(0.2f, () => RefreshSkins(player)); + + return HookResult.Continue; + } + } +} diff --git a/Utility.cs b/Utility.cs index 820acca6..a59a6047 100644 --- a/Utility.cs +++ b/Utility.cs @@ -1,20 +1,102 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Entities; using CounterStrikeSharp.API.Modules.Utils; +using Dapper; +using MySqlConnector; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; using System.Reflection; namespace WeaponPaints { - public static class Utility + internal static class Utility { - public static WeaponPaintsConfig? Config { get; set; } + internal static WeaponPaintsConfig? Config { get; set; } - public static bool IsPlayerValid(CCSPlayerController? player) + internal static bool IsPlayerValid(CCSPlayerController? player) { return (player != null && player.IsValid && !player.IsBot && !player.IsHLTV); } - public static string ReplaceTags(string message) + internal static string BuildDatabaseConnectionString() + { + if (Config == null) return String.Empty; + var builder = new MySqlConnectionStringBuilder + { + Server = Config.DatabaseHost, + UserID = Config.DatabaseUser, + Password = Config.DatabasePassword, + Database = Config.DatabaseName, + Port = (uint)Config.DatabasePort, + }; + + return builder.ConnectionString; + } + + internal static void TestDatabaseConnection() + { + try + { + using var connection = new MySqlConnection(BuildDatabaseConnectionString()); + connection.Open(); + + if (connection.State != System.Data.ConnectionState.Open) + { + throw new Exception("[WeaponPaints] Unable connect to database!"); + } + } + catch (Exception ex) + { + throw new Exception("[WeaponPaints] Unknown mysql exception! " + ex.Message); + } + CheckDatabaseTables(); + } + + internal static async void CheckDatabaseTables() + { + try + { + using var connection = new MySqlConnection(BuildDatabaseConnectionString()); + await connection.OpenAsync(); + + using var transaction = await connection.BeginTransactionAsync(); + + try + { + string createTable1 = "CREATE TABLE IF NOT EXISTS `wp_player_skins` (`steamid` varchar(64) NOT NULL, `weapon_defindex` int(6) NOT NULL, `weapon_paint_id` int(6) NOT NULL, `weapon_wear` float NOT NULL DEFAULT 0.0001, `weapon_seed` int(16) NOT NULL DEFAULT 0) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci"; + string createTable2 = "CREATE TABLE IF NOT EXISTS `wp_player_knife` (`steamid` varchar(64) NOT NULL, `knife` varchar(64) NOT NULL, UNIQUE (`steamid`)) ENGINE = InnoDB"; + + await connection.ExecuteAsync(createTable1, transaction: transaction); + await connection.ExecuteAsync(createTable2, transaction: transaction); + + await transaction.CommitAsync(); + } + catch (Exception) + { + await transaction.RollbackAsync(); + throw new Exception("[WeaponPaints] Unable to create tables!"); + } + } + catch (Exception ex) + { + throw new Exception("[WeaponPaints] Unknown mysql exception! " + ex.Message); + } + } + internal static void LoadSkinsFromFile(string filePath) + { + if (File.Exists(filePath)) + { + string json = File.ReadAllText(filePath); + var deserializedSkins = JsonConvert.DeserializeObject>(json); + WeaponPaints.skinsList = deserializedSkins ?? new List(); + } + else + { + throw new FileNotFoundException("File not found.", filePath); + } + } + + internal static string ReplaceTags(string message) { if (message.Contains('{')) { @@ -37,14 +119,14 @@ namespace WeaponPaints return message; } - public static void Log(string message) + internal static void Log(string message) { Console.BackgroundColor = ConsoleColor.DarkGray; Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("[WeaponPaints] " + message); Console.ResetColor(); } - public static void ShowAd(string moduleVersion) + internal static void ShowAd(string moduleVersion) { Console.WriteLine(" "); Console.WriteLine(" _ _ _______ _______ _______ _______ __ _ _______ _______ ___ __ _ _______ _______ "); diff --git a/WeaponAction.cs b/WeaponAction.cs new file mode 100644 index 00000000..9e8d52a6 --- /dev/null +++ b/WeaponAction.cs @@ -0,0 +1,222 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Memory; +using CounterStrikeSharp.API.Modules.Utils; + +namespace WeaponPaints +{ + public partial class WeaponPaints + { + internal static void ChangeWeaponAttributes(CBasePlayerWeapon? weapon, CCSPlayerController? player, bool isKnife = false) + { + if (weapon == null || !weapon.IsValid || !Utility.IsPlayerValid(player)) return; + + int playerIndex = (int)player!.EntityIndex!.Value.Value; + + if (!gPlayerWeaponsInfo.ContainsKey(playerIndex)) return; + + if (isKnife && !g_playersKnife.ContainsKey(playerIndex) || isKnife && g_playersKnife[playerIndex] == "weapon_knife") return; + + int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex; + + if (_config.Additional.GiveRandomSkin && + !gPlayerWeaponsInfo[playerIndex].ContainsKey(weaponDefIndex)) + { + // Random skins + weapon.AttributeManager.Item.ItemID = 16384; + weapon.AttributeManager.Item.ItemIDLow = 16384 & 0xFFFFFFFF; + weapon.AttributeManager.Item.ItemIDHigh = weapon.AttributeManager.Item.ItemIDLow >> 32; + weapon.FallbackPaintKit = GetRandomPaint(weaponDefIndex); + weapon.FallbackSeed = 0; + weapon.FallbackWear = 0.0f; + if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null) + { + var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode); + skeleton.ModelState.MeshGroupMask = 2; + } + return; + } + + if (!gPlayerWeaponsInfo[playerIndex].ContainsKey(weaponDefIndex)) return; + WeaponInfo weaponInfo = gPlayerWeaponsInfo[playerIndex][weaponDefIndex]; + //Log($"Apply on {weapon.DesignerName}({weapon.AttributeManager.Item.ItemDefinitionIndex}) paint {gPlayerWeaponPaints[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex]} seed {gPlayerWeaponSeed[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex]} wear {gPlayerWeaponWear[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex]}"); + weapon.AttributeManager.Item.ItemID = 16384; + weapon.AttributeManager.Item.ItemIDLow = 16384 & 0xFFFFFFFF; + weapon.AttributeManager.Item.ItemIDHigh = weapon.AttributeManager.Item.ItemIDLow >> 32; + weapon.FallbackPaintKit = weaponInfo.Paint; + weapon.FallbackSeed = weaponInfo.Seed; + weapon.FallbackWear = weaponInfo.Wear; + if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null) + { + var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode); + skeleton.ModelState.MeshGroupMask = 2; + } + } + internal static void GiveKnifeToPlayer(CCSPlayerController? player) + { + if (!_config.Additional.KnifeEnabled || player == null || !player.EntityIndex.HasValue || !player.IsValid) return; + if (g_playersKnife.TryGetValue((int)player.EntityIndex.Value.Value, out var knife)) + { + player.GiveNamedItem(knife); + } + else if (_config.Additional.GiveRandomKnife) + { + var knifeTypes = weaponList.Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")).ToDictionary(pair => pair.Key, pair => pair.Value); + + Random random = new(); + int index = random.Next(knifeTypes.Count); + var randomKnifeClass = knifeTypes.Keys.ElementAt(index); + + player.GiveNamedItem(randomKnifeClass); + } + else + { + var defaultKnife = (CsTeam)player.TeamNum == CsTeam.Terrorist ? "weapon_knife_t" : "weapon_knife"; + player.GiveNamedItem(defaultKnife); + } + } + internal void RemovePlayerKnife(CCSPlayerController? player) + { + if (player == null || !player.IsValid || !player.PawnIsAlive) return; + if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return; + + var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; + if (weapons != null && weapons.Count > 0) + { + CCSPlayer_ItemServices service = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle); + //var dropWeapon = VirtualFunction.CreateVoid(service.Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon")); + + foreach (var weapon in weapons) + { + if (weapon != null && weapon.IsValid && weapon.Value.IsValid) + { + //if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59) + if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) + { + if (!weapon.Value.EntityIndex.HasValue) return; + int weaponEntityIndex = (int)weapon.Value.EntityIndex!.Value.Value; + NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3"); + AddTimer(0.35f, () => service.DropActivePlayerWeapon(weapon.Value)); + + AddTimer(1.0f, () => + { + CEntityInstance? knife = Utilities.GetEntityFromIndex(weaponEntityIndex); + if (knife != null && knife.IsValid) + { + knife.Remove(); + } + }); + + break; + } + } + } + } + } + internal void RefreshPlayerKnife(CCSPlayerController? player) + { + if (player == null || !player.IsValid || !player.PawnIsAlive) return; + if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return; + + var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; + if (weapons != null && weapons.Count > 0) + { + CCSPlayer_ItemServices service = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle); + //var dropWeapon = VirtualFunction.CreateVoid(service.Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon")); + + foreach (var weapon in weapons) + { + if (weapon != null && weapon.IsValid && weapon.Value.IsValid) + { + //if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59) + if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) + { + if (!weapon.Value.EntityIndex.HasValue) return; + int weaponEntityIndex = (int)weapon.Value.EntityIndex!.Value.Value; + NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3"); + AddTimer(0.35f, () => service.DropActivePlayerWeapon(weapon.Value)); + + AddTimer(1.0f, () => + { + CEntityInstance? knife = Utilities.GetEntityFromIndex(weaponEntityIndex); + if (knife != null && knife.IsValid) + { + knife.Remove(); + + if (player.PawnIsAlive) + GiveKnifeToPlayer(player); + } + }); + + break; + } + } + } + } + } + + internal void RefreshSkins(CCSPlayerController? player) + { + if (!Utility.IsPlayerValid(player) || !player!.PawnIsAlive) return; + + AddTimer(0.18f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3")); + AddTimer(0.25f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot2")); + AddTimer(0.38f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot1")); + } + internal static bool PlayerHasKnife(CCSPlayerController? player) + { + if (!WeaponPaints._config.Additional.KnifeEnabled) return false; + + if (player == null || !player.IsValid) + { + return false; + } + + var weapons = player.PlayerPawn.Value.WeaponServices!.MyWeapons; + if (weapons == null || weapons.Count <= 0) return false; + foreach (var weapon in weapons) + { + if (weapon != null && weapon.IsValid && weapon.Value.IsValid) + { + if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) + { + return true; + } + } + } + return false; + } + + private static CSkeletonInstance GetSkeletonInstance(CGameSceneNode node) + { + Func GetSkeletonInstance = VirtualFunction.Create(node.Handle, 8); + return new CSkeletonInstance(GetSkeletonInstance(node.Handle)); + } + + private static int GetRandomPaint(int defindex) + { + Random rnd = new Random(); + + if (WeaponPaints.skinsList != null) + { + // Filter weapons by the provided defindex + var filteredWeapons = WeaponPaints.skinsList.FindAll(w => w["weapon_defindex"]?.ToString() == defindex.ToString()); + + if (filteredWeapons.Count > 0) + { + var randomWeapon = filteredWeapons[rnd.Next(filteredWeapons.Count)]; + if (int.TryParse(randomWeapon["paint"]?.ToString(), out int paintValue)) + { + return paintValue; + } + else + { + return 0; + } + + } + } + return 0; + } + } +} diff --git a/WeaponInfo.cs b/WeaponInfo.cs new file mode 100644 index 00000000..310c6e87 --- /dev/null +++ b/WeaponInfo.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace WeaponPaints +{ + public class WeaponInfo + { + public int Paint { get; set; } + public int Seed { get; set; } + public float Wear { get; set; } + } +} diff --git a/WeaponPaints.cs b/WeaponPaints.cs index e713f6f2..82605b14 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -1,44 +1,43 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core.Attributes; -using CounterStrikeSharp.API.Modules.Commands; -using CounterStrikeSharp.API.Modules.Entities; using CounterStrikeSharp.API.Modules.Memory; -using CounterStrikeSharp.API.Modules.Menu; using CounterStrikeSharp.API.Modules.Utils; -using MySqlConnector; -using Dapper; -using System.Runtime.ExceptionServices; -using System.Reflection; using CounterStrikeSharp.API.Modules.Cvars; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace WeaponPaints; [MinimumApiVersion(61)] -public class WeaponPaints : BasePlugin, IPluginConfig +public partial class WeaponPaints : BasePlugin, IPluginConfig { public override string ModuleName => "WeaponPaints"; public override string ModuleDescription => "Skin and knife selector, standalone and web-based"; public override string ModuleAuthor => "Nereziel & daffyy"; public override string ModuleVersion => "1.2a"; public WeaponPaintsConfig Config { get; set; } = new(); + internal static WeaponPaintsConfig _config = new WeaponPaintsConfig(); - private string DatabaseConnectionString = string.Empty; - private Uri GlobalShareApi = new Uri("https://weaponpaints.fun/api.php"); + internal static WeaponSynchronization? weaponSync; - public bool IsMatchZy = false; - public int GlobalShareServerId = 0; - - private DateTime[] commandCooldown = new DateTime[Server.MaxPlayers]; + /* private Dictionary> gPlayerWeaponPaints = new(); private Dictionary> gPlayerWeaponSeed = new(); private Dictionary> gPlayerWeaponWear = new(); - private Dictionary g_playersKnife = new(); - private List g_changedKnife = new(); + */ + private string DatabaseConnectionString = string.Empty; - private static List skinsList = new List(); - private static readonly Dictionary weaponList = new() + internal Uri GlobalShareApi = new Uri("https://weaponpaints.fun/api.php"); + internal int GlobalShareServerId = 0; + + private DateTime[] commandCooldown = new DateTime[Server.MaxPlayers]; + internal static Dictionary> gPlayerWeaponsInfo = new Dictionary>(); + internal static Dictionary g_knifePickupCount = new Dictionary(); + internal static Dictionary g_playersKnife = new(); + internal static List g_changedKnife = new(); + + internal static List skinsList = new List(); + internal static readonly Dictionary weaponList = new() { {"weapon_deagle", "Desert Eagle"}, {"weapon_elite", "Dual Berettas"}, @@ -100,18 +99,15 @@ public class WeaponPaints : BasePlugin, IPluginConfig { if (!Config.GlobalShare) { - BuildDatabaseConnectionString(); - TestDatabaseConnection(); + DatabaseConnectionString = Utility.BuildDatabaseConnectionString(); + Utility.TestDatabaseConnection(); } - RegisterListener(OnEntitySpawned); - RegisterEventHandler(OnEventItemPurchasePost); - RegisterListener(OnClientPutInServer); - RegisterListener(OnClientDisconnect); - RegisterListener(OnMapStart); - RegisterEventHandler(OnPlayerSpawn); - RegisterEventHandler(OnRoundStart, HookMode.Pre); - RegisterEventHandler(OnItemPickup, HookMode.Pre); - RegisterEventHandler(OnItemRemove); + + if (Config.GlobalShare) + GlobalShareConnect(); + + + weaponSync = new WeaponSynchronization(DatabaseConnectionString, Config, GlobalShareApi, GlobalShareServerId); if (hotReload) { @@ -120,11 +116,11 @@ public class WeaponPaints : BasePlugin, IPluginConfig { for (int i = 1; i <= Server.MaxPlayers; i++) { - if (Config.Additional.SkinEnabled) - await GetWeaponPaintsFromDatabase(i); + if (Config.Additional.SkinEnabled && weaponSync != null) + await weaponSync.GetWeaponPaintsFromDatabase(i); - if (Config.Additional.KnifeEnabled) - await GetKnifeFromDatabase(i); + if (Config.Additional.KnifeEnabled && weaponSync != null) + await weaponSync.GetKnifeFromDatabase(i); } }); } @@ -134,15 +130,10 @@ public class WeaponPaints : BasePlugin, IPluginConfig if (Config.Additional.SkinEnabled) SetupSkinsMenu(); + RegisterEvents(); RegisterCommands(); - LoadSkinsFromFile(ModuleDirectory + "/skins.json"); - } - - private HookResult OnItemRemove(EventItemRemove @event, GameEventInfo info) - { - Console.WriteLine(@event.Defindex); - return HookResult.Continue; + Utility.LoadSkinsFromFile(ModuleDirectory + "/skins.json"); } public void OnConfigParsed(WeaponPaintsConfig config) @@ -156,72 +147,16 @@ public class WeaponPaints : BasePlugin, IPluginConfig } Config = config; + _config = config; Utility.Config = config; Utility.ShowAd(ModuleVersion); } - private void BuildDatabaseConnectionString() - { - var builder = new MySqlConnectionStringBuilder - { - Server = Config.DatabaseHost, - UserID = Config.DatabaseUser, - Password = Config.DatabasePassword, - Database = Config.DatabaseName, - Port = (uint)Config.DatabasePort, - }; - DatabaseConnectionString = builder.ConnectionString; + public static WeaponPaintsConfig GetWeaponPaintsConfig() + { + return _config; } - private void TestDatabaseConnection() - { - try - { - using var connection = new MySqlConnection(DatabaseConnectionString); - connection.Open(); - - if (connection.State != System.Data.ConnectionState.Open) - { - throw new Exception("[WeaponPaints] Unable connect to database!"); - } - } - catch (Exception ex) - { - throw new Exception("[WeaponPaints] Unknown mysql exception! " + ex.Message); - } - CheckDatabaseTables(); - } - - async private void CheckDatabaseTables() - { - try - { - using var connection = new MySqlConnection(DatabaseConnectionString); - await connection.OpenAsync(); - - using var transaction = await connection.BeginTransactionAsync(); - - try - { - string createTable1 = "CREATE TABLE IF NOT EXISTS `wp_player_skins` (`steamid` varchar(64) NOT NULL, `weapon_defindex` int(6) NOT NULL, `weapon_paint_id` int(6) NOT NULL, `weapon_wear` float NOT NULL DEFAULT 0.0001, `weapon_seed` int(16) NOT NULL DEFAULT 0) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci"; - string createTable2 = "CREATE TABLE IF NOT EXISTS `wp_player_knife` (`steamid` varchar(64) NOT NULL, `knife` varchar(64) NOT NULL, UNIQUE (`steamid`)) ENGINE = InnoDB"; - - await connection.ExecuteAsync(createTable1, transaction: transaction); - await connection.ExecuteAsync(createTable2, transaction: transaction); - - await transaction.CommitAsync(); - } - catch (Exception) - { - await transaction.RollbackAsync(); - throw new Exception("[WeaponPaints] Unable to create tables!"); - } - } - catch (Exception ex) - { - throw new Exception("[WeaponPaints] Unknown mysql exception! " + ex.Message); - } - } // TODO: fix for map which change mp_t_default_melee /*private HookResult OnRoundPreStart(EventRoundPrestart @event, GameEventInfo info) { @@ -234,52 +169,6 @@ public class WeaponPaints : BasePlugin, IPluginConfig { base.Unload(hotReload); } - private void RegisterCommands() - { - AddCommand($"css_{Config.Additional.CommandSkin}", "Skins info", (player, info) => - { - if (!Utility.IsPlayerValid(player)) return; - OnCommandWS(player, info); - }); - AddCommand($"css_{Config.Additional.CommandRefresh}", "Skins refresh", (player, info) => - { - if (!Utility.IsPlayerValid(player)) return; - OnCommandRefresh(player, info); - }); - if (Config.Additional.CommandKillEnabled) - { - AddCommand($"css_{Config.Additional.CommandKill}", "kill yourself", (player, info) => - { - if (!Utility.IsPlayerValid(player) || !player!.PlayerPawn.IsValid) return; - - player.PlayerPawn.Value.CommitSuicide(true, false); - }); - } - } - private void IncompatibilityCheck() - { - // MatchZy - if (Directory.Exists(Path.GetDirectoryName(ModuleDirectory) + "/MatchZy")) - { - Console.WriteLine("[WeaponPaints] Incompatibility found: MatchZy"); - IsMatchZy = true; - } - } - - private void OnMapStart(string mapName) - { - if (!Config.Additional.KnifeEnabled) return; - // TODO - // needed for now - AddTimer(2.0f, () => - { - NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); - NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); - IncompatibilityCheck(); - }); - if (Config.GlobalShare) - GlobalShareConnect(); - } private void GlobalShareConnect() { @@ -314,832 +203,4 @@ public class WeaponPaints : BasePlugin, IPluginConfig } Console.WriteLine("[WeaponPaints] GlobalShare ONLINE"); } - - private void OnClientPutInServer(int playerSlot) - { - int playerIndex = playerSlot + 1; - Task.Run(async () => - { - if (Config.Additional.KnifeEnabled) - await GetKnifeFromDatabase(playerIndex); - if (Config.Additional.SkinEnabled) - await GetWeaponPaintsFromDatabase(playerIndex); - }); - } - private void OnClientDisconnect(int playerSlot) - { - CCSPlayerController player = Utilities.GetPlayerFromSlot(playerSlot); - if (!Utility.IsPlayerValid(player)) return; - if (Config.Additional.KnifeEnabled) - g_playersKnife.Remove((int)player.EntityIndex!.Value.Value); - if (Config.Additional.SkinEnabled) - gPlayerWeaponPaints.Remove((int)player.EntityIndex!.Value.Value); - } - - private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) - { - CCSPlayerController? player = @event.Userid; - if (player == null || !player.IsValid || !player.PlayerPawn.IsValid) - { - return HookResult.Continue; - } - - if (Config.Additional.KnifeEnabled) - { - if (!PlayerHasKnife(player)) - GiveKnifeToPlayer(player); - } - - if (Config.Additional.SkinVisibilityFix) - { - AddTimer(0.3f, () => RefreshSkins(player)); - } - - return HookResult.Continue; - } - private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) - { - /* - if (!IsMatchZy) return HookResult.Continue; - */ - - NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); - NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); - - return HookResult.Continue; - } - private HookResult OnItemPickup(EventItemPickup @event, GameEventInfo info) - { - if (@event.Defindex == 42 || @event.Defindex == 59) - { - CCSPlayerController? player = @event.Userid; - if (!Utility.IsPlayerValid(player) || !player.PawnIsAlive) return HookResult.Continue; - - if (g_playersKnife.ContainsKey((int)player.EntityIndex!.Value.Value) - && - g_playersKnife[(int)player.EntityIndex!.Value.Value] != "weapon_knife") - { - if (PlayerHasKnife(player)) - RemoveKnifeFromPlayer(player); - - AddTimer(0.3f, () => - { - if (!PlayerHasKnife(player)) - GiveKnifeToPlayer(player); - }); - - if (Config.Additional.SkinVisibilityFix) - { - AddTimer(0.25f, () => RefreshSkins(player)); - } - } - } - return HookResult.Continue; - } - - private void OnEntitySpawned(CEntityInstance entity) - { - if (!Config.Additional.SkinEnabled) return; - var designerName = entity.DesignerName; - if (!weaponList.ContainsKey(designerName)) return; - bool isKnife = false; - var weapon = new CBasePlayerWeapon(entity.Handle); - - if (designerName.Contains("knife") || designerName.Contains("bayonet")) - { - isKnife = true; - } - Server.NextFrame(() => - { - try - { - if (!weapon.IsValid) return; - if (weapon.OwnerEntity.Value == null) return; - if (!weapon.OwnerEntity.Value.EntityIndex.HasValue) - { - for (int i = 1; i <= Server.MaxPlayers; i++) - { - CCSPlayerController? ghostPlayer = Utilities.GetPlayerFromIndex(i); - if (!Utility.IsPlayerValid(ghostPlayer)) continue; - if (g_changedKnife.Contains((int)ghostPlayer.EntityIndex!.Value.Value)) - { - ChangeWeaponAttributes(weapon, ghostPlayer, isKnife); - g_changedKnife.Remove((int)ghostPlayer.EntityIndex!.Value.Value); - break; - } - } - return; - } - - if (!weapon.OwnerEntity.Value.EntityIndex.HasValue) return; - int weaponOwner = (int)weapon.OwnerEntity.Value.EntityIndex.Value.Value; - var pawn = new CBasePlayerPawn(NativeAPI.GetEntityFromIndex(weaponOwner)); - if (!pawn.IsValid) return; - var playerIndex = (int)pawn.Controller.Value.EntityIndex!.Value.Value; - var player = Utilities.GetPlayerFromIndex(playerIndex); - if (!Utility.IsPlayerValid(player)) return; - - // TODO: Remove knife crashes here, needs another solution - /*if (isKnife && g_playersKnife[(int)player.EntityIndex!.Value.Value] != "weapon_knife" && (weapon.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.AttributeManager.Item.ItemDefinitionIndex == 59)) - { - RemoveKnifeFromPlayer(player); - return; - }*/ - ChangeWeaponAttributes(weapon, player, isKnife); - } - catch (Exception) { } - }); - } - private void ChangeWeaponAttributes(CBasePlayerWeapon? weapon, CCSPlayerController? player, bool isKnife = false) - { - if (weapon == null || !weapon.IsValid || !Utility.IsPlayerValid(player)) return; - - int playerIndex = (int)player!.EntityIndex!.Value.Value; - if (!gPlayerWeaponPaints.ContainsKey(playerIndex)) return; - - if (isKnife && !g_playersKnife.ContainsKey(playerIndex) || isKnife && g_playersKnife[playerIndex] == "weapon_knife") return; - - if (Config.Additional.GiveRandomSkin && - !gPlayerWeaponPaints[playerIndex].ContainsKey(weapon.AttributeManager.Item.ItemDefinitionIndex)) - { - // Random skins - weapon.AttributeManager.Item.ItemID = 16384; - weapon.AttributeManager.Item.ItemIDLow = 16384 & 0xFFFFFFFF; - weapon.AttributeManager.Item.ItemIDHigh = weapon.AttributeManager.Item.ItemIDLow >> 32; - weapon.FallbackPaintKit = GetRandomPaint(weapon.AttributeManager.Item.ItemDefinitionIndex); - weapon.FallbackSeed = 0; - weapon.FallbackWear = 0.0f; - if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null) - { - var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode); - skeleton.ModelState.MeshGroupMask = 2; - } - return; - } - - if (!gPlayerWeaponPaints[playerIndex].ContainsKey(weapon.AttributeManager.Item.ItemDefinitionIndex)) return; - //Log($"Apply on {weapon.DesignerName}({weapon.AttributeManager.Item.ItemDefinitionIndex}) paint {gPlayerWeaponPaints[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex]} seed {gPlayerWeaponSeed[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex]} wear {gPlayerWeaponWear[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex]}"); - weapon.AttributeManager.Item.ItemID = 16384; - weapon.AttributeManager.Item.ItemIDLow = 16384 & 0xFFFFFFFF; - weapon.AttributeManager.Item.ItemIDHigh = weapon.AttributeManager.Item.ItemIDLow >> 32; - weapon.FallbackPaintKit = gPlayerWeaponPaints[playerIndex][weapon.AttributeManager.Item.ItemDefinitionIndex]; - weapon.FallbackSeed = gPlayerWeaponSeed[playerIndex][weapon.AttributeManager.Item.ItemDefinitionIndex]; - weapon.FallbackWear = gPlayerWeaponWear[playerIndex][weapon.AttributeManager.Item.ItemDefinitionIndex]; - if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null) - { - var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode); - skeleton.ModelState.MeshGroupMask = 2; - } - } - - private HookResult OnEventItemPurchasePost(EventItemPurchase @event, GameEventInfo info) - { - CCSPlayerController? player = @event.Userid; - - if (player == null || !player.IsValid) return HookResult.Continue; - - if (Config.Additional.SkinVisibilityFix) - AddTimer(0.2f, () => RefreshSkins(player)); - - return HookResult.Continue; - } - private void GiveKnifeToPlayer(CCSPlayerController? player) - { - if (!Config.Additional.KnifeEnabled || player == null || !player.IsValid) return; - - if (g_playersKnife.TryGetValue((int)player.EntityIndex!.Value.Value, out var knife)) - { - player.GiveNamedItem(knife); - } - else if (Config.Additional.GiveRandomKnife) - { - var knifeTypes = weaponList.Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")).ToDictionary(pair => pair.Key, pair => pair.Value); - - Random random = new(); - int index = random.Next(knifeTypes.Count); - var randomKnifeClass = knifeTypes.Keys.ElementAt(index); - - player.GiveNamedItem(randomKnifeClass); - } - else - { - var defaultKnife = (CsTeam)player.TeamNum == CsTeam.Terrorist ? "weapon_knife_t" : "weapon_knife"; - player.GiveNamedItem(defaultKnife); - } - } - private void RemoveKnifeFromPlayer(CCSPlayerController? player) - { - if (player == null || !player.IsValid || !player.PawnIsAlive) return; - if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return; - - var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; - if (weapons != null && weapons.Count > 0) - { - CCSPlayer_ItemServices service = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle); - //var dropWeapon = VirtualFunction.CreateVoid(service.Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon")); - - foreach (var weapon in weapons) - { - if (weapon != null && weapon.IsValid && weapon.Value.IsValid) - { - //if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59) - if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) - { - NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3"); - AddTimer(0.5f, () => service.DropActivePlayerWeapon(weapon.Value)); - - /* - CEntityInstance knife = new(weapon.Value.Handle); - AddTimer(1.0f, () => { - knife.Remove(); - if (knife != null && knife.IsValid && player.PawnIsAlive) - }); - */ - - break; - } - } - } - } - } - /* Causing crashes - private void RefreshPlayerKnife(CCSPlayerController? player, bool remove = false) - { - if (player == null || !player.IsValid || player.IsBot || !player.PawnIsAlive) return; - - AddTimer(0.1f, () => - { - if (remove == true) - { - if (PlayerHasKnife(player)) - RemoveKnifeFromPlayer(player); - } - - GiveKnifeToPlayer(player); - }); - - if (Config.Additional.SkinVisibilityFix) - { - AddTimer(0.25f, () => RefreshSkins(player)); - } - } - */ - private void RefreshSkins(CCSPlayerController? player) - { - if (!Utility.IsPlayerValid(player) || !player!.PawnIsAlive) return; - - AddTimer(0.18f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3")); - AddTimer(0.25f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot2")); - AddTimer(0.38f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot1")); - } - private bool PlayerHasKnife(CCSPlayerController? player) - { - if (!Config.Additional.KnifeEnabled) return false; - - if (player == null || !player.IsValid || !player.PawnIsAlive) - { - return false; - } - - var weapons = player.PlayerPawn.Value.WeaponServices!.MyWeapons; - if (weapons == null || weapons.Count <= 0) return false; - foreach (var weapon in weapons) - { - if (weapon != null && weapon.IsValid && weapon.Value.IsValid) - { - if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) - { - return true; - } - } - } - return false; - } - private void SetupKnifeMenu() - { - if (!Config.Additional.KnifeEnabled) return; - - var knivesOnly = weaponList - .Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")) - .ToDictionary(pair => pair.Key, pair => pair.Value); - - var giveItemMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.KnifeMenuTitle}")); - var handleGive = (CCSPlayerController? player, ChatMenuOption option) => - { - if (Utility.IsPlayerValid(player)) - { - var knifeName = option.Text; - var knifeKey = knivesOnly.FirstOrDefault(x => x.Value == knifeName).Key; - if (!string.IsNullOrEmpty(knifeKey)) - { - string temp = ""; - - if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenu)) - { - temp = $" {Config.Prefix} {Config.Messages.ChosenKnifeMenu}".Replace("{KNIFE}", knifeName); - player!.PrintToChat(Utility.ReplaceTags(temp)); - } - - if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenuKill) && Config.Additional.CommandKillEnabled) - { - temp = $" {Config.Prefix} {Config.Messages.ChosenKnifeMenuKill}"; - player!.PrintToChat(Utility.ReplaceTags(temp)); - } - - g_playersKnife[(int)player!.EntityIndex!.Value.Value] = knifeKey; - - if (player!.PawnIsAlive) - { - if (PlayerHasKnife(player)) - { - RemoveKnifeFromPlayer(player); - } - - g_changedKnife.Add((int)player.EntityIndex!.Value.Value); - GiveKnifeToPlayer(player); - } - - Task.Run(() => SyncKnifeToDatabase((int)player.EntityIndex!.Value.Value, knifeKey)); - - } - } - }; - foreach (var knifePair in knivesOnly) - { - giveItemMenu.AddMenuOption(knifePair.Value, handleGive); - } - AddCommand($"css_{Config.Additional.CommandKnife}", "Knife Menu", (player, info) => - { - if (!Utility.IsPlayerValid(player)) return; - int playerIndex = (int)player!.EntityIndex!.Value.Value; - - if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds) && playerIndex > 0 && playerIndex < commandCooldown.Length) - { - commandCooldown[playerIndex] = DateTime.UtcNow; - ChatMenus.OpenMenu(player, giveItemMenu); - return; - } - if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) - { - string temp = $" {Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; - player.PrintToChat(Utility.ReplaceTags(temp)); - } - }); - } - - private void SetupSkinsMenu() - { - var classNamesByWeapon = weaponList.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); - var weaponSelectionMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.WeaponMenuTitle}")); - - // Function to handle skin selection for a specific weapon - var handleWeaponSelection = (CCSPlayerController? player, ChatMenuOption option) => - { - if (!Utility.IsPlayerValid(player)) return; - - int playerIndex = (int)player!.EntityIndex!.Value.Value; - string selectedWeapon = option.Text; - if (classNamesByWeapon.TryGetValue(selectedWeapon, out string? selectedWeaponClassname)) - { - if (selectedWeaponClassname == null) return; - var skinsForSelectedWeapon = skinsList?.Where(skin => - skin != null && - skin.TryGetValue("weapon_name", out var weaponName) && - weaponName?.ToString() == selectedWeaponClassname - )?.ToList(); - - var skinSubMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.SkinMenuTitle}").Replace("{WEAPON}", selectedWeapon)); - - // Function to handle skin selection for the chosen weapon - var handleSkinSelection = (CCSPlayerController? p, ChatMenuOption opt) => - { - if (p == null || !p.IsValid) return; - - var steamId = new SteamID(player.SteamID); - var firstSkin = skinsList?.FirstOrDefault(skin => - { - if (skin != null && skin.TryGetValue("weapon_name", out var weaponName)) - { - return weaponName?.ToString() == selectedWeaponClassname; - } - return false; - }); - string selectedSkin = opt.Text; - string selectedPaintID = selectedSkin.Split('(')[1].Trim(')').Trim(); - - if (firstSkin != null && - firstSkin.TryGetValue("weapon_defindex", out var weaponDefIndexObj) && - weaponDefIndexObj != null && - int.TryParse(weaponDefIndexObj.ToString(), out var weaponDefIndex) && - int.TryParse(selectedPaintID, out var paintID)) - { - string temp = $" {Config.Prefix} {Config.Messages.ChosenSkinMenu}".Replace("{SKIN}", selectedSkin); - p.PrintToChat(Utility.ReplaceTags(temp)); - gPlayerWeaponPaints[playerIndex][weaponDefIndex] = paintID; - gPlayerWeaponWear[playerIndex][weaponDefIndex] = 0.0f; - gPlayerWeaponSeed[playerIndex][weaponDefIndex] = 0; - - Task.Run(async () => - { - await SyncWeaponPaintsToDatabase(player); - }); - } - }; - - // Add skin options to the submenu for the selected weapon - if (skinsForSelectedWeapon != null) - { - foreach (var skin in skinsForSelectedWeapon.Where(s => s != null)) - { - if (skin.TryGetValue("paint_name", out var paintNameObj) && skin.TryGetValue("paint", out var paintObj)) - { - var paintName = paintNameObj?.ToString(); - var paint = paintObj?.ToString(); - - if (!string.IsNullOrEmpty(paintName) && !string.IsNullOrEmpty(paint)) - { - skinSubMenu.AddMenuOption($"{paintName} ({paint})", handleSkinSelection); - } - } - } - } - - // Open the submenu for skin selection of the chosen weapon - ChatMenus.OpenMenu(player, skinSubMenu); - } - }; - - // Add weapon options to the weapon selection menu - foreach (var weaponClass in weaponList.Keys) - { - string weaponName = weaponList[weaponClass]; - weaponSelectionMenu.AddMenuOption(weaponName, handleWeaponSelection); - } - // Command to open the weapon selection menu for players - AddCommand($"css_{Config.Additional.CommandSkinSelection}", "Skins selection menu", (player, info) => - { - if (!Utility.IsPlayerValid(player)) return; - int playerIndex = (int)player!.EntityIndex!.Value.Value; - - if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds) && playerIndex > 0 && playerIndex < commandCooldown.Length) - { - commandCooldown[playerIndex] = DateTime.UtcNow; - ChatMenus.OpenMenu(player, weaponSelectionMenu); - return; - } - if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) - { - string temp = $"{Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; - player.PrintToChat(Utility.ReplaceTags(temp)); - } - - }); - } - - // [ConsoleCommand($"css_{Config.Additional.CommandRefresh}", "refreshskins")] - private void OnCommandRefresh(CCSPlayerController? player, CommandInfo command) - { - if (!Config.Additional.CommandWpEnabled || !Config.Additional.SkinEnabled) return; - if (!Utility.IsPlayerValid(player)) return; - - string temp = ""; - int playerIndex = (int)player!.EntityIndex!.Value.Value; - if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds)) - { - commandCooldown[playerIndex] = DateTime.UtcNow; - Task.Run(async () => await GetWeaponPaintsFromDatabase(playerIndex)); - if (Config.Additional.KnifeEnabled) - { - if (PlayerHasKnife(player)) - RemoveKnifeFromPlayer(player); - AddTimer(0.3f, () => - { - GiveKnifeToPlayer(player); - }); - - Task.Run(async () => await GetKnifeFromDatabase(playerIndex)); - /* - RemoveKnifeFromPlayer(player); - AddTimer(0.2f, () => GiveKnifeToPlayer(player)); - */ - } - if (!string.IsNullOrEmpty(Config.Messages.SuccessRefreshCommand)) - { - temp = $" {Config.Prefix} {Config.Messages.SuccessRefreshCommand}"; - player.PrintToChat(Utility.ReplaceTags(temp)); - } - return; - } - if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) - { - temp = $" {Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; - player.PrintToChat(Utility.ReplaceTags(temp)); - } - } - // [ConsoleCommand($"css_{Config.Additional.CommandSkin}", "weaponskins")] - private void OnCommandWS(CCSPlayerController? player, CommandInfo command) - { - if (!Config.Additional.SkinEnabled) return; - if (!Utility.IsPlayerValid(player)) return; - - string temp = ""; - - if (!string.IsNullOrEmpty(Config.Messages.WebsiteMessageCommand)) - { - temp = $" {Config.Prefix} {Config.Messages.WebsiteMessageCommand}"; - player!.PrintToChat(Utility.ReplaceTags(temp)); - } - if (!string.IsNullOrEmpty(Config.Messages.SynchronizeMessageCommand)) - { - temp = $" {Config.Prefix} {Config.Messages.SynchronizeMessageCommand}"; - player!.PrintToChat(Utility.ReplaceTags(temp)); - } - if (!Config.Additional.KnifeEnabled) return; - if (!string.IsNullOrEmpty(Config.Messages.KnifeMessageCommand)) - { - temp = $" {Config.Prefix} {Config.Messages.KnifeMessageCommand}"; - player!.PrintToChat(Utility.ReplaceTags(temp)); - } - } - private static CSkeletonInstance GetSkeletonInstance(CGameSceneNode node) - { - Func GetSkeletonInstance = VirtualFunction.Create(node.Handle, 8); - return new CSkeletonInstance(GetSkeletonInstance(node.Handle)); - } - private async Task GetWeaponPaintsFromDatabase(int playerIndex) - { - if (!Config.Additional.SkinEnabled) return; - - CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); - if (!Utility.IsPlayerValid(player)) return; - - var steamId = new SteamID(player.SteamID); - - gPlayerWeaponPaints[playerIndex] = new Dictionary(); - gPlayerWeaponWear[playerIndex] = new Dictionary(); - gPlayerWeaponSeed[playerIndex] = new Dictionary(); - - try - { - if (Config.GlobalShare) - { - var values = new Dictionary - { - { "server_id", GlobalShareServerId.ToString() }, - { "steamid", steamId.SteamId64.ToString() }, - { "skins", "1" } - }; - UriBuilder builder = new UriBuilder(GlobalShareApi); - builder.Query = string.Join("&", values.Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}")); - - using (var httpClient = new HttpClient()) - { - httpClient.BaseAddress = GlobalShareApi; - var formContent = new FormUrlEncodedContent(values); - HttpResponseMessage response = await httpClient.GetAsync(builder.Uri); - - if (response.IsSuccessStatusCode) - { - string responseBody = await response.Content.ReadAsStringAsync(); - JArray jsonArray = JArray.Parse(responseBody); - if (jsonArray != null && jsonArray.Count > 0) - { - foreach (var weapon in jsonArray) - { - int? weaponDefIndex = weapon["weapon_defindex"]?.Value(); - int? weaponPaintId = weapon["weapon_paint_id"]?.Value(); - float? weaponWear = weapon["weapon_wear"]?.Value(); - int? weaponSeed = weapon["weapon_seed"]?.Value(); - - if (weaponDefIndex != null && weaponPaintId != null && weaponWear != null && weaponSeed != null) - { - gPlayerWeaponPaints[playerIndex][weaponDefIndex.Value] = weaponPaintId.Value; - gPlayerWeaponWear[playerIndex][weaponDefIndex.Value] = weaponWear.Value; - gPlayerWeaponSeed[playerIndex][weaponDefIndex.Value] = weaponSeed.Value; - } - } - } - return; - } - else - { - return; - } - } - } - - using (var connection = new MySqlConnection(DatabaseConnectionString)) - { - await connection.OpenAsync(); - - string query = "SELECT * FROM `wp_player_skins` WHERE `steamid` = @steamid"; - - IEnumerable PlayerSkins = await connection.QueryAsync(query, new { steamid = steamId.SteamId64.ToString() }); - - if (PlayerSkins != null && PlayerSkins.AsList().Count > 0) - { - PlayerSkins.ToList().ForEach(row => - { - int weaponDefIndex = row.weapon_defindex ?? default(int); - int weaponPaintId = row.weapon_paint_id ?? default(int); - float weaponWear = row.weapon_wear ?? default(float); - int weaponSeed = row.weapon_seed ?? default(int); - - gPlayerWeaponPaints[playerIndex][weaponDefIndex] = weaponPaintId; - gPlayerWeaponWear[playerIndex][weaponDefIndex] = weaponWear; - gPlayerWeaponSeed[playerIndex][weaponDefIndex] = weaponSeed; - }); - } - else - { - return; - } - await connection.CloseAsync(); - } - } - catch (Exception e) - { - Utility.Log(e.Message); - return; - } - } - private async Task GetKnifeFromDatabase(int playerIndex) - { - if (!Config.Additional.KnifeEnabled) return; - try - { - CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); - if (!Utility.IsPlayerValid(player)) return; - var steamId = new SteamID(player.SteamID); - - if (Config.GlobalShare) - { - var values = new Dictionary - { - { "server_id", GlobalShareServerId.ToString() }, - { "steamid", steamId.SteamId64.ToString() }, - { "knife", "1" } - }; - UriBuilder builder = new UriBuilder(GlobalShareApi); - builder.Query = string.Join("&", values.Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}")); - - using (var httpClient = new HttpClient()) - { - httpClient.BaseAddress = GlobalShareApi; - var formContent = new FormUrlEncodedContent(values); - HttpResponseMessage response = await httpClient.GetAsync(builder.Uri); - - if (response.IsSuccessStatusCode) - { - string result = await response.Content.ReadAsStringAsync(); - if (!string.IsNullOrEmpty(result)) - { - g_playersKnife[playerIndex] = result; - } - else - { - return; - } - - } - else - { - return; - } - } - return; - } - - using (var connection = new MySqlConnection(DatabaseConnectionString)) - { - await connection.OpenAsync(); - string query = "SELECT `knife` FROM `wp_player_knife` WHERE `steamid` = @steamid"; - string? PlayerKnife = await connection.QueryFirstOrDefaultAsync(query, new { steamid = steamId.SteamId64.ToString() }); - - if (PlayerKnife != null) - { - g_playersKnife[playerIndex] = PlayerKnife; - } - else - { - return; - } - await connection.CloseAsync(); - } - //Log($"{player.PlayerName} has this knife -> {g_playersKnife[playerIndex]}"); - } - catch (Exception e) - { - Utility.Log(e.Message); - return; - } - } - private async Task SyncKnifeToDatabase(int playerIndex, string knife) - { - if (!Config.Additional.KnifeEnabled) return; - try - { - CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); - if (player == null || !player.IsValid) return; - var steamId = new SteamID(player.SteamID); - - using var connection = new MySqlConnection(DatabaseConnectionString); - await connection.OpenAsync(); - string query = "INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(@steamid, @newKnife) ON DUPLICATE KEY UPDATE `knife` = @newKnife"; - await connection.ExecuteAsync(query, new { steamid = steamId.SteamId64.ToString(), newKnife = knife }); - await connection.CloseAsync(); - } - catch (Exception e) - { - Utility.Log(e.Message); - return; - } - } - - private async Task SyncWeaponPaintsToDatabase(CCSPlayerController? player) - { - if (!Utility.IsPlayerValid(player)) return; - - int playerIndex = (int)player!.EntityIndex!.Value.Value; - - string steamId = new SteamID(player.SteamID).SteamId64.ToString(); - - using var connection = new MySqlConnection(DatabaseConnectionString); - await connection.OpenAsync(); - - if (!gPlayerWeaponPaints.ContainsKey(playerIndex)) - return; - - foreach (var weaponDefIndex in gPlayerWeaponPaints[playerIndex].Keys) - { - int paintId = gPlayerWeaponPaints[playerIndex][weaponDefIndex]; - float wear = gPlayerWeaponWear.TryGetValue(playerIndex, out var wearDictionary) - && wearDictionary.TryGetValue(weaponDefIndex, out var retrievedWear) - ? retrievedWear - : 0.0f; - - // Assigning values for gPlayerWeaponSeed - int seed = gPlayerWeaponSeed.TryGetValue(playerIndex, out var seedDictionary) - && seedDictionary.TryGetValue(weaponDefIndex, out var retrievedSeed) - ? retrievedSeed - : 0; - - string updateSql = "UPDATE `wp_player_skins` SET `weapon_paint_id` = @paintId, " + - "`weapon_wear` = @wear, `weapon_seed` = @seed WHERE `steamid` = @steamid " + - "AND `weapon_defindex` = @weaponDefIndex"; - - var updateParams = new { paintId, wear, seed, steamid = steamId, weaponDefIndex }; - int rowsAffected = await connection.ExecuteAsync(updateSql, updateParams); - - if (rowsAffected == 0) - { - string insertSql = "INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, " + - "`weapon_paint_id`, `weapon_wear`, `weapon_seed`) " + - "VALUES (@steamid, @weaponDefIndex, @paintId, @wear, @seed)"; - - await connection.ExecuteAsync(insertSql, updateParams); - } - } - await connection.CloseAsync(); - } - - private static int GetRandomPaint(int defindex) - { - Random rnd = new Random(); - - if (skinsList != null) - { - // Filter weapons by the provided defindex - var filteredWeapons = skinsList.FindAll(w => w["weapon_defindex"]?.ToString() == defindex.ToString()); - - if (filteredWeapons.Count > 0) - { - var randomWeapon = filteredWeapons[rnd.Next(filteredWeapons.Count)]; - if (int.TryParse(randomWeapon["paint"]?.ToString(), out int paintValue)) - { - return paintValue; - } - else - { - return 0; - } - - } - } - return 0; - } - - private static void LoadSkinsFromFile(string filePath) - { - if (File.Exists(filePath)) - { - string json = File.ReadAllText(filePath); - var deserializedSkins = JsonConvert.DeserializeObject>(json); - skinsList = deserializedSkins ?? new List(); - } - else - { - throw new FileNotFoundException("File not found.", filePath); - } - } -} +} \ No newline at end of file diff --git a/WeaponSynchronization.cs b/WeaponSynchronization.cs new file mode 100644 index 00000000..560d425f --- /dev/null +++ b/WeaponSynchronization.cs @@ -0,0 +1,272 @@ +using CounterStrikeSharp.API; +using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Entities; +using Dapper; +using MySqlConnector; +using Newtonsoft.Json.Linq; + +namespace WeaponPaints +{ + internal class WeaponSynchronization + { + private readonly string _databaseConnectionString; + private readonly WeaponPaintsConfig _config; + + private readonly Uri _globalShareApi; + private readonly int _globalShareServerId; + + + internal WeaponSynchronization(string databaseConnectionString, WeaponPaintsConfig config, Uri globalShareApi, int globalShareServerId) + { + _databaseConnectionString = databaseConnectionString; + _config = config; + _globalShareApi = globalShareApi; + _globalShareServerId = globalShareServerId; + } + + internal async Task GetKnifeFromDatabase(int playerIndex) + { + if (!_config.Additional.KnifeEnabled) return; + try + { + CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); + if (!Utility.IsPlayerValid(player)) return; + var steamId = new SteamID(player.SteamID); + + if (_config.GlobalShare) + { + var values = new Dictionary + { + { "server_id", _globalShareServerId.ToString() }, + { "steamid", steamId.SteamId64.ToString() }, + { "knife", "1" } + }; + UriBuilder builder = new UriBuilder(_globalShareApi); + builder.Query = string.Join("&", values.Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}")); + + using (var httpClient = new HttpClient()) + { + httpClient.BaseAddress = _globalShareApi; + var formContent = new FormUrlEncodedContent(values); + HttpResponseMessage response = await httpClient.GetAsync(builder.Uri); + + if (response.IsSuccessStatusCode) + { + string result = await response.Content.ReadAsStringAsync(); + if (!string.IsNullOrEmpty(result)) + { + WeaponPaints.g_playersKnife[playerIndex] = result; + } + else + { + return; + } + + } + else + { + return; + } + } + return; + } + + using (var connection = new MySqlConnection(_databaseConnectionString)) + { + await connection.OpenAsync(); + string query = "SELECT `knife` FROM `wp_player_knife` WHERE `steamid` = @steamid"; + string? PlayerKnife = await connection.QueryFirstOrDefaultAsync(query, new { steamid = steamId.SteamId64.ToString() }); + if (PlayerKnife != null) + { + WeaponPaints.g_playersKnife[playerIndex] = PlayerKnife; + } + else + { + return; + } + await connection.CloseAsync(); + } + //Log($"{player.PlayerName} has this knife -> {g_playersKnife[playerIndex]}"); + } + catch (Exception e) + { + Utility.Log(e.Message); + return; + } + } + + internal async Task SyncKnifeToDatabase(int playerIndex, string knife) + { + if (!_config.Additional.KnifeEnabled) return; + try + { + CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); + if (player == null || !player.IsValid) return; + var steamId = new SteamID(player.SteamID); + + using var connection = new MySqlConnection(_databaseConnectionString); + await connection.OpenAsync(); + string query = "INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(@steamid, @newKnife) ON DUPLICATE KEY UPDATE `knife` = @newKnife"; + await connection.ExecuteAsync(query, new { steamid = steamId.SteamId64.ToString(), newKnife = knife }); + await connection.CloseAsync(); + } + catch (Exception e) + { + Utility.Log(e.Message); + return; + } + } + + internal async Task GetWeaponPaintsFromDatabase(int playerIndex) + { + if (!_config.Additional.SkinEnabled) return; + + CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); + if (!Utility.IsPlayerValid(player)) return; + + + var steamId = new SteamID(player.SteamID); + + if (!WeaponPaints.gPlayerWeaponsInfo.ContainsKey(playerIndex)) + { + WeaponPaints.gPlayerWeaponsInfo[playerIndex] = new Dictionary(); + } + try + { + if (_config.GlobalShare) + { + var values = new Dictionary + { + { "server_id", _globalShareServerId.ToString() }, + { "steamid", steamId.SteamId64.ToString() }, + { "skins", "1" } + }; + UriBuilder builder = new UriBuilder(_globalShareApi); + builder.Query = string.Join("&", values.Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}")); + + using (var httpClient = new HttpClient()) + { + httpClient.BaseAddress = _globalShareApi; + var formContent = new FormUrlEncodedContent(values); + HttpResponseMessage response = await httpClient.GetAsync(builder.Uri); + + if (response.IsSuccessStatusCode) + { + string responseBody = await response.Content.ReadAsStringAsync(); + JArray jsonArray = JArray.Parse(responseBody); + if (jsonArray != null && jsonArray.Count > 0) + { + foreach (var weapon in jsonArray) + { + int? weaponDefIndex = weapon["weapon_defindex"]?.Value(); + int? weaponPaintId = weapon["weapon_paint_id"]?.Value(); + float? weaponWear = weapon["weapon_wear"]?.Value(); + int? weaponSeed = weapon["weapon_seed"]?.Value(); + + if (weaponDefIndex != null && weaponPaintId != null && weaponWear != null && weaponSeed != null) + { + WeaponInfo weaponInfo = new WeaponInfo + { + Paint = weaponPaintId.Value, // Example paint value + Seed = weaponSeed.Value, // Example seed value + Wear = weaponWear.Value // Example wear value + }; + WeaponPaints.gPlayerWeaponsInfo[playerIndex][weaponDefIndex.Value] = weaponInfo; + /* + gPlayerWeaponPaints[playerIndex][weaponDefIndex.Value] = weaponPaintId.Value; + gPlayerWeaponWear[playerIndex][weaponDefIndex.Value] = weaponWear.Value; + gPlayerWeaponSeed[playerIndex][weaponDefIndex.Value] = weaponSeed.Value; + */ + } + } + } + return; + } + else + { + return; + } + } + } + + using (var connection = new MySqlConnection(_databaseConnectionString)) + { + await connection.OpenAsync(); + + string query = "SELECT * FROM `wp_player_skins` WHERE `steamid` = @steamid"; + IEnumerable PlayerSkins = await connection.QueryAsync(query, new { steamid = steamId.SteamId64.ToString() }); + + if (PlayerSkins != null && PlayerSkins.AsList().Count > 0) + { + PlayerSkins.ToList().ForEach(row => + { + int weaponDefIndex = row.weapon_defindex ?? default(int); + int weaponPaintId = row.weapon_paint_id ?? default(int); + float weaponWear = row.weapon_wear ?? default(float); + int weaponSeed = row.weapon_seed ?? default(int); + + WeaponInfo weaponInfo = new WeaponInfo + { + Paint = weaponPaintId, + Seed = weaponSeed, + Wear = weaponWear + }; + WeaponPaints.gPlayerWeaponsInfo[playerIndex][weaponDefIndex] = weaponInfo; + }); + } + else + { + return; + } + await connection.CloseAsync(); + } + } + catch (Exception e) + { + Utility.Log(e.Message); + return; + } + } + + internal async Task SyncWeaponPaintsToDatabase(CCSPlayerController? player) + { + if (!Utility.IsPlayerValid(player)) return; + + int playerIndex = (int)player!.EntityIndex!.Value.Value; + string steamId = new SteamID(player.SteamID).SteamId64.ToString(); + + using var connection = new MySqlConnection(_databaseConnectionString); + await connection.OpenAsync(); + + if (!WeaponPaints.gPlayerWeaponsInfo.ContainsKey(playerIndex)) + return; + + foreach (var weaponInfoPair in WeaponPaints.gPlayerWeaponsInfo[playerIndex]) + { + int weaponDefIndex = weaponInfoPair.Key; + WeaponInfo weaponInfo = weaponInfoPair.Value; + + int paintId = weaponInfo.Paint; + float wear = weaponInfo.Wear; + int seed = weaponInfo.Seed; + + string updateSql = "UPDATE `wp_player_skins` SET `weapon_paint_id` = @paintId, " + + "`weapon_wear` = @wear, `weapon_seed` = @seed WHERE `steamid` = @steamid " + + "AND `weapon_defindex` = @weaponDefIndex"; + + var updateParams = new { paintId, wear, seed, steamid = steamId, weaponDefIndex }; + int rowsAffected = await connection.ExecuteAsync(updateSql, updateParams); + + if (rowsAffected == 0) + { + string insertSql = "INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, " + + "`weapon_paint_id`, `weapon_wear`, `weapon_seed`) " + + "VALUES (@steamid, @weaponDefIndex, @paintId, @wear, @seed)"; + + await connection.ExecuteAsync(insertSql, updateParams); + } + } + await connection.CloseAsync(); + } + } +} From 93e8af07d2dd007f0ee8780bd4c95d1f4224baa4 Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Tue, 28 Nov 2023 00:40:43 +0100 Subject: [PATCH 06/47] Changes changes --- Commands.cs | 12 +++++++----- Events.cs | 32 ++++++++++++++++++++++++++++---- Utility.cs | 2 +- WeaponAction.cs | 31 +++++++++++++++++++++++-------- WeaponSynchronization.cs | 3 +-- 5 files changed, 60 insertions(+), 20 deletions(-) diff --git a/Commands.cs b/Commands.cs index 9e8bc041..524a3ca6 100644 --- a/Commands.cs +++ b/Commands.cs @@ -151,10 +151,12 @@ namespace WeaponPaints string temp = $" {Config.Prefix} {Config.Messages.ChosenSkinMenu}".Replace("{SKIN}", selectedSkin); p.PrintToChat(Utility.ReplaceTags(temp)); - if (!gPlayerWeaponsInfo[playerIndex].ContainsKey(weaponDefIndex)) + /* + if (!gPlayerWeaponsInfo[playerIndex].ContainsKey(weaponDefIndex)) { gPlayerWeaponsInfo[playerIndex][weaponDefIndex] = new WeaponInfo(); } + */ gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Paint = paintID; gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Wear = 0.0f; @@ -223,15 +225,16 @@ namespace WeaponPaints if (!Config.Additional.CommandWpEnabled || !Config.Additional.SkinEnabled) return; if (!Utility.IsPlayerValid(player)) return; string temp = ""; + if (!player!.EntityIndex.HasValue) return; int playerIndex = (int)player!.EntityIndex!.Value.Value; - if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds)) + if (playerIndex != 0 && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds)) { commandCooldown[playerIndex] = DateTime.UtcNow; if (weaponSync != null) Task.Run(async () => await weaponSync.GetWeaponPaintsFromDatabase(playerIndex)); if (Config.Additional.KnifeEnabled) { - if (PlayerHasKnife(player)) + /*if (PlayerHasKnife(player)) RefreshPlayerKnife(player); /* AddTimer(1.0f, () => @@ -264,8 +267,7 @@ namespace WeaponPaints if (!Config.Additional.SkinEnabled) return; if (!Utility.IsPlayerValid(player)) return; - string temp = ""; - + string temp; if (!string.IsNullOrEmpty(Config.Messages.WebsiteMessageCommand)) { temp = $" {Config.Prefix} {Config.Messages.WebsiteMessageCommand}"; diff --git a/Events.cs b/Events.cs index f94afe5c..ebc145b6 100644 --- a/Events.cs +++ b/Events.cs @@ -17,15 +17,39 @@ namespace WeaponPaints private void RegisterEvents() { RegisterListener(OnEntitySpawned); - RegisterEventHandler(OnEventItemPurchasePost); - RegisterListener(OnClientAuthorized); + /*RegisterListener(OnClientAuthorized);*/ RegisterListener(OnClientDisconnect); RegisterListener(OnMapStart); + RegisterEventHandler(OnPlayerConnectFull); RegisterEventHandler(OnPlayerSpawn); RegisterEventHandler(OnRoundStart, HookMode.Pre); + RegisterEventHandler(OnEventItemPurchasePost); RegisterEventHandler(OnItemPickup); } + + private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info) + { + CCSPlayerController? player = @event.Userid; + + if (player == null || !player.IsValid || !player.EntityIndex.HasValue || player.IsHLTV) return HookResult.Continue; + + int playerIndex = (int)player.EntityIndex.Value.Value; + if (Config.Additional.SkinEnabled && weaponSync != null) + _ = weaponSync.GetWeaponPaintsFromDatabase(playerIndex); + if (Config.Additional.KnifeEnabled && weaponSync != null) + _ = weaponSync.GetKnifeFromDatabase(playerIndex); + /* + Task.Run(async () => + { + if (Config.Additional.SkinEnabled && weaponSync != null) + if (Config.Additional.KnifeEnabled && weaponSync != null) + }); + */ + + return HookResult.Continue; + } + private void OnMapStart(string mapName) { if (!Config.Additional.KnifeEnabled) return; @@ -42,10 +66,10 @@ namespace WeaponPaints int playerIndex = playerSlot + 1; Task.Run(async () => { - if (Config.Additional.KnifeEnabled && weaponSync != null) - await weaponSync.GetKnifeFromDatabase(playerIndex); if (Config.Additional.SkinEnabled && weaponSync != null) await weaponSync.GetWeaponPaintsFromDatabase(playerIndex); + if (Config.Additional.KnifeEnabled && weaponSync != null) + await weaponSync.GetKnifeFromDatabase(playerIndex); }); } private void OnClientDisconnect(int playerSlot) diff --git a/Utility.cs b/Utility.cs index a59a6047..a847ca0c 100644 --- a/Utility.cs +++ b/Utility.cs @@ -15,7 +15,7 @@ namespace WeaponPaints internal static bool IsPlayerValid(CCSPlayerController? player) { - return (player != null && player.IsValid && !player.IsBot && !player.IsHLTV); + return (player != null && player.IsValid && !player.IsBot && !player.IsHLTV && player.SteamID.ToString() != "0"); } internal static string BuildDatabaseConnectionString() diff --git a/WeaponAction.cs b/WeaponAction.cs index 9e8d52a6..ffefd9ef 100644 --- a/WeaponAction.cs +++ b/WeaponAction.cs @@ -113,6 +113,7 @@ namespace WeaponPaints } } } + internal void RefreshPlayerKnife(CCSPlayerController? player) { if (player == null || !player.IsValid || !player.PawnIsAlive) return; @@ -134,17 +135,28 @@ namespace WeaponPaints if (!weapon.Value.EntityIndex.HasValue) return; int weaponEntityIndex = (int)weapon.Value.EntityIndex!.Value.Value; NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3"); - AddTimer(0.35f, () => service.DropActivePlayerWeapon(weapon.Value)); + AddTimer(0.22f, () => + { + if (player.PlayerPawn.Value.WeaponServices.ActiveWeapon.Value.DesignerName.Contains("knife") + || + player.PlayerPawn.Value.WeaponServices.ActiveWeapon.Value.DesignerName.Contains("bayonet") + ) + { + if (player.PawnIsAlive) + { + NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3"); + service.DropActivePlayerWeapon(weapon.Value); + GiveKnifeToPlayer(player); + } + } + }); - AddTimer(1.0f, () => + AddTimer(2.5f, () => { CEntityInstance? knife = Utilities.GetEntityFromIndex(weaponEntityIndex); - if (knife != null && knife.IsValid) + if (knife != null && knife.IsValid && knife.EntityIndex.HasValue) { knife.Remove(); - - if (player.PawnIsAlive) - GiveKnifeToPlayer(player); } }); @@ -165,14 +177,17 @@ namespace WeaponPaints } internal static bool PlayerHasKnife(CCSPlayerController? player) { - if (!WeaponPaints._config.Additional.KnifeEnabled) return false; + if (!_config.Additional.KnifeEnabled) return false; if (player == null || !player.IsValid) { return false; } - var weapons = player.PlayerPawn.Value.WeaponServices!.MyWeapons; + if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) + return false; + + var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; if (weapons == null || weapons.Count <= 0) return false; foreach (var weapon in weapons) { diff --git a/WeaponSynchronization.cs b/WeaponSynchronization.cs index 560d425f..d919d9c6 100644 --- a/WeaponSynchronization.cs +++ b/WeaponSynchronization.cs @@ -124,10 +124,9 @@ namespace WeaponPaints CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); if (!Utility.IsPlayerValid(player)) return; - var steamId = new SteamID(player.SteamID); - if (!WeaponPaints.gPlayerWeaponsInfo.ContainsKey(playerIndex)) + if (!WeaponPaints.gPlayerWeaponsInfo.TryGetValue(playerIndex, out _)) { WeaponPaints.gPlayerWeaponsInfo[playerIndex] = new Dictionary(); } From 8e7a2a29232ef1fa351e41dbdb31663295af157b Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Tue, 28 Nov 2023 23:44:28 +0100 Subject: [PATCH 07/47] fixxx --- Events.cs | 25 ++++++++++++------------- Utility.cs | 1 - WeaponAction.cs | 44 ++++++++++++++++++++++++++++---------------- WeaponInfo.cs | 8 +------- WeaponPaints.cs | 5 +---- 5 files changed, 42 insertions(+), 41 deletions(-) diff --git a/Events.cs b/Events.cs index ebc145b6..9efda73c 100644 --- a/Events.cs +++ b/Events.cs @@ -1,14 +1,5 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; -using CounterStrikeSharp.API.Modules.Entities; -using CounterStrikeSharp.API.Core.Attributes; -using CounterStrikeSharp.API.Core.Attributes.Registration; -using CounterStrikeSharp.API.Modules.Utils; -using CounterStrikeSharp.API.Modules.Admin; -using CounterStrikeSharp.API.Modules.Commands; -using CounterStrikeSharp.API.Modules.Cvars; -using CounterStrikeSharp.API.Modules.Memory; -using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; namespace WeaponPaints { @@ -25,7 +16,6 @@ namespace WeaponPaints RegisterEventHandler(OnRoundStart, HookMode.Pre); RegisterEventHandler(OnEventItemPurchasePost); RegisterEventHandler(OnItemPickup); - } private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info) @@ -59,8 +49,10 @@ namespace WeaponPaints { NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); + NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0"); }); } + /* private void OnClientAuthorized(int playerSlot, SteamID steamID) { int playerIndex = playerSlot + 1; @@ -72,6 +64,7 @@ namespace WeaponPaints await weaponSync.GetKnifeFromDatabase(playerIndex); }); } + */ private void OnClientDisconnect(int playerSlot) { CCSPlayerController player = Utilities.GetPlayerFromSlot(playerSlot); @@ -110,6 +103,7 @@ namespace WeaponPaints { NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); + NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0"); return HookResult.Continue; } @@ -118,22 +112,27 @@ namespace WeaponPaints if (@event.Defindex == 42 || @event.Defindex == 59) { CCSPlayerController? player = @event.Userid; - if (!Utility.IsPlayerValid(player) || !player.PawnIsAlive || g_knifePickupCount[(int)player.EntityIndex!.Value.Value] >= 1) return HookResult.Continue; + if (!Utility.IsPlayerValid(player) || !player.PawnIsAlive || g_knifePickupCount[(int)player.EntityIndex!.Value.Value] >= 2) return HookResult.Continue; if (g_playersKnife.ContainsKey((int)player.EntityIndex!.Value.Value) && g_playersKnife[(int)player.EntityIndex!.Value.Value] != "weapon_knife") { g_knifePickupCount[(int)player.EntityIndex!.Value.Value]++; - RefreshPlayerKnife(player); + + RemovePlayerKnife(player, true); + AddTimer(0.3f, ()=> GiveKnifeToPlayer(player)); + + //RefreshPlayerKnife(player); /* if (!PlayerHasKnife(player)) GiveKnifeToPlayer(player); - */ + if (Config.Additional.SkinVisibilityFix) { AddTimer(0.25f, () => RefreshSkins(player)); } + */ } } return HookResult.Continue; diff --git a/Utility.cs b/Utility.cs index a847ca0c..3a824140 100644 --- a/Utility.cs +++ b/Utility.cs @@ -1,5 +1,4 @@ using CounterStrikeSharp.API.Core; -using CounterStrikeSharp.API.Modules.Entities; using CounterStrikeSharp.API.Modules.Utils; using Dapper; using MySqlConnector; diff --git a/WeaponAction.cs b/WeaponAction.cs index ffefd9ef..c004152c 100644 --- a/WeaponAction.cs +++ b/WeaponAction.cs @@ -75,7 +75,7 @@ namespace WeaponPaints player.GiveNamedItem(defaultKnife); } } - internal void RemovePlayerKnife(CCSPlayerController? player) + internal void RemovePlayerKnife(CCSPlayerController? player, bool force = false) { if (player == null || !player.IsValid || !player.PawnIsAlive) return; if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return; @@ -93,19 +93,26 @@ namespace WeaponPaints //if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59) if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) { - if (!weapon.Value.EntityIndex.HasValue) return; - int weaponEntityIndex = (int)weapon.Value.EntityIndex!.Value.Value; - NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3"); - AddTimer(0.35f, () => service.DropActivePlayerWeapon(weapon.Value)); - - AddTimer(1.0f, () => + if (!force) { - CEntityInstance? knife = Utilities.GetEntityFromIndex(weaponEntityIndex); - if (knife != null && knife.IsValid) + if (!weapon.Value.EntityIndex.HasValue) return; + int weaponEntityIndex = (int)weapon.Value.EntityIndex!.Value.Value; + NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3"); + AddTimer(0.35f, () => service.DropActivePlayerWeapon(weapon.Value)); + + AddTimer(1.0f, () => { - knife.Remove(); - } - }); + CEntityInstance? knife = Utilities.GetEntityFromIndex(weaponEntityIndex); + if (knife != null && knife.IsValid) + { + knife.Remove(); + } + }); + } + else + { + weapon.Value.Remove(); + } break; } @@ -151,13 +158,18 @@ namespace WeaponPaints } }); - AddTimer(2.5f, () => + Task.Delay(TimeSpan.FromSeconds(3.5)).ContinueWith(_ => { - CEntityInstance? knife = Utilities.GetEntityFromIndex(weaponEntityIndex); - if (knife != null && knife.IsValid && knife.EntityIndex.HasValue) + try { - knife.Remove(); + CEntityInstance? knife = Utilities.GetEntityFromIndex(weaponEntityIndex); + + if (knife != null && knife.IsValid && knife.Handle != -1 && knife.EntityIndex.HasValue) + { + knife.Remove(); + } } + catch (Exception) { } }); break; diff --git a/WeaponInfo.cs b/WeaponInfo.cs index 310c6e87..770ac38e 100644 --- a/WeaponInfo.cs +++ b/WeaponInfo.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace WeaponPaints +namespace WeaponPaints { public class WeaponInfo { diff --git a/WeaponPaints.cs b/WeaponPaints.cs index 82605b14..0417d2c3 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -1,14 +1,11 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core.Attributes; -using CounterStrikeSharp.API.Modules.Memory; -using CounterStrikeSharp.API.Modules.Utils; using CounterStrikeSharp.API.Modules.Cvars; -using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace WeaponPaints; -[MinimumApiVersion(61)] +[MinimumApiVersion(71)] public partial class WeaponPaints : BasePlugin, IPluginConfig { public override string ModuleName => "WeaponPaints"; From 6a7b15e9427a93224a61ca4645c92c8a4c018975 Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Wed, 29 Nov 2023 00:41:26 +0100 Subject: [PATCH 08/47] Update WeaponAction.cs --- WeaponAction.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WeaponAction.cs b/WeaponAction.cs index c004152c..e003ca1c 100644 --- a/WeaponAction.cs +++ b/WeaponAction.cs @@ -54,8 +54,8 @@ namespace WeaponPaints } internal static void GiveKnifeToPlayer(CCSPlayerController? player) { - if (!_config.Additional.KnifeEnabled || player == null || !player.EntityIndex.HasValue || !player.IsValid) return; - if (g_playersKnife.TryGetValue((int)player.EntityIndex.Value.Value, out var knife)) + if (!_config.Additional.KnifeEnabled || player == null || !player.IsValid) return; + if (g_playersKnife.TryGetValue((int)player.EntityIndex!.Value.Value, out var knife)) { player.GiveNamedItem(knife); } From a5787d95e03bd9d960d8660ddd80c000588f0c49 Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Wed, 29 Nov 2023 11:56:07 +0100 Subject: [PATCH 09/47] improved refreshing skins --- Commands.cs | 2 ++ WeaponAction.cs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/Commands.cs b/Commands.cs index 524a3ca6..6db1c65e 100644 --- a/Commands.cs +++ b/Commands.cs @@ -248,6 +248,8 @@ namespace WeaponPaints RemoveKnifeFromPlayer(player); AddTimer(0.2f, () => GiveKnifeToPlayer(player)); */ + + RefreshWeapons(player); } if (!string.IsNullOrEmpty(Config.Messages.SuccessRefreshCommand)) { diff --git a/WeaponAction.cs b/WeaponAction.cs index e003ca1c..4b9acba6 100644 --- a/WeaponAction.cs +++ b/WeaponAction.cs @@ -187,6 +187,52 @@ namespace WeaponPaints AddTimer(0.25f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot2")); AddTimer(0.38f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot1")); } + + internal void RefreshWeapons(CCSPlayerController? player) + { + if (player == null || !player.IsValid || !player.PawnIsAlive) return; + if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return; + + var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; + if (weapons != null && weapons.Count > 0) + { + CCSPlayer_ItemServices service = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle); + //var dropWeapon = VirtualFunction.CreateVoid(service.Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon")); + + foreach (var weapon in weapons) + { + if (weapon != null && weapon.IsValid && weapon.Value.IsValid) + { + if (!weapon.Value.EntityIndex.HasValue || !weapon.Value.DesignerName.Contains("weapon_")) continue; + //if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59) + if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) + { + weapon.Value.Remove(); + GiveKnifeToPlayer(player); + } + else + { + int clip1, reservedAmmo; + + clip1 = weapon.Value.Clip1; + reservedAmmo = weapon.Value.ReserveAmmo[0]; + + weapon.Value.Remove(); + CBasePlayerWeapon newWeapon = new CBasePlayerWeapon(player.GiveNamedItem(weapon.Value.DesignerName)); + + Server.NextFrame(() => + { + newWeapon.Clip1 = clip1; + newWeapon.ReserveAmmo[0] = reservedAmmo; + }); + + } + } + } + RefreshSkins(player); + } + } + internal static bool PlayerHasKnife(CCSPlayerController? player) { if (!_config.Additional.KnifeEnabled) return false; From 393281ea12631595bbc6c9f051ff1bf9e60a74e7 Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Wed, 29 Nov 2023 11:58:34 +0100 Subject: [PATCH 10/47] Maybe, maybe :+1: --- WeaponPaints.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WeaponPaints.cs b/WeaponPaints.cs index 0417d2c3..34d3553e 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -11,7 +11,7 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig "WeaponPaints"; public override string ModuleDescription => "Skin and knife selector, standalone and web-based"; public override string ModuleAuthor => "Nereziel & daffyy"; - public override string ModuleVersion => "1.2a"; + public override string ModuleVersion => "1.3a"; public WeaponPaintsConfig Config { get; set; } = new(); internal static WeaponPaintsConfig _config = new WeaponPaintsConfig(); From 73e3a052705b086f5cecb2b0855ee514deeccd4d Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Wed, 29 Nov 2023 12:37:08 +0100 Subject: [PATCH 11/47] Back to onclientauthorized Thanks cssharp :D --- Events.cs | 29 +++++++++++++++++++++-------- WeaponPaints.csproj | 4 ++-- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/Events.cs b/Events.cs index 9efda73c..bc2b5462 100644 --- a/Events.cs +++ b/Events.cs @@ -1,5 +1,6 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; +using CounterStrikeSharp.API.Modules.Entities; namespace WeaponPaints { @@ -8,17 +9,17 @@ namespace WeaponPaints private void RegisterEvents() { RegisterListener(OnEntitySpawned); - /*RegisterListener(OnClientAuthorized);*/ + RegisterListener(OnClientAuthorized); RegisterListener(OnClientDisconnect); RegisterListener(OnMapStart); - RegisterEventHandler(OnPlayerConnectFull); + //RegisterEventHandler(OnPlayerConnectFull); RegisterEventHandler(OnPlayerSpawn); RegisterEventHandler(OnRoundStart, HookMode.Pre); RegisterEventHandler(OnEventItemPurchasePost); RegisterEventHandler(OnItemPickup); } - private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info) + /*private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info) { CCSPlayerController? player = @event.Userid; @@ -29,17 +30,16 @@ namespace WeaponPaints _ = weaponSync.GetWeaponPaintsFromDatabase(playerIndex); if (Config.Additional.KnifeEnabled && weaponSync != null) _ = weaponSync.GetKnifeFromDatabase(playerIndex); - /* + Task.Run(async () => { if (Config.Additional.SkinEnabled && weaponSync != null) if (Config.Additional.KnifeEnabled && weaponSync != null) }); - */ return HookResult.Continue; } - + */ private void OnMapStart(string mapName) { if (!Config.Additional.KnifeEnabled) return; @@ -52,10 +52,22 @@ namespace WeaponPaints NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0"); }); } - /* + private void OnClientAuthorized(int playerSlot, SteamID steamID) { int playerIndex = playerSlot + 1; + + CCSPlayerController? player = Utilities.GetPlayerFromIndex(playerIndex); + + if (player == null || !player.IsValid || player.IsHLTV) return; + + if (Config.Additional.SkinEnabled && weaponSync != null) + _ = weaponSync.GetWeaponPaintsFromDatabase(playerIndex); + if (Config.Additional.KnifeEnabled && weaponSync != null) + _ = weaponSync.GetKnifeFromDatabase(playerIndex); + + + /* Task.Run(async () => { if (Config.Additional.SkinEnabled && weaponSync != null) @@ -63,8 +75,9 @@ namespace WeaponPaints if (Config.Additional.KnifeEnabled && weaponSync != null) await weaponSync.GetKnifeFromDatabase(playerIndex); }); + */ } - */ + private void OnClientDisconnect(int playerSlot) { CCSPlayerController player = Utilities.GetPlayerFromSlot(playerSlot); diff --git a/WeaponPaints.csproj b/WeaponPaints.csproj index 5db828f3..ee25d9f5 100644 --- a/WeaponPaints.csproj +++ b/WeaponPaints.csproj @@ -8,8 +8,8 @@ - - + + From 00f920713df29e326720d1e10facc0461b2a8df2 Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Wed, 29 Nov 2023 19:39:56 +0100 Subject: [PATCH 12/47] Some fixes --- Commands.cs | 37 ++++++++++++++++++----------------- Events.cs | 11 ++++++++++- WeaponAction.cs | 51 ++++++++++++++++++++++++++++--------------------- WeaponPaints.cs | 3 ++- 4 files changed, 60 insertions(+), 42 deletions(-) diff --git a/Commands.cs b/Commands.cs index 6db1c65e..0a9a4059 100644 --- a/Commands.cs +++ b/Commands.cs @@ -16,7 +16,7 @@ namespace WeaponPaints }); AddCommand($"css_{Config.Additional.CommandRefresh}", "Skins refresh", (player, info) => { - if (!Utility.IsPlayerValid(player)) return; + if (!Utility.IsPlayerValid(player) || !g_bCommandsAllowed) return; OnCommandRefresh(player, info); }); if (Config.Additional.CommandKillEnabled) @@ -31,7 +31,7 @@ namespace WeaponPaints } private void SetupKnifeMenu() { - if (!Config.Additional.KnifeEnabled) return; + if (!Config.Additional.KnifeEnabled || !g_bCommandsAllowed) return; var knivesOnly = weaponList .Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")) @@ -62,13 +62,11 @@ namespace WeaponPaints g_playersKnife[(int)player!.EntityIndex!.Value.Value] = knifeKey; - if (player!.PawnIsAlive) + if (player!.PawnIsAlive && g_bCommandsAllowed) { g_changedKnife.Add((int)player.EntityIndex!.Value.Value); - if (PlayerHasKnife(player)) - { - RefreshPlayerKnife(player); - } + RefreshWeapons(player); + //RefreshPlayerKnife(player); /* AddTimer(1.0f, () => GiveKnifeToPlayer(player)); @@ -85,7 +83,7 @@ namespace WeaponPaints } AddCommand($"css_{Config.Additional.CommandKnife}", "Knife Menu", (player, info) => { - if (!Utility.IsPlayerValid(player)) return; + if (!Utility.IsPlayerValid(player) || !g_bCommandsAllowed) return; int playerIndex = (int)player!.EntityIndex!.Value.Value; if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds) && playerIndex > 0 && playerIndex < commandCooldown.Length) @@ -128,9 +126,11 @@ namespace WeaponPaints // Function to handle skin selection for the chosen weapon var handleSkinSelection = (CCSPlayerController? p, ChatMenuOption opt) => { - if (p == null || !p.IsValid) return; + if (p == null || !p.IsValid || !p.EntityIndex.HasValue) return; - var steamId = new SteamID(player.SteamID); + playerIndex = (int)p.EntityIndex.Value.Value; + + var steamId = new SteamID(p.SteamID); var firstSkin = skinsList?.FirstOrDefault(skin => { if (skin != null && skin.TryGetValue("weapon_name", out var weaponName)) @@ -151,22 +151,23 @@ namespace WeaponPaints string temp = $" {Config.Prefix} {Config.Messages.ChosenSkinMenu}".Replace("{SKIN}", selectedSkin); p.PrintToChat(Utility.ReplaceTags(temp)); - /* - if (!gPlayerWeaponsInfo[playerIndex].ContainsKey(weaponDefIndex)) + if (!gPlayerWeaponsInfo[playerIndex].ContainsKey(weaponDefIndex)) { gPlayerWeaponsInfo[playerIndex][weaponDefIndex] = new WeaponInfo(); } - */ gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Paint = paintID; gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Wear = 0.0f; gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Seed = 0; - if (weaponSync == null) return; - Task.Run(async () => + if (!Config.GlobalShare) { - await weaponSync.SyncWeaponPaintsToDatabase(player); - }); + if (weaponSync == null) return; + Task.Run(async () => + { + await weaponSync.SyncWeaponPaintsToDatabase(p); + }); + } } }; @@ -222,7 +223,7 @@ namespace WeaponPaints private void OnCommandRefresh(CCSPlayerController? player, CommandInfo command) { - if (!Config.Additional.CommandWpEnabled || !Config.Additional.SkinEnabled) return; + if (!Config.Additional.CommandWpEnabled || !Config.Additional.SkinEnabled || !g_bCommandsAllowed) return; if (!Utility.IsPlayerValid(player)) return; string temp = ""; if (!player!.EntityIndex.HasValue) return; diff --git a/Events.cs b/Events.cs index bc2b5462..9336ef05 100644 --- a/Events.cs +++ b/Events.cs @@ -15,6 +15,7 @@ namespace WeaponPaints //RegisterEventHandler(OnPlayerConnectFull); RegisterEventHandler(OnPlayerSpawn); RegisterEventHandler(OnRoundStart, HookMode.Pre); + RegisterEventHandler(OnRoundEnd); RegisterEventHandler(OnEventItemPurchasePost); RegisterEventHandler(OnItemPickup); } @@ -66,7 +67,6 @@ namespace WeaponPaints if (Config.Additional.KnifeEnabled && weaponSync != null) _ = weaponSync.GetKnifeFromDatabase(playerIndex); - /* Task.Run(async () => { @@ -118,8 +118,17 @@ namespace WeaponPaints NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0"); + g_bCommandsAllowed = true; + return HookResult.Continue; } + + private HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info) + { + g_bCommandsAllowed = false; + return HookResult.Continue; + } + private HookResult OnItemPickup(EventItemPickup @event, GameEventInfo info) { if (@event.Defindex == 42 || @event.Defindex == 59) diff --git a/WeaponAction.cs b/WeaponAction.cs index 4b9acba6..192cb9d4 100644 --- a/WeaponAction.cs +++ b/WeaponAction.cs @@ -196,7 +196,7 @@ namespace WeaponPaints var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; if (weapons != null && weapons.Count > 0) { - CCSPlayer_ItemServices service = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle); + CCSPlayer_ItemServices service = new(player.PlayerPawn.Value.ItemServices.Handle); //var dropWeapon = VirtualFunction.CreateVoid(service.Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon")); foreach (var weapon in weapons) @@ -205,31 +205,38 @@ namespace WeaponPaints { if (!weapon.Value.EntityIndex.HasValue || !weapon.Value.DesignerName.Contains("weapon_")) continue; //if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59) - if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) + try { - weapon.Value.Remove(); - GiveKnifeToPlayer(player); - } - else + if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) + { + weapon.Value.Remove(); + GiveKnifeToPlayer(player); + } + else + { + int clip1, reservedAmmo; + + clip1 = weapon.Value.Clip1; + reservedAmmo = weapon.Value.ReserveAmmo[0]; + + weapon.Value.Remove(); + CBasePlayerWeapon newWeapon = new(player.GiveNamedItem(weapon.Value.DesignerName)); + + Server.NextFrame(() => + { + newWeapon.Clip1 = clip1; + newWeapon.ReserveAmmo[0] = reservedAmmo; + }); + } + } catch(Exception ex) { - int clip1, reservedAmmo; - - clip1 = weapon.Value.Clip1; - reservedAmmo = weapon.Value.ReserveAmmo[0]; - - weapon.Value.Remove(); - CBasePlayerWeapon newWeapon = new CBasePlayerWeapon(player.GiveNamedItem(weapon.Value.DesignerName)); - - Server.NextFrame(() => - { - newWeapon.Clip1 = clip1; - newWeapon.ReserveAmmo[0] = reservedAmmo; - }); - + Console.WriteLine("[WeaponPaints] Refreshing weapons exception"); + Console.WriteLine(ex.Message); } } } - RefreshSkins(player); + if (Config.Additional.SkinVisibilityFix) + RefreshSkins(player); } } @@ -273,7 +280,7 @@ namespace WeaponPaints if (WeaponPaints.skinsList != null) { // Filter weapons by the provided defindex - var filteredWeapons = WeaponPaints.skinsList.FindAll(w => w["weapon_defindex"]?.ToString() == defindex.ToString()); + var filteredWeapons = skinsList.FindAll(w => w["weapon_defindex"]?.ToString() == defindex.ToString()); if (filteredWeapons.Count > 0) { diff --git a/WeaponPaints.cs b/WeaponPaints.cs index 34d3553e..0f4cd368 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -5,7 +5,7 @@ using CounterStrikeSharp.API.Modules.Cvars; using Newtonsoft.Json.Linq; namespace WeaponPaints; -[MinimumApiVersion(71)] +[MinimumApiVersion(82)] public partial class WeaponPaints : BasePlugin, IPluginConfig { public override string ModuleName => "WeaponPaints"; @@ -32,6 +32,7 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig g_knifePickupCount = new Dictionary(); internal static Dictionary g_playersKnife = new(); internal static List g_changedKnife = new(); + internal bool g_bCommandsAllowed = true; internal static List skinsList = new List(); internal static readonly Dictionary weaponList = new() From be51e4d1e9a527140e7fe6603663371d9c9e1ec3 Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Thu, 30 Nov 2023 02:20:16 +0100 Subject: [PATCH 13/47] Improved player index The latest version of cssharp adds player.Index instead of entityIndex and does not allow to compile --- Commands.cs | 22 ++++++++--------- Events.cs | 33 +++++++++++++------------ Utility.cs | 2 +- WeaponAction.cs | 52 ++++++++++++++++++++-------------------- WeaponPaints.cs | 2 +- WeaponPaints.csproj | 9 +------ WeaponSynchronization.cs | 4 ++-- 7 files changed, 60 insertions(+), 64 deletions(-) diff --git a/Commands.cs b/Commands.cs index 0a9a4059..dce10b93 100644 --- a/Commands.cs +++ b/Commands.cs @@ -23,7 +23,7 @@ namespace WeaponPaints { AddCommand($"css_{Config.Additional.CommandKill}", "kill yourself", (player, info) => { - if (!Utility.IsPlayerValid(player) || !player!.PlayerPawn.IsValid) return; + if (player == null || !Utility.IsPlayerValid(player) || player.PlayerPawn.Value == null || !player!.PlayerPawn.IsValid) return; player.PlayerPawn.Value.CommitSuicide(true, false); }); @@ -60,11 +60,11 @@ namespace WeaponPaints player!.PrintToChat(Utility.ReplaceTags(temp)); } - g_playersKnife[(int)player!.EntityIndex!.Value.Value] = knifeKey; + g_playersKnife[(int)player!.Index] = knifeKey; if (player!.PawnIsAlive && g_bCommandsAllowed) { - g_changedKnife.Add((int)player.EntityIndex!.Value.Value); + //g_changedKnife.Add((int)player.Index); RefreshWeapons(player); //RefreshPlayerKnife(player); @@ -73,7 +73,7 @@ namespace WeaponPaints */ } if (weaponSync != null) - Task.Run(() => weaponSync.SyncKnifeToDatabase((int)player.EntityIndex!.Value.Value, knifeKey)); + Task.Run(() => weaponSync.SyncKnifeToDatabase((int)player.Index, knifeKey)); } } }; @@ -84,7 +84,7 @@ namespace WeaponPaints AddCommand($"css_{Config.Additional.CommandKnife}", "Knife Menu", (player, info) => { if (!Utility.IsPlayerValid(player) || !g_bCommandsAllowed) return; - int playerIndex = (int)player!.EntityIndex!.Value.Value; + int playerIndex = (int)player!.Index; if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds) && playerIndex > 0 && playerIndex < commandCooldown.Length) { @@ -110,7 +110,7 @@ namespace WeaponPaints { if (!Utility.IsPlayerValid(player)) return; - int playerIndex = (int)player!.EntityIndex!.Value.Value; + int playerIndex = (int)player!.Index; string selectedWeapon = option.Text; if (classNamesByWeapon.TryGetValue(selectedWeapon, out string? selectedWeaponClassname)) { @@ -126,9 +126,9 @@ namespace WeaponPaints // Function to handle skin selection for the chosen weapon var handleSkinSelection = (CCSPlayerController? p, ChatMenuOption opt) => { - if (p == null || !p.IsValid || !p.EntityIndex.HasValue) return; + if (p == null || !p.IsValid || p.Index <= 0) return; - playerIndex = (int)p.EntityIndex.Value.Value; + playerIndex = (int)p.Index; var steamId = new SteamID(p.SteamID); var firstSkin = skinsList?.FirstOrDefault(skin => @@ -204,7 +204,7 @@ namespace WeaponPaints AddCommand($"css_{Config.Additional.CommandSkinSelection}", "Skins selection menu", (player, info) => { if (!Utility.IsPlayerValid(player)) return; - int playerIndex = (int)player!.EntityIndex!.Value.Value; + int playerIndex = (int)player!.Index; if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds) && playerIndex > 0 && playerIndex < commandCooldown.Length) { @@ -226,8 +226,8 @@ namespace WeaponPaints if (!Config.Additional.CommandWpEnabled || !Config.Additional.SkinEnabled || !g_bCommandsAllowed) return; if (!Utility.IsPlayerValid(player)) return; string temp = ""; - if (!player!.EntityIndex.HasValue) return; - int playerIndex = (int)player!.EntityIndex!.Value.Value; + if (player == null || player.Index <= 0) return; + int playerIndex = (int)player!.Index; if (playerIndex != 0 && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds)) { commandCooldown[playerIndex] = DateTime.UtcNow; diff --git a/Events.cs b/Events.cs index 9336ef05..49d73aa8 100644 --- a/Events.cs +++ b/Events.cs @@ -85,9 +85,9 @@ namespace WeaponPaints if (player == null || !player.IsValid || player.IsHLTV) return; if (Config.Additional.KnifeEnabled) - g_playersKnife.Remove((int)player.EntityIndex!.Value.Value); + g_playersKnife.Remove((int)player.Index); if (Config.Additional.SkinEnabled) - gPlayerWeaponsInfo.Remove((int)player.EntityIndex!.Value.Value); + gPlayerWeaponsInfo.Remove((int)player.Index); } private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) @@ -100,7 +100,7 @@ namespace WeaponPaints if (Config.Additional.KnifeEnabled) { - g_knifePickupCount[(int)player.EntityIndex!.Value.Value] = 0; + g_knifePickupCount[(int)player.Index] = 0; if (!PlayerHasKnife(player)) GiveKnifeToPlayer(player); } @@ -134,13 +134,13 @@ namespace WeaponPaints if (@event.Defindex == 42 || @event.Defindex == 59) { CCSPlayerController? player = @event.Userid; - if (!Utility.IsPlayerValid(player) || !player.PawnIsAlive || g_knifePickupCount[(int)player.EntityIndex!.Value.Value] >= 2) return HookResult.Continue; + if (!Utility.IsPlayerValid(player) || !player.PawnIsAlive || g_knifePickupCount[(int)player.Index] >= 2) return HookResult.Continue; - if (g_playersKnife.ContainsKey((int)player.EntityIndex!.Value.Value) + if (g_playersKnife.ContainsKey((int)player.Index) && - g_playersKnife[(int)player.EntityIndex!.Value.Value] != "weapon_knife") + g_playersKnife[(int)player.Index] != "weapon_knife") { - g_knifePickupCount[(int)player.EntityIndex!.Value.Value]++; + g_knifePickupCount[(int)player.Index]++; RemovePlayerKnife(player, true); AddTimer(0.3f, ()=> GiveKnifeToPlayer(player)); @@ -176,29 +176,32 @@ namespace WeaponPaints { try { + if (!weapon.IsValid) return; if (weapon.OwnerEntity.Value == null) return; - if (!weapon.OwnerEntity.Value.EntityIndex.HasValue) + /* + if (weapon.OwnerEntity.Index > 0) { for (int i = 1; i <= Server.MaxPlayers; i++) { CCSPlayerController? ghostPlayer = Utilities.GetPlayerFromIndex(i); if (!Utility.IsPlayerValid(ghostPlayer)) continue; - if (g_changedKnife.Contains((int)ghostPlayer.EntityIndex!.Value.Value)) + if (g_changedKnife.Contains((int)ghostPlayer.Index)) { ChangeWeaponAttributes(weapon, ghostPlayer, isKnife); - g_changedKnife.Remove((int)ghostPlayer.EntityIndex!.Value.Value); + g_changedKnife.Remove((int)ghostPlayer.Index); break; } } - return; + } - - if (!weapon.OwnerEntity.Value.EntityIndex.HasValue) return; - int weaponOwner = (int)weapon.OwnerEntity.Value.EntityIndex.Value.Value; + */ + if (weapon.OwnerEntity.Index <= 0) return; + int weaponOwner = (int)weapon.OwnerEntity.Index; var pawn = new CBasePlayerPawn(NativeAPI.GetEntityFromIndex(weaponOwner)); if (!pawn.IsValid) return; - var playerIndex = (int)pawn.Controller.Value.EntityIndex!.Value.Value; + + var playerIndex = (int)pawn.Controller.Index; var player = Utilities.GetPlayerFromIndex(playerIndex); if (!Utility.IsPlayerValid(player)) return; diff --git a/Utility.cs b/Utility.cs index 3a824140..07572b66 100644 --- a/Utility.cs +++ b/Utility.cs @@ -19,7 +19,7 @@ namespace WeaponPaints internal static string BuildDatabaseConnectionString() { - if (Config == null) return String.Empty; + if (Config == null) return string.Empty; var builder = new MySqlConnectionStringBuilder { Server = Config.DatabaseHost, diff --git a/WeaponAction.cs b/WeaponAction.cs index 192cb9d4..4070e78e 100644 --- a/WeaponAction.cs +++ b/WeaponAction.cs @@ -9,9 +9,9 @@ namespace WeaponPaints { internal static void ChangeWeaponAttributes(CBasePlayerWeapon? weapon, CCSPlayerController? player, bool isKnife = false) { - if (weapon == null || !weapon.IsValid || !Utility.IsPlayerValid(player)) return; + if (player == null || weapon == null || !weapon.IsValid || !Utility.IsPlayerValid(player)) return; - int playerIndex = (int)player!.EntityIndex!.Value.Value; + int playerIndex = (int)player.Index; if (!gPlayerWeaponsInfo.ContainsKey(playerIndex)) return; @@ -55,7 +55,7 @@ namespace WeaponPaints internal static void GiveKnifeToPlayer(CCSPlayerController? player) { if (!_config.Additional.KnifeEnabled || player == null || !player.IsValid) return; - if (g_playersKnife.TryGetValue((int)player.EntityIndex!.Value.Value, out var knife)) + if (g_playersKnife.TryGetValue((int)player.Index, out var knife)) { player.GiveNamedItem(knife); } @@ -77,7 +77,7 @@ namespace WeaponPaints } internal void RemovePlayerKnife(CCSPlayerController? player, bool force = false) { - if (player == null || !player.IsValid || !player.PawnIsAlive) return; + if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PawnIsAlive) return; if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return; var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; @@ -88,16 +88,16 @@ namespace WeaponPaints foreach (var weapon in weapons) { - if (weapon != null && weapon.IsValid && weapon.Value.IsValid) + if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid) { //if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59) if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) { if (!force) { - if (!weapon.Value.EntityIndex.HasValue) return; - int weaponEntityIndex = (int)weapon.Value.EntityIndex!.Value.Value; - NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3"); + if ((int)weapon.Index <= 0) return; + int weaponEntityIndex = (int)weapon.Index; + NativeAPI.IssueClientCommand((int)player.Index - 1, "slot3"); AddTimer(0.35f, () => service.DropActivePlayerWeapon(weapon.Value)); AddTimer(1.0f, () => @@ -123,35 +123,35 @@ namespace WeaponPaints internal void RefreshPlayerKnife(CCSPlayerController? player) { - if (player == null || !player.IsValid || !player.PawnIsAlive) return; + if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PawnIsAlive) return; if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return; var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; if (weapons != null && weapons.Count > 0) { - CCSPlayer_ItemServices service = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle); + CCSPlayer_ItemServices service = new(player.PlayerPawn.Value.ItemServices.Handle); //var dropWeapon = VirtualFunction.CreateVoid(service.Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon")); foreach (var weapon in weapons) { - if (weapon != null && weapon.IsValid && weapon.Value.IsValid) + if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid) { //if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59) if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) { - if (!weapon.Value.EntityIndex.HasValue) return; - int weaponEntityIndex = (int)weapon.Value.EntityIndex!.Value.Value; - NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3"); + if (weapon.Index <= 0) return; + int weaponEntityIndex = (int)weapon.Index; + NativeAPI.IssueClientCommand((int)player.Index - 1, "slot3"); AddTimer(0.22f, () => { - if (player.PlayerPawn.Value.WeaponServices.ActiveWeapon.Value.DesignerName.Contains("knife") + if (player.PlayerPawn.Value.WeaponServices.ActiveWeapon.Value!.DesignerName.Contains("knife") || - player.PlayerPawn.Value.WeaponServices.ActiveWeapon.Value.DesignerName.Contains("bayonet") + player.PlayerPawn.Value.WeaponServices.ActiveWeapon.Value!.DesignerName.Contains("bayonet") ) { if (player.PawnIsAlive) { - NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3"); + NativeAPI.IssueClientCommand((int)player.Index - 1, "slot3"); service.DropActivePlayerWeapon(weapon.Value); GiveKnifeToPlayer(player); } @@ -164,7 +164,7 @@ namespace WeaponPaints { CEntityInstance? knife = Utilities.GetEntityFromIndex(weaponEntityIndex); - if (knife != null && knife.IsValid && knife.Handle != -1 && knife.EntityIndex.HasValue) + if (knife != null && knife.IsValid && knife.Handle != -1 && knife.Index > 0) { knife.Remove(); } @@ -183,14 +183,14 @@ namespace WeaponPaints { if (!Utility.IsPlayerValid(player) || !player!.PawnIsAlive) return; - AddTimer(0.18f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot3")); - AddTimer(0.25f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot2")); - AddTimer(0.38f, () => NativeAPI.IssueClientCommand((int)player.EntityIndex!.Value.Value - 1, "slot1")); + AddTimer(0.18f, () => NativeAPI.IssueClientCommand((int)player.Index - 1, "slot3")); + AddTimer(0.25f, () => NativeAPI.IssueClientCommand((int)player.Index - 1, "slot2")); + AddTimer(0.38f, () => NativeAPI.IssueClientCommand((int)player.Index - 1, "slot1")); } internal void RefreshWeapons(CCSPlayerController? player) { - if (player == null || !player.IsValid || !player.PawnIsAlive) return; + if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PawnIsAlive) return; if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return; var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; @@ -201,9 +201,9 @@ namespace WeaponPaints foreach (var weapon in weapons) { - if (weapon != null && weapon.IsValid && weapon.Value.IsValid) + if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid) { - if (!weapon.Value.EntityIndex.HasValue || !weapon.Value.DesignerName.Contains("weapon_")) continue; + if (weapon.Index <= 0 || !weapon.Value.DesignerName.Contains("weapon_")) continue; //if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59) try { @@ -249,14 +249,14 @@ namespace WeaponPaints return false; } - if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) + if (player.PlayerPawn.Value == null || player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return false; var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; if (weapons == null || weapons.Count <= 0) return false; foreach (var weapon in weapons) { - if (weapon != null && weapon.IsValid && weapon.Value.IsValid) + if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid) { if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) { diff --git a/WeaponPaints.cs b/WeaponPaints.cs index 0f4cd368..de83e1d1 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -31,7 +31,7 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig> gPlayerWeaponsInfo = new Dictionary>(); internal static Dictionary g_knifePickupCount = new Dictionary(); internal static Dictionary g_playersKnife = new(); - internal static List g_changedKnife = new(); + //internal static List g_changedKnife = new(); internal bool g_bCommandsAllowed = true; internal static List skinsList = new List(); diff --git a/WeaponPaints.csproj b/WeaponPaints.csproj index ee25d9f5..f65ee2fa 100644 --- a/WeaponPaints.csproj +++ b/WeaponPaints.csproj @@ -8,16 +8,9 @@ - + - - - - deps\CounterStrikeSharp.API.dll - - - diff --git a/WeaponSynchronization.cs b/WeaponSynchronization.cs index d919d9c6..e45ffd07 100644 --- a/WeaponSynchronization.cs +++ b/WeaponSynchronization.cs @@ -229,9 +229,9 @@ namespace WeaponPaints internal async Task SyncWeaponPaintsToDatabase(CCSPlayerController? player) { - if (!Utility.IsPlayerValid(player)) return; + if (player == null || !Utility.IsPlayerValid(player)) return; - int playerIndex = (int)player!.EntityIndex!.Value.Value; + int playerIndex = (int)player.Index; string steamId = new SteamID(player.SteamID).SteamId64.ToString(); using var connection = new MySqlConnection(_databaseConnectionString); From 2772fb59b93229a756f20a44342b8c55044b940a Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Thu, 30 Nov 2023 14:18:57 +0100 Subject: [PATCH 14/47] GlobalShare fix --- Events.cs | 3 +++ WeaponAction.cs | 13 ++++++++++--- WeaponPaints.cs | 6 +----- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Events.cs b/Events.cs index 49d73aa8..08911451 100644 --- a/Events.cs +++ b/Events.cs @@ -51,6 +51,9 @@ namespace WeaponPaints NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0"); + + if (Config.GlobalShare) + GlobalShareConnect(); }); } diff --git a/WeaponAction.cs b/WeaponAction.cs index 4070e78e..ad238e6d 100644 --- a/WeaponAction.cs +++ b/WeaponAction.cs @@ -224,11 +224,18 @@ namespace WeaponPaints Server.NextFrame(() => { - newWeapon.Clip1 = clip1; - newWeapon.ReserveAmmo[0] = reservedAmmo; + if (newWeapon == null) return; + try + { + newWeapon.Clip1 = clip1; + newWeapon.ReserveAmmo[0] = reservedAmmo; + } + catch (Exception) + { } }); } - } catch(Exception ex) + } + catch (Exception ex) { Console.WriteLine("[WeaponPaints] Refreshing weapons exception"); Console.WriteLine(ex.Message); diff --git a/WeaponPaints.cs b/WeaponPaints.cs index de83e1d1..3f6dddce 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -11,7 +11,7 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig "WeaponPaints"; public override string ModuleDescription => "Skin and knife selector, standalone and web-based"; public override string ModuleAuthor => "Nereziel & daffyy"; - public override string ModuleVersion => "1.3a"; + public override string ModuleVersion => "1.3b"; public WeaponPaintsConfig Config { get; set; } = new(); internal static WeaponPaintsConfig _config = new WeaponPaintsConfig(); @@ -101,10 +101,6 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig Date: Thu, 30 Nov 2023 17:28:40 +0100 Subject: [PATCH 15/47] Workaround for long player authorization --- Events.cs | 70 ++++++++++++++++++++++++++++++++++++++----------- WeaponPaints.cs | 2 ++ 2 files changed, 56 insertions(+), 16 deletions(-) diff --git a/Events.cs b/Events.cs index 08911451..bd1393dc 100644 --- a/Events.cs +++ b/Events.cs @@ -12,7 +12,7 @@ namespace WeaponPaints RegisterListener(OnClientAuthorized); RegisterListener(OnClientDisconnect); RegisterListener(OnMapStart); - //RegisterEventHandler(OnPlayerConnectFull); + RegisterEventHandler(OnPlayerConnectFull); RegisterEventHandler(OnPlayerSpawn); RegisterEventHandler(OnRoundStart, HookMode.Pre); RegisterEventHandler(OnRoundEnd); @@ -55,37 +55,75 @@ namespace WeaponPaints if (Config.GlobalShare) GlobalShareConnect(); }); + + g_hTimerCheckSkinsData = AddTimer(10.0f, () => + { + List players = Utilities.GetPlayers(); + + foreach (CCSPlayerController player in players) + { + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || player.SteamID == 0) continue; + if (gPlayerWeaponsInfo.ContainsKey((int)player.Index)) continue; + + if (Config.Additional.SkinEnabled && weaponSync != null) + _ = weaponSync.GetWeaponPaintsFromDatabase((int)player.Index); + if (Config.Additional.KnifeEnabled && weaponSync != null) + _ = weaponSync.GetKnifeFromDatabase((int)player.Index); + + } + }, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE | CounterStrikeSharp.API.Modules.Timers.TimerFlags.REPEAT); } - + private void OnClientAuthorized(int playerSlot, SteamID steamID) { int playerIndex = playerSlot + 1; CCSPlayerController? player = Utilities.GetPlayerFromIndex(playerIndex); - if (player == null || !player.IsValid || player.IsHLTV) return; + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; if (Config.Additional.SkinEnabled && weaponSync != null) _ = weaponSync.GetWeaponPaintsFromDatabase(playerIndex); if (Config.Additional.KnifeEnabled && weaponSync != null) _ = weaponSync.GetKnifeFromDatabase(playerIndex); - - /* - Task.Run(async () => - { - if (Config.Additional.SkinEnabled && weaponSync != null) - await weaponSync.GetWeaponPaintsFromDatabase(playerIndex); - if (Config.Additional.KnifeEnabled && weaponSync != null) - await weaponSync.GetKnifeFromDatabase(playerIndex); - }); - */ } - + + /* WORKAROUND FOR CLIENTS WITHOUT STEAMID ON AUTHORIZATION */ + private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info) + { + CCSPlayerController? player = @event.Userid; + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return HookResult.Continue; + + if (!gPlayerWeaponsInfo.ContainsKey((int)player.Index)) + { + Console.WriteLine($"[WeaponPaints] Retrying to retrieve player {player.PlayerName} skins"); + if (Config.Additional.SkinEnabled && weaponSync != null) + _ = weaponSync.GetWeaponPaintsFromDatabase((int)player.Index); + if (Config.Additional.KnifeEnabled && weaponSync != null) + _ = weaponSync.GetKnifeFromDatabase((int)player.Index); + + /* + AddTimer(2.0f, () => + { + if (!gPlayerWeaponsInfo.ContainsKey((int)player.Index)) + { + Console.WriteLine($"[WeaponPaints] Last try to retrieve player {player.PlayerName} skins"); + if (Config.Additional.SkinEnabled && weaponSync != null) + _ = weaponSync.GetWeaponPaintsFromDatabase((int)player.Index); + if (Config.Additional.KnifeEnabled && weaponSync != null) + _ = weaponSync.GetKnifeFromDatabase((int)player.Index); + } + }); + */ + } + + return HookResult.Continue; + } private void OnClientDisconnect(int playerSlot) { CCSPlayerController player = Utilities.GetPlayerFromSlot(playerSlot); - if (player == null || !player.IsValid || player.IsHLTV) return; + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; if (Config.Additional.KnifeEnabled) g_playersKnife.Remove((int)player.Index); @@ -146,7 +184,7 @@ namespace WeaponPaints g_knifePickupCount[(int)player.Index]++; RemovePlayerKnife(player, true); - AddTimer(0.3f, ()=> GiveKnifeToPlayer(player)); + AddTimer(0.3f, () => GiveKnifeToPlayer(player)); //RefreshPlayerKnife(player); /* diff --git a/WeaponPaints.cs b/WeaponPaints.cs index 3f6dddce..0c97216b 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -17,6 +17,8 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig> gPlayerWeaponPaints = new(); private Dictionary> gPlayerWeaponSeed = new(); From 06559af2305615757ffe803b0ebde7d274a05c48 Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Sat, 2 Dec 2023 12:54:13 +0100 Subject: [PATCH 16/47] Changes --- Commands.cs | 2 +- Events.cs | 12 +++++++++++- WeaponAction.cs | 4 ++-- WeaponPaints.cs | 2 +- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Commands.cs b/Commands.cs index dce10b93..6437a964 100644 --- a/Commands.cs +++ b/Commands.cs @@ -86,7 +86,7 @@ namespace WeaponPaints if (!Utility.IsPlayerValid(player) || !g_bCommandsAllowed) return; int playerIndex = (int)player!.Index; - if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds) && playerIndex > 0 && playerIndex < commandCooldown.Length) + if (commandCooldown != null && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds)) { commandCooldown[playerIndex] = DateTime.UtcNow; ChatMenus.OpenMenu(player, giveItemMenu); diff --git a/Events.cs b/Events.cs index bd1393dc..cc01183a 100644 --- a/Events.cs +++ b/Events.cs @@ -1,23 +1,26 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Entities; +using CounterStrikeSharp.API.Core.Attributes.Registration; namespace WeaponPaints { public partial class WeaponPaints { - private void RegisterEvents() + private void RegisterListeners() { RegisterListener(OnEntitySpawned); RegisterListener(OnClientAuthorized); RegisterListener(OnClientDisconnect); RegisterListener(OnMapStart); + /* RegisterEventHandler(OnPlayerConnectFull); RegisterEventHandler(OnPlayerSpawn); RegisterEventHandler(OnRoundStart, HookMode.Pre); RegisterEventHandler(OnRoundEnd); RegisterEventHandler(OnEventItemPurchasePost); RegisterEventHandler(OnItemPickup); + */ } /*private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info) @@ -89,6 +92,7 @@ namespace WeaponPaints } /* WORKAROUND FOR CLIENTS WITHOUT STEAMID ON AUTHORIZATION */ + [GameEventHandler] private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info) { CCSPlayerController? player = @event.Userid; @@ -131,6 +135,7 @@ namespace WeaponPaints gPlayerWeaponsInfo.Remove((int)player.Index); } + [GameEventHandler] private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) { CCSPlayerController? player = @event.Userid; @@ -153,6 +158,7 @@ namespace WeaponPaints return HookResult.Continue; } + [GameEventHandler(HookMode.Pre)] private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) { NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); @@ -164,12 +170,14 @@ namespace WeaponPaints return HookResult.Continue; } + [GameEventHandler] private HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info) { g_bCommandsAllowed = false; return HookResult.Continue; } + [GameEventHandler] private HookResult OnItemPickup(EventItemPickup @event, GameEventInfo info) { if (@event.Defindex == 42 || @event.Defindex == 59) @@ -257,6 +265,8 @@ namespace WeaponPaints catch (Exception) { } }); } + + [GameEventHandler] private HookResult OnEventItemPurchasePost(EventItemPurchase @event, GameEventInfo info) { CCSPlayerController? player = @event.Userid; diff --git a/WeaponAction.cs b/WeaponAction.cs index ad238e6d..e8d5991f 100644 --- a/WeaponAction.cs +++ b/WeaponAction.cs @@ -251,7 +251,7 @@ namespace WeaponPaints { if (!_config.Additional.KnifeEnabled) return false; - if (player == null || !player.IsValid) + if (player == null || !player.IsValid || !player.Pawn.IsValid) { return false; } @@ -260,7 +260,7 @@ namespace WeaponPaints return false; var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; - if (weapons == null || weapons.Count <= 0) return false; + if (weapons == null) return false; foreach (var weapon in weapons) { if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid) diff --git a/WeaponPaints.cs b/WeaponPaints.cs index 0c97216b..3a8f309f 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -126,7 +126,7 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig Date: Sat, 2 Dec 2023 13:52:06 +0100 Subject: [PATCH 17/47] CounterStrikeSharp v90 Adapted for counterstrikesharp v90 --- Commands.cs | 4 +++- Events.cs | 15 ++++----------- README.md | 11 ++++++++--- Utility.cs | 2 +- WeaponAction.cs | 6 +++--- WeaponPaints.cs | 2 +- WeaponPaints.csproj | 2 +- WeaponSynchronization.cs | 23 ++++++++++++++--------- 8 files changed, 35 insertions(+), 30 deletions(-) diff --git a/Commands.cs b/Commands.cs index 6437a964..55f4dc9b 100644 --- a/Commands.cs +++ b/Commands.cs @@ -130,7 +130,9 @@ namespace WeaponPaints playerIndex = (int)p.Index; - var steamId = new SteamID(p.SteamID); + if (p.AuthorizedSteamID == null) return; + + string steamId = p.AuthorizedSteamID.SteamId64.ToString(); var firstSkin = skinsList?.FirstOrDefault(skin => { if (skin != null && skin.TryGetValue("weapon_name", out var weaponName)) diff --git a/Events.cs b/Events.cs index cc01183a..c10cd136 100644 --- a/Events.cs +++ b/Events.cs @@ -1,7 +1,6 @@ using CounterStrikeSharp.API; using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Entities; -using CounterStrikeSharp.API.Core.Attributes.Registration; namespace WeaponPaints { @@ -13,14 +12,13 @@ namespace WeaponPaints RegisterListener(OnClientAuthorized); RegisterListener(OnClientDisconnect); RegisterListener(OnMapStart); - /* + RegisterEventHandler(OnPlayerConnectFull); RegisterEventHandler(OnPlayerSpawn); RegisterEventHandler(OnRoundStart, HookMode.Pre); RegisterEventHandler(OnRoundEnd); RegisterEventHandler(OnEventItemPurchasePost); RegisterEventHandler(OnItemPickup); - */ } /*private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info) @@ -65,7 +63,7 @@ namespace WeaponPaints foreach (CCSPlayerController player in players) { - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || player.SteamID == 0) continue; + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || player.AuthorizedSteamID == null) continue; if (gPlayerWeaponsInfo.ContainsKey((int)player.Index)) continue; if (Config.Additional.SkinEnabled && weaponSync != null) @@ -92,7 +90,6 @@ namespace WeaponPaints } /* WORKAROUND FOR CLIENTS WITHOUT STEAMID ON AUTHORIZATION */ - [GameEventHandler] private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info) { CCSPlayerController? player = @event.Userid; @@ -135,11 +132,10 @@ namespace WeaponPaints gPlayerWeaponsInfo.Remove((int)player.Index); } - [GameEventHandler] private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) { CCSPlayerController? player = @event.Userid; - if (player == null || !player.IsValid || !player.PlayerPawn.IsValid) + if (player == null || !player.IsValid) { return HookResult.Continue; } @@ -158,7 +154,7 @@ namespace WeaponPaints return HookResult.Continue; } - [GameEventHandler(HookMode.Pre)] + private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) { NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); @@ -170,14 +166,12 @@ namespace WeaponPaints return HookResult.Continue; } - [GameEventHandler] private HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info) { g_bCommandsAllowed = false; return HookResult.Continue; } - [GameEventHandler] private HookResult OnItemPickup(EventItemPickup @event, GameEventInfo info) { if (@event.Defindex == 42 || @event.Defindex == 59) @@ -266,7 +260,6 @@ namespace WeaponPaints }); } - [GameEventHandler] private HookResult OnEventItemPurchasePost(EventItemPurchase @event, GameEventInfo info) { CCSPlayerController? player = @event.Userid; diff --git a/README.md b/README.md index 127c82ff..eaa5f34c 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin ## Plugin Configuration
- Spoiler warning + Click to expand
{
 	"Version": 4, // Don't touch
 	"DatabaseHost": "", // MySQL host (required if GlobalShare = false)
@@ -43,8 +43,11 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
 	"CooldownRefreshCommand": "You can\u0027t refresh weapon paints right now.", // Cooldown information (!wp command) Set to empty to disable
 	"SuccessRefreshCommand": "Refreshing weapon paints.", // Information about refreshing skins (!wp command) Set to empty to disable
 	"ChosenKnifeMenu": "You have chosen {KNIFE} as your knife.", // Information about choosen knife (!knife command) Set to empty to disable
+	"ChosenSkinMenu": "You have chosen {SKIN} as your skin.", // Information about choosen skin (!skins command) Set to empty to disable
 	"ChosenKnifeMenuKill": "To correctly apply skin for knife, you need to type !kill.", // Information about suicide after knife selection (!knife command) Set to empty to disable
-	"KnifeMenuTitle": "Knife Menu."  // Menu title (!knife menu)
+	"KnifeMenuTitle": "Knife Menu.",  // Menu title (!knife menu)
+	"WeaponMenuTitle": "Weapon Menu.", // Menu title (!skins menu)
+	"SkinMenuTitle": "Select skin for {WEAPON}" // Menu title (!skins menu, after weapon select)
 },
 "Additional": {
 	"SkinVisibilityFix": true, // Enable or disable fix for skin visibility
@@ -54,9 +57,11 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
 	"CommandKillEnabled": true, // Enable or disable kill command
 	"CommandKnife": "knife", // Name of knife menu command, u can change to for e.g, knives
 	"CommandSkin": "ws", // Name of skin information command, u can change to for e.g, skins
+	"CommandSkinSelection": "skins", // Name of skins menu command, u can change to for e.g, weapons
 	"CommandRefresh": "wp", // Name of skin refreshing command, u can change to for e.g, refreshskins
 	"CommandKill": "kill", // Name of kill command, u can change to for e.g, suicide
-	"GiveRandomKnife": false  // Give random knife to players if they didn't choose
+	"GiveRandomKnife": false,  // Give random knife to players if they didn't choose
+	"GiveRandomSkins": false  // Give random skins to players if they didn't choose
 },
 
 "ConfigVersion": 4  // Don't touch
diff --git a/Utility.cs b/Utility.cs
index 07572b66..1419fd64 100644
--- a/Utility.cs
+++ b/Utility.cs
@@ -14,7 +14,7 @@ namespace WeaponPaints
 
 		internal static bool IsPlayerValid(CCSPlayerController? player)
 		{
-			return (player != null && player.IsValid && !player.IsBot && !player.IsHLTV && player.SteamID.ToString() != "0");
+			return (player != null && player.IsValid && !player.IsBot && !player.IsHLTV && player.AuthorizedSteamID != null);
 		}
 
 		internal static string BuildDatabaseConnectionString()
diff --git a/WeaponAction.cs b/WeaponAction.cs
index e8d5991f..00bc6e30 100644
--- a/WeaponAction.cs
+++ b/WeaponAction.cs
@@ -251,15 +251,15 @@ namespace WeaponPaints
 		{
 			if (!_config.Additional.KnifeEnabled) return false;
 
-			if (player == null || !player.IsValid || !player.Pawn.IsValid)
+			if (player == null || !player.IsValid || !player.PlayerPawn.IsValid)
 			{
 				return false;
 			}
 
-			if (player.PlayerPawn.Value == null || player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null)
+			if (player.PlayerPawn?.Value == null || player.PlayerPawn?.Value.WeaponServices == null || player.PlayerPawn?.Value.ItemServices == null)
 				return false;
 
-			var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons;
+			var weapons = player.PlayerPawn.Value.WeaponServices?.MyWeapons;
 			if (weapons == null) return false;
 			foreach (var weapon in weapons)
 			{
diff --git a/WeaponPaints.cs b/WeaponPaints.cs
index 3a8f309f..555f5acd 100644
--- a/WeaponPaints.cs
+++ b/WeaponPaints.cs
@@ -5,7 +5,7 @@ using CounterStrikeSharp.API.Modules.Cvars;
 using Newtonsoft.Json.Linq;
 
 namespace WeaponPaints;
-[MinimumApiVersion(82)]
+[MinimumApiVersion(90)]
 public partial class WeaponPaints : BasePlugin, IPluginConfig
 {
 	public override string ModuleName => "WeaponPaints";
diff --git a/WeaponPaints.csproj b/WeaponPaints.csproj
index f65ee2fa..76a279cf 100644
--- a/WeaponPaints.csproj
+++ b/WeaponPaints.csproj
@@ -8,7 +8,7 @@
   
 
   
-    
+    
     
     
     
diff --git a/WeaponSynchronization.cs b/WeaponSynchronization.cs
index e45ffd07..5d520f25 100644
--- a/WeaponSynchronization.cs
+++ b/WeaponSynchronization.cs
@@ -31,14 +31,15 @@ namespace WeaponPaints
 			{
 				CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex);
 				if (!Utility.IsPlayerValid(player)) return;
-				var steamId = new SteamID(player.SteamID);
+				if (player.AuthorizedSteamID == null) return;
+				string steamId = player.AuthorizedSteamID.SteamId64.ToString();
 
 				if (_config.GlobalShare)
 				{
 					var values = new Dictionary
 				{
 				   { "server_id", _globalShareServerId.ToString() },
-				   { "steamid", steamId.SteamId64.ToString() },
+				   { "steamid", steamId },
 				   { "knife", "1" }
 				};
 					UriBuilder builder = new UriBuilder(_globalShareApi);
@@ -75,7 +76,7 @@ namespace WeaponPaints
 				{
 					await connection.OpenAsync();
 					string query = "SELECT `knife` FROM `wp_player_knife` WHERE `steamid` = @steamid";
-					string? PlayerKnife = await connection.QueryFirstOrDefaultAsync(query, new { steamid = steamId.SteamId64.ToString() });
+					string? PlayerKnife = await connection.QueryFirstOrDefaultAsync(query, new { steamid = steamId });
 					if (PlayerKnife != null)
 					{
 						WeaponPaints.g_playersKnife[playerIndex] = PlayerKnife;
@@ -102,12 +103,13 @@ namespace WeaponPaints
 			{
 				CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex);
 				if (player == null || !player.IsValid) return;
-				var steamId = new SteamID(player.SteamID);
+				if (player.AuthorizedSteamID == null) return;
+				string steamId = player.AuthorizedSteamID.SteamId64.ToString();
 
 				using var connection = new MySqlConnection(_databaseConnectionString);
 				await connection.OpenAsync();
 				string query = "INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(@steamid, @newKnife) ON DUPLICATE KEY UPDATE `knife` = @newKnife";
-				await connection.ExecuteAsync(query, new { steamid = steamId.SteamId64.ToString(), newKnife = knife });
+				await connection.ExecuteAsync(query, new { steamid = steamId, newKnife = knife });
 				await connection.CloseAsync();
 			}
 			catch (Exception e)
@@ -124,7 +126,9 @@ namespace WeaponPaints
 			CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex);
 			if (!Utility.IsPlayerValid(player)) return;
 
-			var steamId = new SteamID(player.SteamID);
+			if (player.AuthorizedSteamID == null) return;
+
+			string steamId = player.AuthorizedSteamID.SteamId64.ToString();
 
 			if (!WeaponPaints.gPlayerWeaponsInfo.TryGetValue(playerIndex, out _))
 			{
@@ -137,7 +141,7 @@ namespace WeaponPaints
 					var values = new Dictionary
 				{
 				   { "server_id", _globalShareServerId.ToString() },
-				   { "steamid", steamId.SteamId64.ToString() },
+				   { "steamid", steamId },
 				   { "skins", "1" }
 				};
 					UriBuilder builder = new UriBuilder(_globalShareApi);
@@ -193,7 +197,7 @@ namespace WeaponPaints
 					await connection.OpenAsync();
 
 					string query = "SELECT * FROM `wp_player_skins` WHERE `steamid` = @steamid";
-					IEnumerable PlayerSkins = await connection.QueryAsync(query, new { steamid = steamId.SteamId64.ToString() });
+					IEnumerable PlayerSkins = await connection.QueryAsync(query, new { steamid = steamId });
 
 					if (PlayerSkins != null && PlayerSkins.AsList().Count > 0)
 					{
@@ -232,7 +236,8 @@ namespace WeaponPaints
 			if (player == null || !Utility.IsPlayerValid(player)) return;
 
 			int playerIndex = (int)player.Index;
-			string steamId = new SteamID(player.SteamID).SteamId64.ToString();
+			if (player.AuthorizedSteamID == null) return;
+			string steamId = player.AuthorizedSteamID.SteamId64.ToString();
 
 			using var connection = new MySqlConnection(_databaseConnectionString);
 			await connection.OpenAsync();

From 1e5c2a439fdc438f9a148bd8d258500373c9b5f4 Mon Sep 17 00:00:00 2001
From: daffyyyy 
Date: Sun, 3 Dec 2023 13:00:37 +0100
Subject: [PATCH 18/47] Fix for !wp and comments cleanup

---
 Commands.cs              | 136 +++++++++-----------
 Events.cs                | 270 ++++++++++++++++-----------------------
 Utility.cs               |  63 ++++-----
 WeaponAction.cs          | 124 +++++++++---------
 WeaponInfo.cs            |   2 +-
 WeaponPaints.cs          | 124 ++++++++++++------
 WeaponSynchronization.cs |  65 ++++------
 7 files changed, 375 insertions(+), 409 deletions(-)

diff --git a/Commands.cs b/Commands.cs
index 55f4dc9b..fc6d6474 100644
--- a/Commands.cs
+++ b/Commands.cs
@@ -1,12 +1,68 @@
 using CounterStrikeSharp.API.Core;
 using CounterStrikeSharp.API.Modules.Commands;
-using CounterStrikeSharp.API.Modules.Entities;
 using CounterStrikeSharp.API.Modules.Menu;
 
 namespace WeaponPaints
 {
 	public partial class WeaponPaints
 	{
+		private void OnCommandRefresh(CCSPlayerController? player, CommandInfo command)
+		{
+			if (!Config.Additional.CommandWpEnabled || !Config.Additional.SkinEnabled || !g_bCommandsAllowed) return;
+			if (!Utility.IsPlayerValid(player)) return;
+			string temp = "";
+			if (player == null || player.Index <= 0) return;
+			int playerIndex = (int)player!.Index;
+			if (playerIndex != 0 && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds))
+			{
+				commandCooldown[playerIndex] = DateTime.UtcNow;
+				if (weaponSync != null)
+					Task.Run(async () => await weaponSync.GetWeaponPaintsFromDatabase(playerIndex));
+				if (Config.Additional.KnifeEnabled)
+				{
+					if (weaponSync != null)
+						Task.Run(async () => await weaponSync.GetKnifeFromDatabase(playerIndex));
+
+					RefreshWeapons(player);
+				}
+				if (!string.IsNullOrEmpty(Config.Messages.SuccessRefreshCommand))
+				{
+					temp = $" {Config.Prefix} {Config.Messages.SuccessRefreshCommand}";
+					player.PrintToChat(Utility.ReplaceTags(temp));
+				}
+				return;
+			}
+			if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand))
+			{
+				temp = $" {Config.Prefix} {Config.Messages.CooldownRefreshCommand}";
+				player.PrintToChat(Utility.ReplaceTags(temp));
+			}
+		}
+
+		private void OnCommandWS(CCSPlayerController? player, CommandInfo command)
+		{
+			if (!Config.Additional.SkinEnabled) return;
+			if (!Utility.IsPlayerValid(player)) return;
+
+			string temp;
+			if (!string.IsNullOrEmpty(Config.Messages.WebsiteMessageCommand))
+			{
+				temp = $" {Config.Prefix} {Config.Messages.WebsiteMessageCommand}";
+				player!.PrintToChat(Utility.ReplaceTags(temp));
+			}
+			if (!string.IsNullOrEmpty(Config.Messages.SynchronizeMessageCommand))
+			{
+				temp = $" {Config.Prefix} {Config.Messages.SynchronizeMessageCommand}";
+				player!.PrintToChat(Utility.ReplaceTags(temp));
+			}
+			if (!Config.Additional.KnifeEnabled) return;
+			if (!string.IsNullOrEmpty(Config.Messages.KnifeMessageCommand))
+			{
+				temp = $" {Config.Prefix} {Config.Messages.KnifeMessageCommand}";
+				player!.PrintToChat(Utility.ReplaceTags(temp));
+			}
+		}
+
 		private void RegisterCommands()
 		{
 			AddCommand($"css_{Config.Additional.CommandSkin}", "Skins info", (player, info) =>
@@ -29,6 +85,7 @@ namespace WeaponPaints
 				});
 			}
 		}
+
 		private void SetupKnifeMenu()
 		{
 			if (!Config.Additional.KnifeEnabled || !g_bCommandsAllowed) return;
@@ -64,13 +121,7 @@ namespace WeaponPaints
 
 						if (player!.PawnIsAlive && g_bCommandsAllowed)
 						{
-							//g_changedKnife.Add((int)player.Index);
 							RefreshWeapons(player);
-							//RefreshPlayerKnife(player);
-
-							/*
-							AddTimer(1.0f, () => GiveKnifeToPlayer(player));
-							*/
 						}
 						if (weaponSync != null)
 							Task.Run(() => weaponSync.SyncKnifeToDatabase((int)player.Index, knifeKey));
@@ -219,76 +270,7 @@ namespace WeaponPaints
 					string temp = $"{Config.Prefix} {Config.Messages.CooldownRefreshCommand}";
 					player.PrintToChat(Utility.ReplaceTags(temp));
 				}
-
 			});
 		}
-
-		private void OnCommandRefresh(CCSPlayerController? player, CommandInfo command)
-		{
-			if (!Config.Additional.CommandWpEnabled || !Config.Additional.SkinEnabled || !g_bCommandsAllowed) return;
-			if (!Utility.IsPlayerValid(player)) return;
-			string temp = "";
-			if (player == null || player.Index <= 0) return;
-			int playerIndex = (int)player!.Index;
-			if (playerIndex != 0 && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds))
-			{
-				commandCooldown[playerIndex] = DateTime.UtcNow;
-				if (weaponSync != null)
-					Task.Run(async () => await weaponSync.GetWeaponPaintsFromDatabase(playerIndex));
-				if (Config.Additional.KnifeEnabled)
-				{
-					/*if (PlayerHasKnife(player))
-						RefreshPlayerKnife(player);
-					/*
-					AddTimer(1.0f, () =>
-					{
-						GiveKnifeToPlayer(player);
-					});
-					*/
-					if (weaponSync != null)
-						Task.Run(async () => await weaponSync.GetKnifeFromDatabase(playerIndex));
-					/*
-					RemoveKnifeFromPlayer(player);
-					AddTimer(0.2f, () => GiveKnifeToPlayer(player));
-					*/
-
-					RefreshWeapons(player);
-				}
-				if (!string.IsNullOrEmpty(Config.Messages.SuccessRefreshCommand))
-				{
-					temp = $" {Config.Prefix} {Config.Messages.SuccessRefreshCommand}";
-					player.PrintToChat(Utility.ReplaceTags(temp));
-				}
-				return;
-			}
-			if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand))
-			{
-				temp = $" {Config.Prefix} {Config.Messages.CooldownRefreshCommand}";
-				player.PrintToChat(Utility.ReplaceTags(temp));
-			}
-		}
-		private void OnCommandWS(CCSPlayerController? player, CommandInfo command)
-		{
-			if (!Config.Additional.SkinEnabled) return;
-			if (!Utility.IsPlayerValid(player)) return;
-
-			string temp;
-			if (!string.IsNullOrEmpty(Config.Messages.WebsiteMessageCommand))
-			{
-				temp = $" {Config.Prefix} {Config.Messages.WebsiteMessageCommand}";
-				player!.PrintToChat(Utility.ReplaceTags(temp));
-			}
-			if (!string.IsNullOrEmpty(Config.Messages.SynchronizeMessageCommand))
-			{
-				temp = $" {Config.Prefix} {Config.Messages.SynchronizeMessageCommand}";
-				player!.PrintToChat(Utility.ReplaceTags(temp));
-			}
-			if (!Config.Additional.KnifeEnabled) return;
-			if (!string.IsNullOrEmpty(Config.Messages.KnifeMessageCommand))
-			{
-				temp = $" {Config.Prefix} {Config.Messages.KnifeMessageCommand}";
-				player!.PrintToChat(Utility.ReplaceTags(temp));
-			}
-		}
 	}
-}
+}
\ No newline at end of file
diff --git a/Events.cs b/Events.cs
index c10cd136..09e30a39 100644
--- a/Events.cs
+++ b/Events.cs
@@ -6,42 +6,98 @@ namespace WeaponPaints
 {
 	public partial class WeaponPaints
 	{
-		private void RegisterListeners()
+		private void OnClientAuthorized(int playerSlot, SteamID steamID)
 		{
-			RegisterListener(OnEntitySpawned);
-			RegisterListener(OnClientAuthorized);
-			RegisterListener(OnClientDisconnect);
-			RegisterListener(OnMapStart);
-		
-			RegisterEventHandler(OnPlayerConnectFull);
-			RegisterEventHandler(OnPlayerSpawn);
-			RegisterEventHandler(OnRoundStart, HookMode.Pre);
-			RegisterEventHandler(OnRoundEnd);
-			RegisterEventHandler(OnEventItemPurchasePost);
-			RegisterEventHandler(OnItemPickup);
-		}
+			int playerIndex = playerSlot + 1;
 
-		/*private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info)
-		{
-			CCSPlayerController? player = @event.Userid;
+			CCSPlayerController? player = Utilities.GetPlayerFromIndex(playerIndex);
 
-			if (player == null || !player.IsValid || !player.EntityIndex.HasValue || player.IsHLTV) return HookResult.Continue;
+			if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return;
 
-			int playerIndex = (int)player.EntityIndex.Value.Value;
 			if (Config.Additional.SkinEnabled && weaponSync != null)
 				_ = weaponSync.GetWeaponPaintsFromDatabase(playerIndex);
 			if (Config.Additional.KnifeEnabled && weaponSync != null)
 				_ = weaponSync.GetKnifeFromDatabase(playerIndex);
+		}
 
-			Task.Run(async () =>
+		private void OnClientDisconnect(int playerSlot)
+		{
+			CCSPlayerController player = Utilities.GetPlayerFromSlot(playerSlot);
+
+			if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return;
+
+			if (Config.Additional.KnifeEnabled)
+				g_playersKnife.Remove((int)player.Index);
+			if (Config.Additional.SkinEnabled)
+				gPlayerWeaponsInfo.Remove((int)player.Index);
+		}
+
+		private void OnEntitySpawned(CEntityInstance entity)
+		{
+			if (!Config.Additional.SkinEnabled) return;
+			var designerName = entity.DesignerName;
+			if (!weaponList.ContainsKey(designerName)) return;
+			bool isKnife = false;
+			var weapon = new CBasePlayerWeapon(entity.Handle);
+
+			if (designerName.Contains("knife") || designerName.Contains("bayonet"))
 			{
-				if (Config.Additional.SkinEnabled && weaponSync != null)
-				if (Config.Additional.KnifeEnabled && weaponSync != null)
+				isKnife = true;
+			}
+			Server.NextFrame(() =>
+			{
+				try
+				{
+					if (!weapon.IsValid) return;
+					if (weapon.OwnerEntity.Value == null) return;
+					if (weapon.OwnerEntity.Index <= 0) return;
+					int weaponOwner = (int)weapon.OwnerEntity.Index;
+					var pawn = new CBasePlayerPawn(NativeAPI.GetEntityFromIndex(weaponOwner));
+					if (!pawn.IsValid) return;
+
+					var playerIndex = (int)pawn.Controller.Index;
+					var player = Utilities.GetPlayerFromIndex(playerIndex);
+					if (!Utility.IsPlayerValid(player)) return;
+
+					ChangeWeaponAttributes(weapon, player, isKnife);
+				}
+				catch (Exception) { }
 			});
+		}
+
+		private HookResult OnEventItemPurchasePost(EventItemPurchase @event, GameEventInfo info)
+		{
+			CCSPlayerController? player = @event.Userid;
+
+			if (player == null || !player.IsValid) return HookResult.Continue;
+
+			if (Config.Additional.SkinVisibilityFix)
+				AddTimer(0.2f, () => RefreshSkins(player));
 
 			return HookResult.Continue;
 		}
-	*/
+
+		private HookResult OnItemPickup(EventItemPickup @event, GameEventInfo info)
+		{
+			if (@event.Defindex == 42 || @event.Defindex == 59)
+			{
+				CCSPlayerController? player = @event.Userid;
+				if (!Utility.IsPlayerValid(player) || !player.PawnIsAlive || g_knifePickupCount[(int)player.Index] >= 2) return HookResult.Continue;
+
+				if (g_playersKnife.ContainsKey((int)player.Index)
+					&&
+				   g_playersKnife[(int)player.Index] != "weapon_knife")
+				{
+					g_knifePickupCount[(int)player.Index]++;
+
+					RemovePlayerKnife(player, true);
+					AddTimer(0.3f, () => GiveKnifeToPlayer(player));
+
+				}
+			}
+			return HookResult.Continue;
+		}
+
 		private void OnMapStart(string mapName)
 		{
 			if (!Config.Additional.KnifeEnabled) return;
@@ -70,26 +126,10 @@ namespace WeaponPaints
 						_ = weaponSync.GetWeaponPaintsFromDatabase((int)player.Index);
 					if (Config.Additional.KnifeEnabled && weaponSync != null)
 						_ = weaponSync.GetKnifeFromDatabase((int)player.Index);
-
 				}
 			}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE | CounterStrikeSharp.API.Modules.Timers.TimerFlags.REPEAT);
 		}
 
-		private void OnClientAuthorized(int playerSlot, SteamID steamID)
-		{
-			int playerIndex = playerSlot + 1;
-
-			CCSPlayerController? player = Utilities.GetPlayerFromIndex(playerIndex);
-
-			if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return;
-
-			if (Config.Additional.SkinEnabled && weaponSync != null)
-				_ = weaponSync.GetWeaponPaintsFromDatabase(playerIndex);
-			if (Config.Additional.KnifeEnabled && weaponSync != null)
-				_ = weaponSync.GetKnifeFromDatabase(playerIndex);
-		}
-
-		/* WORKAROUND FOR CLIENTS WITHOUT STEAMID ON AUTHORIZATION */
 		private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info)
 		{
 			CCSPlayerController? player = @event.Userid;
@@ -103,34 +143,10 @@ namespace WeaponPaints
 				if (Config.Additional.KnifeEnabled && weaponSync != null)
 					_ = weaponSync.GetKnifeFromDatabase((int)player.Index);
 
-				/*
-				AddTimer(2.0f, () =>
-				{
-					if (!gPlayerWeaponsInfo.ContainsKey((int)player.Index))
-					{
-						Console.WriteLine($"[WeaponPaints] Last try to retrieve player {player.PlayerName} skins");
-						if (Config.Additional.SkinEnabled && weaponSync != null)
-							_ = weaponSync.GetWeaponPaintsFromDatabase((int)player.Index);
-						if (Config.Additional.KnifeEnabled && weaponSync != null)
-							_ = weaponSync.GetKnifeFromDatabase((int)player.Index);
-					}
-				});
-				*/
 			}
 
 			return HookResult.Continue;
 		}
-		private void OnClientDisconnect(int playerSlot)
-		{
-			CCSPlayerController player = Utilities.GetPlayerFromSlot(playerSlot);
-
-			if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return;
-
-			if (Config.Additional.KnifeEnabled)
-				g_playersKnife.Remove((int)player.Index);
-			if (Config.Additional.SkinEnabled)
-				gPlayerWeaponsInfo.Remove((int)player.Index);
-		}
 
 		private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info)
 		{
@@ -155,6 +171,12 @@ namespace WeaponPaints
 			return HookResult.Continue;
 		}
 
+		private HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info)
+		{
+			g_bCommandsAllowed = false;
+			return HookResult.Continue;
+		}
+
 		private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info)
 		{
 			NativeAPI.IssueServerCommand("mp_t_default_melee \"\"");
@@ -166,110 +188,42 @@ namespace WeaponPaints
 			return HookResult.Continue;
 		}
 
-		private HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info)
+		private void RegisterListeners()
 		{
-			g_bCommandsAllowed = false;
-			return HookResult.Continue;
+			RegisterListener(OnEntitySpawned);
+			RegisterListener(OnClientAuthorized);
+			RegisterListener(OnClientDisconnect);
+			RegisterListener(OnMapStart);
+
+			RegisterEventHandler(OnPlayerConnectFull);
+			RegisterEventHandler(OnPlayerSpawn);
+			RegisterEventHandler(OnRoundStart, HookMode.Pre);
+			RegisterEventHandler(OnRoundEnd);
+			RegisterEventHandler(OnEventItemPurchasePost);
+			RegisterEventHandler(OnItemPickup);
 		}
 
-		private HookResult OnItemPickup(EventItemPickup @event, GameEventInfo info)
-		{
-			if (@event.Defindex == 42 || @event.Defindex == 59)
-			{
-				CCSPlayerController? player = @event.Userid;
-				if (!Utility.IsPlayerValid(player) || !player.PawnIsAlive || g_knifePickupCount[(int)player.Index] >= 2) return HookResult.Continue;
-
-				if (g_playersKnife.ContainsKey((int)player.Index)
-					&&
-				   g_playersKnife[(int)player.Index] != "weapon_knife")
-				{
-					g_knifePickupCount[(int)player.Index]++;
-
-					RemovePlayerKnife(player, true);
-					AddTimer(0.3f, () => GiveKnifeToPlayer(player));
-
-					//RefreshPlayerKnife(player);
-					/*
-					if (!PlayerHasKnife(player))
-						GiveKnifeToPlayer(player);
-					
-					if (Config.Additional.SkinVisibilityFix)
-					{
-						AddTimer(0.25f, () => RefreshSkins(player));
-					}
-					*/
-				}
-			}
-			return HookResult.Continue;
-		}
-
-		private void OnEntitySpawned(CEntityInstance entity)
-		{
-			if (!Config.Additional.SkinEnabled) return;
-			var designerName = entity.DesignerName;
-			if (!weaponList.ContainsKey(designerName)) return;
-			bool isKnife = false;
-			var weapon = new CBasePlayerWeapon(entity.Handle);
-
-			if (designerName.Contains("knife") || designerName.Contains("bayonet"))
-			{
-				isKnife = true;
-			}
-			Server.NextFrame(() =>
-			{
-				try
-				{
-
-					if (!weapon.IsValid) return;
-					if (weapon.OwnerEntity.Value == null) return;
-					/*
-					if (weapon.OwnerEntity.Index > 0)
-					{
-						for (int i = 1; i <= Server.MaxPlayers; i++)
-						{
-							CCSPlayerController? ghostPlayer = Utilities.GetPlayerFromIndex(i);
-							if (!Utility.IsPlayerValid(ghostPlayer)) continue;
-							if (g_changedKnife.Contains((int)ghostPlayer.Index))
-							{
-								ChangeWeaponAttributes(weapon, ghostPlayer, isKnife);
-								g_changedKnife.Remove((int)ghostPlayer.Index);
-								break;
-							}
-						}
-						
-					}
-					*/
-					if (weapon.OwnerEntity.Index <= 0) return;
-					int weaponOwner = (int)weapon.OwnerEntity.Index;
-					var pawn = new CBasePlayerPawn(NativeAPI.GetEntityFromIndex(weaponOwner));
-					if (!pawn.IsValid) return;
-
-					var playerIndex = (int)pawn.Controller.Index;
-					var player = Utilities.GetPlayerFromIndex(playerIndex);
-					if (!Utility.IsPlayerValid(player)) return;
-
-					// TODO: Remove knife crashes here, needs another solution
-					/*if (isKnife && g_playersKnife[(int)player.EntityIndex!.Value.Value] != "weapon_knife" && (weapon.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.AttributeManager.Item.ItemDefinitionIndex == 59))
-					{
-						RemoveKnifeFromPlayer(player);
-						return;
-					}*/
-					ChangeWeaponAttributes(weapon, player, isKnife);
-				}
-				catch (Exception) { }
-			});
-		}
-
-		private HookResult OnEventItemPurchasePost(EventItemPurchase @event, GameEventInfo info)
+		/* WORKAROUND FOR CLIENTS WITHOUT STEAMID ON AUTHORIZATION */
+		/*private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info)
 		{
 			CCSPlayerController? player = @event.Userid;
 
-			if (player == null || !player.IsValid) return HookResult.Continue;
+			if (player == null || !player.IsValid || !player.EntityIndex.HasValue || player.IsHLTV) return HookResult.Continue;
 
-			if (Config.Additional.SkinVisibilityFix)
-				AddTimer(0.2f, () => RefreshSkins(player));
+			int playerIndex = (int)player.EntityIndex.Value.Value;
+			if (Config.Additional.SkinEnabled && weaponSync != null)
+				_ = weaponSync.GetWeaponPaintsFromDatabase(playerIndex);
+			if (Config.Additional.KnifeEnabled && weaponSync != null)
+				_ = weaponSync.GetKnifeFromDatabase(playerIndex);
+
+			Task.Run(async () =>
+			{
+				if (Config.Additional.SkinEnabled && weaponSync != null)
+				if (Config.Additional.KnifeEnabled && weaponSync != null)
+			});
 
 			return HookResult.Continue;
 		}
+	*/
 	}
-}
+}
\ No newline at end of file
diff --git a/Utility.cs b/Utility.cs
index 1419fd64..6e7876da 100644
--- a/Utility.cs
+++ b/Utility.cs
@@ -12,11 +12,6 @@ namespace WeaponPaints
 	{
 		internal static WeaponPaintsConfig? Config { get; set; }
 
-		internal static bool IsPlayerValid(CCSPlayerController? player)
-		{
-			return (player != null && player.IsValid && !player.IsBot && !player.IsHLTV && player.AuthorizedSteamID != null);
-		}
-
 		internal static string BuildDatabaseConnectionString()
 		{
 			if (Config == null) return string.Empty;
@@ -32,25 +27,6 @@ namespace WeaponPaints
 			return builder.ConnectionString;
 		}
 
-		internal static void TestDatabaseConnection()
-		{
-			try
-			{
-				using var connection = new MySqlConnection(BuildDatabaseConnectionString());
-				connection.Open();
-
-				if (connection.State != System.Data.ConnectionState.Open)
-				{
-					throw new Exception("[WeaponPaints] Unable connect to database!");
-				}
-			}
-			catch (Exception ex)
-			{
-				throw new Exception("[WeaponPaints] Unknown mysql exception! " + ex.Message);
-			}
-			CheckDatabaseTables();
-		}
-
 		internal static async void CheckDatabaseTables()
 		{
 			try
@@ -81,6 +57,11 @@ namespace WeaponPaints
 				throw new Exception("[WeaponPaints] Unknown mysql exception! " + ex.Message);
 			}
 		}
+
+		internal static bool IsPlayerValid(CCSPlayerController? player)
+		{
+			return (player != null && player.IsValid && !player.IsBot && !player.IsHLTV && player.AuthorizedSteamID != null);
+		}
 		internal static void LoadSkinsFromFile(string filePath)
 		{
 			if (File.Exists(filePath))
@@ -95,6 +76,14 @@ namespace WeaponPaints
 			}
 		}
 
+		internal static void Log(string message)
+		{
+			Console.BackgroundColor = ConsoleColor.DarkGray;
+			Console.ForegroundColor = ConsoleColor.Cyan;
+			Console.WriteLine("[WeaponPaints] " + message);
+			Console.ResetColor();
+		}
+
 		internal static string ReplaceTags(string message)
 		{
 			if (message.Contains('{'))
@@ -118,13 +107,6 @@ namespace WeaponPaints
 			return message;
 		}
 
-		internal static void Log(string message)
-		{
-			Console.BackgroundColor = ConsoleColor.DarkGray;
-			Console.ForegroundColor = ConsoleColor.Cyan;
-			Console.WriteLine("[WeaponPaints] " + message);
-			Console.ResetColor();
-		}
 		internal static void ShowAd(string moduleVersion)
 		{
 			Console.WriteLine(" ");
@@ -139,5 +121,24 @@ namespace WeaponPaints
 			Console.WriteLine("			>> GitHub: https://github.com/Nereziel/cs2-WeaponPaints");
 			Console.WriteLine(" ");
 		}
+
+		internal static void TestDatabaseConnection()
+		{
+			try
+			{
+				using var connection = new MySqlConnection(BuildDatabaseConnectionString());
+				connection.Open();
+
+				if (connection.State != System.Data.ConnectionState.Open)
+				{
+					throw new Exception("[WeaponPaints] Unable connect to database!");
+				}
+			}
+			catch (Exception ex)
+			{
+				throw new Exception("[WeaponPaints] Unknown mysql exception! " + ex.Message);
+			}
+			CheckDatabaseTables();
+		}
 	}
 }
\ No newline at end of file
diff --git a/WeaponAction.cs b/WeaponAction.cs
index 00bc6e30..ed33bda6 100644
--- a/WeaponAction.cs
+++ b/WeaponAction.cs
@@ -52,6 +52,7 @@ namespace WeaponPaints
 				skeleton.ModelState.MeshGroupMask = 2;
 			}
 		}
+
 		internal static void GiveKnifeToPlayer(CCSPlayerController? player)
 		{
 			if (!_config.Additional.KnifeEnabled || player == null || !player.IsValid) return;
@@ -75,50 +76,32 @@ namespace WeaponPaints
 				player.GiveNamedItem(defaultKnife);
 			}
 		}
-		internal void RemovePlayerKnife(CCSPlayerController? player, bool force = false)
+
+		internal static bool PlayerHasKnife(CCSPlayerController? player)
 		{
-			if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PawnIsAlive) return;
-			if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return;
+			if (!_config.Additional.KnifeEnabled) return false;
 
-			var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons;
-			if (weapons != null && weapons.Count > 0)
+			if (player == null || !player.IsValid || !player.PlayerPawn.IsValid)
 			{
-				CCSPlayer_ItemServices service = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle);
-				//var dropWeapon = VirtualFunction.CreateVoid(service.Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon"));
+				return false;
+			}
 
-				foreach (var weapon in weapons)
+			if (player.PlayerPawn?.Value == null || player.PlayerPawn?.Value.WeaponServices == null || player.PlayerPawn?.Value.ItemServices == null)
+				return false;
+
+			var weapons = player.PlayerPawn.Value.WeaponServices?.MyWeapons;
+			if (weapons == null) return false;
+			foreach (var weapon in weapons)
+			{
+				if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid)
 				{
-					if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid)
+					if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet"))
 					{
-						//if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59)
-						if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet"))
-						{
-							if (!force)
-							{
-								if ((int)weapon.Index <= 0) return;
-								int weaponEntityIndex = (int)weapon.Index;
-								NativeAPI.IssueClientCommand((int)player.Index - 1, "slot3");
-								AddTimer(0.35f, () => service.DropActivePlayerWeapon(weapon.Value));
-
-								AddTimer(1.0f, () =>
-								{
-									CEntityInstance? knife = Utilities.GetEntityFromIndex(weaponEntityIndex);
-									if (knife != null && knife.IsValid)
-									{
-										knife.Remove();
-									}
-								});
-							}
-							else
-							{
-								weapon.Value.Remove();
-							}
-
-							break;
-						}
+						return true;
 					}
 				}
 			}
+			return false;
 		}
 
 		internal void RefreshPlayerKnife(CCSPlayerController? player)
@@ -197,7 +180,6 @@ namespace WeaponPaints
 			if (weapons != null && weapons.Count > 0)
 			{
 				CCSPlayer_ItemServices service = new(player.PlayerPawn.Value.ItemServices.Handle);
-				//var dropWeapon = VirtualFunction.CreateVoid(service.Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon"));
 
 				foreach (var weapon in weapons)
 				{
@@ -214,13 +196,15 @@ namespace WeaponPaints
 							}
 							else
 							{
+								if (!weaponDefindex.ContainsKey(weapon.Value.AttributeManager.Item.ItemDefinitionIndex)) continue;
 								int clip1, reservedAmmo;
 
 								clip1 = weapon.Value.Clip1;
 								reservedAmmo = weapon.Value.ReserveAmmo[0];
 
 								weapon.Value.Remove();
-								CBasePlayerWeapon newWeapon = new(player.GiveNamedItem(weapon.Value.DesignerName));
+								string weaponByDefindex = weaponDefindex[weapon.Value.AttributeManager.Item.ItemDefinitionIndex];
+								CBasePlayerWeapon newWeapon = new(player.GiveNamedItem(weaponByDefindex));
 
 								Server.NextFrame(() =>
 								{
@@ -247,39 +231,50 @@ namespace WeaponPaints
 			}
 		}
 
-		internal static bool PlayerHasKnife(CCSPlayerController? player)
+		internal void RemovePlayerKnife(CCSPlayerController? player, bool force = false)
 		{
-			if (!_config.Additional.KnifeEnabled) return false;
+			if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PawnIsAlive) return;
+			if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return;
 
-			if (player == null || !player.IsValid || !player.PlayerPawn.IsValid)
+			var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons;
+			if (weapons != null && weapons.Count > 0)
 			{
-				return false;
-			}
+				CCSPlayer_ItemServices service = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle);
 
-			if (player.PlayerPawn?.Value == null || player.PlayerPawn?.Value.WeaponServices == null || player.PlayerPawn?.Value.ItemServices == null)
-				return false;
-
-			var weapons = player.PlayerPawn.Value.WeaponServices?.MyWeapons;
-			if (weapons == null) return false;
-			foreach (var weapon in weapons)
-			{
-				if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid)
+				foreach (var weapon in weapons)
 				{
-					if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet"))
+					if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid)
 					{
-						return true;
+						//if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59)
+						if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet"))
+						{
+							if (!force)
+							{
+								if ((int)weapon.Index <= 0) return;
+								int weaponEntityIndex = (int)weapon.Index;
+								NativeAPI.IssueClientCommand((int)player.Index - 1, "slot3");
+								AddTimer(0.35f, () => service.DropActivePlayerWeapon(weapon.Value));
+
+								AddTimer(1.0f, () =>
+								{
+									CEntityInstance? knife = Utilities.GetEntityFromIndex(weaponEntityIndex);
+									if (knife != null && knife.IsValid)
+									{
+										knife.Remove();
+									}
+								});
+							}
+							else
+							{
+								weapon.Value.Remove();
+							}
+
+							break;
+						}
 					}
 				}
 			}
-			return false;
 		}
-
-		private static CSkeletonInstance GetSkeletonInstance(CGameSceneNode node)
-		{
-			Func GetSkeletonInstance = VirtualFunction.Create(node.Handle, 8);
-			return new CSkeletonInstance(GetSkeletonInstance(node.Handle));
-		}
-
 		private static int GetRandomPaint(int defindex)
 		{
 			Random rnd = new Random();
@@ -300,10 +295,15 @@ namespace WeaponPaints
 					{
 						return 0;
 					}
-
 				}
 			}
 			return 0;
 		}
+
+		private static CSkeletonInstance GetSkeletonInstance(CGameSceneNode node)
+		{
+			Func GetSkeletonInstance = VirtualFunction.Create(node.Handle, 8);
+			return new CSkeletonInstance(GetSkeletonInstance(node.Handle));
+		}
 	}
-}
+}
\ No newline at end of file
diff --git a/WeaponInfo.cs b/WeaponInfo.cs
index 770ac38e..dc702bbf 100644
--- a/WeaponInfo.cs
+++ b/WeaponInfo.cs
@@ -6,4 +6,4 @@
 		public int Seed { get; set; }
 		public float Wear { get; set; }
 	}
-}
+}
\ No newline at end of file
diff --git a/WeaponPaints.cs b/WeaponPaints.cs
index 555f5acd..5ceb2e39 100644
--- a/WeaponPaints.cs
+++ b/WeaponPaints.cs
@@ -5,38 +5,10 @@ using CounterStrikeSharp.API.Modules.Cvars;
 using Newtonsoft.Json.Linq;
 
 namespace WeaponPaints;
+
 [MinimumApiVersion(90)]
 public partial class WeaponPaints : BasePlugin, IPluginConfig
 {
-	public override string ModuleName => "WeaponPaints";
-	public override string ModuleDescription => "Skin and knife selector, standalone and web-based";
-	public override string ModuleAuthor => "Nereziel & daffyy";
-	public override string ModuleVersion => "1.3b";
-	public WeaponPaintsConfig Config { get; set; } = new();
-	internal static WeaponPaintsConfig _config = new WeaponPaintsConfig();
-
-	internal static WeaponSynchronization? weaponSync;
-
-	private CounterStrikeSharp.API.Modules.Timers.Timer? g_hTimerCheckSkinsData = null;
-
-	/*
-	private Dictionary> gPlayerWeaponPaints = new();
-	private Dictionary> gPlayerWeaponSeed = new();
-	private Dictionary> gPlayerWeaponWear = new();
-	*/
-	private string DatabaseConnectionString = string.Empty;
-
-	internal Uri GlobalShareApi = new Uri("https://weaponpaints.fun/api.php");
-	internal int GlobalShareServerId = 0;
-
-	private DateTime[] commandCooldown = new DateTime[Server.MaxPlayers];
-	internal static Dictionary> gPlayerWeaponsInfo = new Dictionary>();
-	internal static Dictionary g_knifePickupCount = new Dictionary();
-	internal static Dictionary g_playersKnife = new();
-	//internal static List g_changedKnife = new();
-	internal bool g_bCommandsAllowed = true;
-
-	internal static List skinsList = new List();
 	internal static readonly Dictionary weaponList = new()
 	{
 		{"weapon_deagle", "Desert Eagle"},
@@ -95,6 +67,87 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig g_knifePickupCount = new Dictionary();
+	internal static Dictionary g_playersKnife = new();
+	internal static Dictionary> gPlayerWeaponsInfo = new Dictionary>();
+	internal static List skinsList = new List();
+	internal static WeaponSynchronization? weaponSync;
+	//internal static List g_changedKnife = new();
+	internal bool g_bCommandsAllowed = true;
+
+	internal Uri GlobalShareApi = new Uri("https://weaponpaints.fun/api.php");
+	internal int GlobalShareServerId = 0;
+	private DateTime[] commandCooldown = new DateTime[Server.MaxPlayers];
+	private string DatabaseConnectionString = string.Empty;
+	private CounterStrikeSharp.API.Modules.Timers.Timer? g_hTimerCheckSkinsData = null;
+	public static Dictionary weaponDefindex { get; } = new Dictionary
+	{
+		{ 1, "weapon_deagle" },
+		{ 2, "weapon_elite" },
+		{ 3, "weapon_fiveseven" },
+		{ 4, "weapon_glock" },
+		{ 7, "weapon_ak47" },
+		{ 8, "weapon_aug" },
+		{ 9, "weapon_awp" },
+		{ 10, "weapon_famas" },
+		{ 11, "weapon_g3sg1" },
+		{ 13, "weapon_galilar" },
+		{ 14, "weapon_m249" },
+		{ 16, "weapon_m4a1" },
+		{ 17, "weapon_mac10" },
+		{ 19, "weapon_p90" },
+		{ 23, "weapon_mp5sd" },
+		{ 24, "weapon_ump45" },
+		{ 25, "weapon_xm1014" },
+		{ 26, "weapon_bizon" },
+		{ 27, "weapon_mag7" },
+		{ 28, "weapon_negev" },
+		{ 29, "weapon_sawedoff" },
+		{ 30, "weapon_tec9" },
+		{ 32, "weapon_hkp2000" },
+		{ 33, "weapon_mp7" },
+		{ 34, "weapon_mp9" },
+		{ 35, "weapon_nova" },
+		{ 36, "weapon_p250" },
+		{ 38, "weapon_scar20" },
+		{ 39, "weapon_sg556" },
+		{ 40, "weapon_ssg08" },
+		{ 60, "weapon_m4a1_silencer" },
+		{ 61, "weapon_usp_silencer" },
+		{ 63, "weapon_cz75a" },
+		{ 64, "weapon_revolver" },
+		{ 500, "weapon_bayonet" },
+		{ 503, "weapon_knife_css" },
+		{ 505, "weapon_knife_flip" },
+		{ 506, "weapon_knife_gut" },
+		{ 507, "weapon_knife_karambit" },
+		{ 508, "weapon_knife_m9_bayonet" },
+		{ 509, "weapon_knife_tactical" },
+		{ 512, "weapon_knife_falchion" },
+		{ 514, "weapon_knife_survival_bowie" },
+		{ 515, "weapon_knife_butterfly" },
+		{ 516, "weapon_knife_push" },
+		{ 517, "weapon_knife_cord" },
+		{ 518, "weapon_knife_canis" },
+		{ 519, "weapon_knife_ursus" },
+		{ 520, "weapon_knife_gypsy_jackknife" },
+		{ 521, "weapon_knife_outdoor" },
+		{ 522, "weapon_knife_stiletto" },
+		{ 523, "weapon_knife_widowmaker" },
+		{ 525, "weapon_knife_skeleton" }
+	};
+
+	public WeaponPaintsConfig Config { get; set; } = new();
+	public override string ModuleAuthor => "Nereziel & daffyy";
+	public override string ModuleDescription => "Skin and knife selector, standalone and web-based";
+	public override string ModuleName => "WeaponPaints";
+	public override string ModuleVersion => "1.3c";
+	public static WeaponPaintsConfig GetWeaponPaintsConfig()
+	{
+		return _config;
+	}
+
 	public override void Load(bool hotReload)
 	{
 		if (!Config.GlobalShare)
@@ -148,19 +201,6 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig {g_playersKnife[playerIndex]}");
-			}
-			catch (Exception e)
-			{
-				Utility.Log(e.Message);
-				return;
-			}
-		}
-
-		internal async Task SyncKnifeToDatabase(int playerIndex, string knife)
-		{
-			if (!_config.Additional.KnifeEnabled) return;
-			try
-			{
-				CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex);
-				if (player == null || !player.IsValid) return;
-				if (player.AuthorizedSteamID == null) return;
-				string steamId = player.AuthorizedSteamID.SteamId64.ToString();
-
-				using var connection = new MySqlConnection(_databaseConnectionString);
-				await connection.OpenAsync();
-				string query = "INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(@steamid, @newKnife) ON DUPLICATE KEY UPDATE `knife` = @newKnife";
-				await connection.ExecuteAsync(query, new { steamid = steamId, newKnife = knife });
-				await connection.CloseAsync();
 			}
 			catch (Exception e)
 			{
@@ -170,16 +142,11 @@ namespace WeaponPaints
 									{
 										WeaponInfo weaponInfo = new WeaponInfo
 										{
-											Paint = weaponPaintId.Value, // Example paint value
-											Seed = weaponSeed.Value, // Example seed value
-											Wear = weaponWear.Value // Example wear value
+											Paint = weaponPaintId.Value,
+											Seed = weaponSeed.Value,
+											Wear = weaponWear.Value
 										};
 										WeaponPaints.gPlayerWeaponsInfo[playerIndex][weaponDefIndex.Value] = weaponInfo;
-										/*
-										gPlayerWeaponPaints[playerIndex][weaponDefIndex.Value] = weaponPaintId.Value;
-										gPlayerWeaponWear[playerIndex][weaponDefIndex.Value] = weaponWear.Value;
-										gPlayerWeaponSeed[playerIndex][weaponDefIndex.Value] = weaponSeed.Value;
-										*/
 									}
 								}
 							}
@@ -231,6 +198,28 @@ namespace WeaponPaints
 			}
 		}
 
+		internal async Task SyncKnifeToDatabase(int playerIndex, string knife)
+		{
+			if (!_config.Additional.KnifeEnabled) return;
+			try
+			{
+				CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex);
+				if (player == null || !player.IsValid) return;
+				if (player.AuthorizedSteamID == null) return;
+				string steamId = player.AuthorizedSteamID.SteamId64.ToString();
+
+				using var connection = new MySqlConnection(_databaseConnectionString);
+				await connection.OpenAsync();
+				string query = "INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(@steamid, @newKnife) ON DUPLICATE KEY UPDATE `knife` = @newKnife";
+				await connection.ExecuteAsync(query, new { steamid = steamId, newKnife = knife });
+				await connection.CloseAsync();
+			}
+			catch (Exception e)
+			{
+				Utility.Log(e.Message);
+				return;
+			}
+		}
 		internal async Task SyncWeaponPaintsToDatabase(CCSPlayerController? player)
 		{
 			if (player == null || !Utility.IsPlayerValid(player)) return;
@@ -273,4 +262,4 @@ namespace WeaponPaints
 			await connection.CloseAsync();
 		}
 	}
-}
+}
\ No newline at end of file

From ed4da98a5061df7da30965443f1e2a24a10c4fa6 Mon Sep 17 00:00:00 2001
From: Nereziel 
Date: Sun, 3 Dec 2023 21:25:04 +0100
Subject: [PATCH 19/47] edit default fallback

+removed unused php
---
 Utility.cs           |  2 +-
 WeaponAction.cs      |  2 +-
 website/getskins.php | 73 --------------------------------------------
 3 files changed, 2 insertions(+), 75 deletions(-)
 delete mode 100644 website/getskins.php

diff --git a/Utility.cs b/Utility.cs
index 6e7876da..16bae464 100644
--- a/Utility.cs
+++ b/Utility.cs
@@ -38,7 +38,7 @@ namespace WeaponPaints
 
 				try
 				{
-					string createTable1 = "CREATE TABLE IF NOT EXISTS `wp_player_skins` (`steamid` varchar(64) NOT NULL, `weapon_defindex` int(6) NOT NULL, `weapon_paint_id` int(6) NOT NULL, `weapon_wear` float NOT NULL DEFAULT 0.0001, `weapon_seed` int(16) NOT NULL DEFAULT 0) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci";
+					string createTable1 = "CREATE TABLE IF NOT EXISTS `wp_player_skins` (`steamid` varchar(64) NOT NULL, `weapon_defindex` int(6) NOT NULL, `weapon_paint_id` int(6) NOT NULL, `weapon_wear` float NOT NULL DEFAULT 0.000001, `weapon_seed` int(16) NOT NULL DEFAULT 0) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_ci";
 					string createTable2 = "CREATE TABLE IF NOT EXISTS `wp_player_knife` (`steamid` varchar(64) NOT NULL, `knife` varchar(64) NOT NULL, UNIQUE (`steamid`)) ENGINE = InnoDB";
 
 					await connection.ExecuteAsync(createTable1, transaction: transaction);
diff --git a/WeaponAction.cs b/WeaponAction.cs
index ed33bda6..a68c1341 100644
--- a/WeaponAction.cs
+++ b/WeaponAction.cs
@@ -28,7 +28,7 @@ namespace WeaponPaints
 				weapon.AttributeManager.Item.ItemIDHigh = weapon.AttributeManager.Item.ItemIDLow >> 32;
 				weapon.FallbackPaintKit = GetRandomPaint(weaponDefIndex);
 				weapon.FallbackSeed = 0;
-				weapon.FallbackWear = 0.0f;
+				weapon.FallbackWear = 0.000001f;
 				if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null)
 				{
 					var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode);
diff --git a/website/getskins.php b/website/getskins.php
deleted file mode 100644
index 95539e97..00000000
--- a/website/getskins.php
+++ /dev/null
@@ -1,73 +0,0 @@
- 1,
-"weapon_elite" => 2,
-"weapon_fiveseven" => 3,
-"weapon_glock" => 4,
-"weapon_ak47" => 7,
-"weapon_aug" => 8,
-"weapon_awp" => 9,
-"weapon_famas" => 10,
-"weapon_g3sg1" => 10,
-"weapon_galilar" => 13,
-"weapon_m249" => 14,
-"weapon_m4a1" => 16,
-"weapon_mac10" => 17,
-"weapon_p90" => 19,
-"weapon_mp5sd" => 23,
-"weapon_ump45" => 24,
-"weapon_xm1014" => 25,
-"weapon_bizon" => 26,
-"weapon_mag7" => 27,
-"weapon_negev" => 28,
-"weapon_sawedoff" => 29,
-"weapon_tec9" => 30,
-"weapon_hkp2000" => 32,
-"weapon_mp7" => 33,
-"weapon_mp9" => 34,
-"weapon_nova" => 35,
-"weapon_p250" => 36,
-"weapon_scar20" => 38,
-"weapon_sg556" => 39,
-"weapon_ssg08" => 40,
-"weapon_m4a1_silencer" => 60,
-"weapon_usp_silencer" => 61,
-"weapon_cz75a" => 63,
-"weapon_revolver" => 64,
-"weapon_bayonet" => 500,
-"weapon_knife_css" => 503,
-"weapon_knife_flip" => 505,
-"weapon_knife_gut" => 506,
-"weapon_knife_karambit" => 507,
-"weapon_knife_m9_bayonet" => 508,
-"weapon_knife_tactical" => 509,
-"weapon_knife_falchion" => 512,
-"weapon_knife_survival_bowie"=> 514,
-"weapon_knife_butterfly" => 515,
-"weapon_knife_push" => 516,
-"weapon_knife_cord" => 517,
-"weapon_knife_canis" => 518,
-"weapon_knife_ursus" => 519,
-"weapon_knife_gypsy_jackknife" => 520,
-"weapon_knife_outdoor" => 521,
-"weapon_knife_stiletto" => 522,
-"weapon_knife_widowmaker" => 523,
-"weapon_knife_skeleton" => 525);
-$json = json_decode(file_get_contents('skins.json')); 
-echo "
";
-foreach($json as $skin)
-{
-	if(!str_contains($skin->weapon->id, "weapon_")) continue;
-	$name = $skin->name;
-	$name = str_replace("'","\'",$name);
-	$weapon = $skin->weapon->id;
-	$image = $skin->image;
-	$paint = $skin->paint_index;
-	echo "('{$weapon}', {$weapons[$weapon]}, {$paint}, '{$image}', '{$name}')";
-	echo ",
"; - -} -//print_r($json); -echo "
"; - -?> From 5e6286b667798a995810e310b25a38b14bcef5b1 Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Mon, 4 Dec 2023 21:00:24 +0100 Subject: [PATCH 20/47] Update for cssharp 101 --- Commands.cs | 18 ++++++++--- Events.cs | 66 +++++++++++++++++++++++++++++++--------- PlayerInfo.cs | 11 +++++++ WeaponPaints.cs | 39 ++++++++++++++++-------- WeaponPaints.csproj | 2 +- WeaponSynchronization.cs | 40 ++++++++++-------------- 6 files changed, 119 insertions(+), 57 deletions(-) create mode 100644 PlayerInfo.cs diff --git a/Commands.cs b/Commands.cs index fc6d6474..c235706d 100644 --- a/Commands.cs +++ b/Commands.cs @@ -13,29 +13,39 @@ namespace WeaponPaints string temp = ""; if (player == null || player.Index <= 0) return; int playerIndex = (int)player!.Index; + + PlayerInfo playerInfo = new PlayerInfo + { + UserId = player.UserId, + Index = (int)player.Index, + SteamId = player?.AuthorizedSteamID?.SteamId64.ToString(), + Name = player?.PlayerName, + IpAddress = player?.IpAddress?.Split(":")[0] + }; + if (playerIndex != 0 && DateTime.UtcNow >= commandCooldown[playerIndex].AddSeconds(Config.CmdRefreshCooldownSeconds)) { commandCooldown[playerIndex] = DateTime.UtcNow; if (weaponSync != null) - Task.Run(async () => await weaponSync.GetWeaponPaintsFromDatabase(playerIndex)); + Task.Run(async () => await weaponSync.GetWeaponPaintsFromDatabase(playerInfo)); if (Config.Additional.KnifeEnabled) { if (weaponSync != null) - Task.Run(async () => await weaponSync.GetKnifeFromDatabase(playerIndex)); + Task.Run(async () => await weaponSync.GetKnifeFromDatabase(playerInfo)); RefreshWeapons(player); } if (!string.IsNullOrEmpty(Config.Messages.SuccessRefreshCommand)) { temp = $" {Config.Prefix} {Config.Messages.SuccessRefreshCommand}"; - player.PrintToChat(Utility.ReplaceTags(temp)); + player!.PrintToChat(Utility.ReplaceTags(temp)); } return; } if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) { temp = $" {Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; - player.PrintToChat(Utility.ReplaceTags(temp)); + player!.PrintToChat(Utility.ReplaceTags(temp)); } } diff --git a/Events.cs b/Events.cs index 09e30a39..7cdeb45b 100644 --- a/Events.cs +++ b/Events.cs @@ -12,12 +12,25 @@ namespace WeaponPaints CCSPlayerController? player = Utilities.GetPlayerFromIndex(playerIndex); - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return; + PlayerInfo playerInfo = new PlayerInfo + { + UserId = player.UserId, + Index = (int)player.Index, + SteamId = player?.AuthorizedSteamID?.SteamId64.ToString(), + Name = player?.PlayerName, + IpAddress = player?.IpAddress?.Split(":")[0] + }; - if (Config.Additional.SkinEnabled && weaponSync != null) - _ = weaponSync.GetWeaponPaintsFromDatabase(playerIndex); - if (Config.Additional.KnifeEnabled && weaponSync != null) - _ = weaponSync.GetKnifeFromDatabase(playerIndex); + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || weaponSync == null) return; + + Task.Run(async () => + { + if (Config.Additional.SkinEnabled) + await weaponSync.GetKnifeFromDatabase(playerInfo); + }); + + //if (Config.Additional.KnifeEnabled && weaponSync != null) + //_ = weaponSync.GetKnifeFromDatabase(playerIndex); } private void OnClientDisconnect(int playerSlot) @@ -92,7 +105,6 @@ namespace WeaponPaints RemovePlayerKnife(player, true); AddTimer(0.3f, () => GiveKnifeToPlayer(player)); - } } return HookResult.Continue; @@ -111,6 +123,8 @@ namespace WeaponPaints if (Config.GlobalShare) GlobalShareConnect(); + + weaponSync = new WeaponSynchronization(DatabaseConnectionString, Config, GlobalShareApi, GlobalShareServerId); }); g_hTimerCheckSkinsData = AddTimer(10.0f, () => @@ -122,27 +136,49 @@ namespace WeaponPaints if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || player.AuthorizedSteamID == null) continue; if (gPlayerWeaponsInfo.ContainsKey((int)player.Index)) continue; + PlayerInfo playerInfo = new PlayerInfo + { + UserId = player.UserId, + Index = (int)player.Index, + SteamId = player?.AuthorizedSteamID?.SteamId64.ToString(), + Name = player?.PlayerName, + IpAddress = player?.IpAddress?.Split(":")[0] + }; + if (Config.Additional.SkinEnabled && weaponSync != null) - _ = weaponSync.GetWeaponPaintsFromDatabase((int)player.Index); + _ = weaponSync.GetWeaponPaintsFromDatabase(playerInfo); if (Config.Additional.KnifeEnabled && weaponSync != null) - _ = weaponSync.GetKnifeFromDatabase((int)player.Index); + _ = weaponSync.GetKnifeFromDatabase(playerInfo); } }, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE | CounterStrikeSharp.API.Modules.Timers.TimerFlags.REPEAT); + } private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info) { CCSPlayerController? player = @event.Userid; - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV) return HookResult.Continue; - if (!gPlayerWeaponsInfo.ContainsKey((int)player.Index)) + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || weaponSync == null) return HookResult.Continue; + + PlayerInfo playerInfo = new PlayerInfo + { + UserId = player.UserId, + Index = (int)player.Index, + SteamId = player?.AuthorizedSteamID?.SteamId64.ToString(), + Name = player?.PlayerName, + IpAddress = player?.IpAddress?.Split(":")[0] + }; + + if (!gPlayerWeaponsInfo.ContainsKey((int)player!.Index)) { Console.WriteLine($"[WeaponPaints] Retrying to retrieve player {player.PlayerName} skins"); - if (Config.Additional.SkinEnabled && weaponSync != null) - _ = weaponSync.GetWeaponPaintsFromDatabase((int)player.Index); - if (Config.Additional.KnifeEnabled && weaponSync != null) - _ = weaponSync.GetKnifeFromDatabase((int)player.Index); - + Task.Run(async () => + { + if (Config.Additional.SkinEnabled) + await weaponSync.GetWeaponPaintsFromDatabase(playerInfo); + if (Config.Additional.KnifeEnabled) + await weaponSync.GetKnifeFromDatabase(playerInfo); + }); } return HookResult.Continue; diff --git a/PlayerInfo.cs b/PlayerInfo.cs new file mode 100644 index 00000000..dba103b6 --- /dev/null +++ b/PlayerInfo.cs @@ -0,0 +1,11 @@ +namespace WeaponPaints +{ + public class PlayerInfo + { + public int Index { get; set; } + public int? UserId { get; set; } + public string? SteamId { get; set; } + public string? Name { get; set; } + public string? IpAddress { get; set; } + } +} diff --git a/WeaponPaints.cs b/WeaponPaints.cs index 5ceb2e39..9333e98a 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -6,7 +6,7 @@ using Newtonsoft.Json.Linq; namespace WeaponPaints; -[MinimumApiVersion(90)] +[MinimumApiVersion(101)] public partial class WeaponPaints : BasePlugin, IPluginConfig { internal static readonly Dictionary weaponList = new() @@ -156,22 +156,34 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig - { - for (int i = 1; i <= Server.MaxPlayers; i++) - { - if (Config.Additional.SkinEnabled && weaponSync != null) - await weaponSync.GetWeaponPaintsFromDatabase(i); - if (Config.Additional.KnifeEnabled && weaponSync != null) - await weaponSync.GetKnifeFromDatabase(i); - } - }); + List players = Utilities.GetPlayers(); + + foreach (CCSPlayerController player in players) + { + if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || player.AuthorizedSteamID == null) continue; + if (gPlayerWeaponsInfo.ContainsKey((int)player.Index)) continue; + + PlayerInfo playerInfo = new PlayerInfo + { + UserId = player.UserId, + Index = (int)player.Index, + SteamId = player?.AuthorizedSteamID?.SteamId64.ToString(), + Name = player?.PlayerName, + IpAddress = player?.IpAddress?.Split(":")[0] + }; + + if (Config.Additional.SkinEnabled && weaponSync != null) + _ = weaponSync.GetWeaponPaintsFromDatabase(playerInfo); + if (Config.Additional.KnifeEnabled && weaponSync != null) + _ = weaponSync.GetKnifeFromDatabase(playerInfo); + } + + RegisterListeners(); + RegisterCommands(); } if (Config.Additional.KnifeEnabled) @@ -237,6 +249,7 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig - + diff --git a/WeaponSynchronization.cs b/WeaponSynchronization.cs index 1e61389b..0192cb31 100644 --- a/WeaponSynchronization.cs +++ b/WeaponSynchronization.cs @@ -21,24 +21,21 @@ namespace WeaponPaints _globalShareServerId = globalShareServerId; } - internal async Task GetKnifeFromDatabase(int playerIndex) + internal async Task GetKnifeFromDatabase(PlayerInfo player) { if (!_config.Additional.KnifeEnabled) return; + if (player.SteamId == null || player.Index == 0) return; try { - CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); - if (!Utility.IsPlayerValid(player)) return; - if (player.AuthorizedSteamID == null) return; - string steamId = player.AuthorizedSteamID.SteamId64.ToString(); - if (_config.GlobalShare) { var values = new Dictionary { { "server_id", _globalShareServerId.ToString() }, - { "steamid", steamId }, + { "steamid", player.SteamId }, { "knife", "1" } }; + UriBuilder builder = new UriBuilder(_globalShareApi); builder.Query = string.Join("&", values.Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}")); @@ -53,7 +50,7 @@ namespace WeaponPaints string result = await response.Content.ReadAsStringAsync(); if (!string.IsNullOrEmpty(result)) { - WeaponPaints.g_playersKnife[playerIndex] = result; + WeaponPaints.g_playersKnife[player.Index] = result; } else { @@ -72,10 +69,11 @@ namespace WeaponPaints { await connection.OpenAsync(); string query = "SELECT `knife` FROM `wp_player_knife` WHERE `steamid` = @steamid"; - string? PlayerKnife = await connection.QueryFirstOrDefaultAsync(query, new { steamid = steamId }); + string? PlayerKnife = await connection.QueryFirstOrDefaultAsync(query, new { steamid = player.SteamId }); + if (PlayerKnife != null) { - WeaponPaints.g_playersKnife[playerIndex] = PlayerKnife; + WeaponPaints.g_playersKnife[player.Index] = PlayerKnife; } else { @@ -91,20 +89,14 @@ namespace WeaponPaints } } - internal async Task GetWeaponPaintsFromDatabase(int playerIndex) + internal async Task GetWeaponPaintsFromDatabase(PlayerInfo player) { if (!_config.Additional.SkinEnabled) return; + if (player.SteamId == null || player.Index == 0) return; - CCSPlayerController player = Utilities.GetPlayerFromIndex(playerIndex); - if (!Utility.IsPlayerValid(player)) return; - - if (player.AuthorizedSteamID == null) return; - - string steamId = player.AuthorizedSteamID.SteamId64.ToString(); - - if (!WeaponPaints.gPlayerWeaponsInfo.TryGetValue(playerIndex, out _)) + if (!WeaponPaints.gPlayerWeaponsInfo.TryGetValue(player.Index, out _)) { - WeaponPaints.gPlayerWeaponsInfo[playerIndex] = new Dictionary(); + WeaponPaints.gPlayerWeaponsInfo[player.Index] = new Dictionary(); } try { @@ -113,7 +105,7 @@ namespace WeaponPaints var values = new Dictionary { { "server_id", _globalShareServerId.ToString() }, - { "steamid", steamId }, + { "steamid", player.SteamId }, { "skins", "1" } }; UriBuilder builder = new UriBuilder(_globalShareApi); @@ -146,7 +138,7 @@ namespace WeaponPaints Seed = weaponSeed.Value, Wear = weaponWear.Value }; - WeaponPaints.gPlayerWeaponsInfo[playerIndex][weaponDefIndex.Value] = weaponInfo; + WeaponPaints.gPlayerWeaponsInfo[player.Index][weaponDefIndex.Value] = weaponInfo; } } } @@ -164,7 +156,7 @@ namespace WeaponPaints await connection.OpenAsync(); string query = "SELECT * FROM `wp_player_skins` WHERE `steamid` = @steamid"; - IEnumerable PlayerSkins = await connection.QueryAsync(query, new { steamid = steamId }); + IEnumerable PlayerSkins = await connection.QueryAsync(query, new { steamid = player.SteamId }); if (PlayerSkins != null && PlayerSkins.AsList().Count > 0) { @@ -181,7 +173,7 @@ namespace WeaponPaints Seed = weaponSeed, Wear = weaponWear }; - WeaponPaints.gPlayerWeaponsInfo[playerIndex][weaponDefIndex] = weaponInfo; + WeaponPaints.gPlayerWeaponsInfo[player.Index][weaponDefIndex] = weaponInfo; }); } else From e37f111f1be11b1640c360e90b5286cfc8518076 Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Mon, 4 Dec 2023 21:01:36 +0100 Subject: [PATCH 21/47] Update WeaponPaints.csproj --- WeaponPaints.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WeaponPaints.csproj b/WeaponPaints.csproj index e3da6939..76a279cf 100644 --- a/WeaponPaints.csproj +++ b/WeaponPaints.csproj @@ -8,7 +8,7 @@ - + From b0790729be2df3f9e152cac0128040b50a178fb2 Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Tue, 5 Dec 2023 19:30:10 +0100 Subject: [PATCH 22/47] Small fixes --- Commands.cs | 13 ++++++++++++- Events.cs | 2 ++ WeaponAction.cs | 5 ++++- WeaponPaints.cs | 6 +++++- WeaponSynchronization.cs | 9 +++------ 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/Commands.cs b/Commands.cs index c235706d..82bd1248 100644 --- a/Commands.cs +++ b/Commands.cs @@ -109,6 +109,7 @@ namespace WeaponPaints { if (Utility.IsPlayerValid(player)) { + if (player == null) return; var knifeName = option.Text; var knifeKey = knivesOnly.FirstOrDefault(x => x.Value == knifeName).Key; if (!string.IsNullOrEmpty(knifeKey)) @@ -127,14 +128,24 @@ namespace WeaponPaints player!.PrintToChat(Utility.ReplaceTags(temp)); } + PlayerInfo playerInfo = new PlayerInfo + { + UserId = player.UserId, + Index = (int)player.Index, + SteamId = player?.AuthorizedSteamID?.SteamId64.ToString(), + Name = player?.PlayerName, + IpAddress = player?.IpAddress?.Split(":")[0] + }; + g_playersKnife[(int)player!.Index] = knifeKey; if (player!.PawnIsAlive && g_bCommandsAllowed) { RefreshWeapons(player); } + if (weaponSync != null) - Task.Run(() => weaponSync.SyncKnifeToDatabase((int)player.Index, knifeKey)); + Task.Run(async () => await weaponSync.SyncKnifeToDatabase(playerInfo, knifeKey)); } } }; diff --git a/Events.cs b/Events.cs index 7cdeb45b..b8836c9f 100644 --- a/Events.cs +++ b/Events.cs @@ -105,6 +105,7 @@ namespace WeaponPaints RemovePlayerKnife(player, true); AddTimer(0.3f, () => GiveKnifeToPlayer(player)); + } } return HookResult.Continue; @@ -207,6 +208,7 @@ namespace WeaponPaints return HookResult.Continue; } + private HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info) { g_bCommandsAllowed = false; diff --git a/WeaponAction.cs b/WeaponAction.cs index ed33bda6..ccecddf4 100644 --- a/WeaponAction.cs +++ b/WeaponAction.cs @@ -2,6 +2,7 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Utils; +using Microsoft.Extensions.Logging; namespace WeaponPaints { @@ -81,7 +82,7 @@ namespace WeaponPaints { if (!_config.Additional.KnifeEnabled) return false; - if (player == null || !player.IsValid || !player.PlayerPawn.IsValid) + if (player == null || !player.IsValid || player.PlayerPawn == null || !player.PlayerPawn.IsValid || !player.PawnIsAlive) { return false; } @@ -221,6 +222,7 @@ namespace WeaponPaints } catch (Exception ex) { + Logger.LogWarning("Refreshing weapons exception"); Console.WriteLine("[WeaponPaints] Refreshing weapons exception"); Console.WriteLine(ex.Message); } @@ -240,6 +242,7 @@ namespace WeaponPaints if (weapons != null && weapons.Count > 0) { CCSPlayer_ItemServices service = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle); + //var dropWeapon = VirtualFunction.CreateVoid(service.Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon")); foreach (var weapon in weapons) { diff --git a/WeaponPaints.cs b/WeaponPaints.cs index 9333e98a..08158eba 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -3,6 +3,7 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core.Attributes; using CounterStrikeSharp.API.Modules.Cvars; using Newtonsoft.Json.Linq; +using Microsoft.Extensions.Logging; namespace WeaponPaints; @@ -142,7 +143,7 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig "Nereziel & daffyy"; public override string ModuleDescription => "Skin and knife selector, standalone and web-based"; public override string ModuleName => "WeaponPaints"; - public override string ModuleVersion => "1.3c"; + public override string ModuleVersion => "1.3d"; public static WeaponPaintsConfig GetWeaponPaintsConfig() { return _config; @@ -203,6 +204,7 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig Date: Wed, 6 Dec 2023 14:47:26 +0100 Subject: [PATCH 23/47] !skins thread-safe and checking version --- Commands.cs | 20 ++++++++++++++------ Events.cs | 3 +-- Utility.cs | 40 ++++++++++++++++++++++++++++++++++++++++ VERSION | 1 + WeaponPaints.cs | 6 ++++-- WeaponSynchronization.cs | 14 +++++--------- 6 files changed, 65 insertions(+), 19 deletions(-) create mode 100644 VERSION diff --git a/Commands.cs b/Commands.cs index 82bd1248..2760fd1a 100644 --- a/Commands.cs +++ b/Commands.cs @@ -1,5 +1,7 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Commands; +using CounterStrikeSharp.API.Modules.Entities.Constants; +using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Menu; namespace WeaponPaints @@ -231,16 +233,22 @@ namespace WeaponPaints } gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Paint = paintID; - gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Wear = 0.0f; + gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Wear = 0.01f; gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Seed = 0; + PlayerInfo playerInfo = new PlayerInfo + { + UserId = player.UserId, + Index = (int)player.Index, + SteamId = player?.AuthorizedSteamID?.SteamId64.ToString(), + Name = player?.PlayerName, + IpAddress = player?.IpAddress?.Split(":")[0] + }; + if (!Config.GlobalShare) { - if (weaponSync == null) return; - Task.Run(async () => - { - await weaponSync.SyncWeaponPaintsToDatabase(p); - }); + if (weaponSync != null) + Task.Run(async () => await weaponSync.SyncWeaponPaintsToDatabase(playerInfo)); } } }; diff --git a/Events.cs b/Events.cs index b8836c9f..c863f0b8 100644 --- a/Events.cs +++ b/Events.cs @@ -95,7 +95,7 @@ namespace WeaponPaints if (@event.Defindex == 42 || @event.Defindex == 59) { CCSPlayerController? player = @event.Userid; - if (!Utility.IsPlayerValid(player) || !player.PawnIsAlive || g_knifePickupCount[(int)player.Index] >= 2) return HookResult.Continue; + if (!Utility.IsPlayerValid(player) || !player.PawnIsAlive || g_knifePickupCount[(int)player.Index] >= 1) return HookResult.Continue; if (g_playersKnife.ContainsKey((int)player.Index) && @@ -105,7 +105,6 @@ namespace WeaponPaints RemovePlayerKnife(player, true); AddTimer(0.3f, () => GiveKnifeToPlayer(player)); - } } return HookResult.Continue; diff --git a/Utility.cs b/Utility.cs index 6e7876da..7bf4318b 100644 --- a/Utility.cs +++ b/Utility.cs @@ -5,6 +5,7 @@ using MySqlConnector; using Newtonsoft.Json.Linq; using Newtonsoft.Json; using System.Reflection; +using Microsoft.Extensions.Logging; namespace WeaponPaints { @@ -107,6 +108,45 @@ namespace WeaponPaints return message; } + internal static async Task CheckVersion(string version) + { + using (HttpClient client = new HttpClient()) + { + try + { + HttpResponseMessage response = await client.GetAsync("https://github.com/Nereziel/cs2-WeaponPaints/blob/main/VERSION"); + + if (response.IsSuccessStatusCode) + { + string remoteVersion = await response.Content.ReadAsStringAsync(); + remoteVersion = remoteVersion.Trim(); + + int comparisonResult = string.Compare(version, remoteVersion); + + if (comparisonResult < 0) + { + WeaponPaints.logger!.LogWarning("Plugin is outdated! Check https://github.com/Nereziel/cs2-WeaponPaints"); + } + else if (comparisonResult > 0) + { + WeaponPaints.logger!.LogInformation("Probably dev version detected"); + } + else + { + WeaponPaints.logger!.LogInformation("Plugin is up to date"); + } + } + else + { + WeaponPaints.logger!.LogWarning("Failed to check version"); + } + } + catch (Exception) + { + } + } + } + internal static void ShowAd(string moduleVersion) { Console.WriteLine(" "); diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..c95270d1 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.3f \ No newline at end of file diff --git a/WeaponPaints.cs b/WeaponPaints.cs index 08158eba..ef4158ad 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -10,6 +10,7 @@ namespace WeaponPaints; [MinimumApiVersion(101)] public partial class WeaponPaints : BasePlugin, IPluginConfig { + internal static ILogger? logger; internal static readonly Dictionary weaponList = new() { {"weapon_deagle", "Desert Eagle"}, @@ -77,7 +78,7 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig g_changedKnife = new(); internal bool g_bCommandsAllowed = true; - internal Uri GlobalShareApi = new Uri("https://weaponpaints.fun/api.php"); + internal Uri GlobalShareApi = new("https://weaponpaints.fun/api.php"); internal int GlobalShareServerId = 0; private DateTime[] commandCooldown = new DateTime[Server.MaxPlayers]; private string DatabaseConnectionString = string.Empty; @@ -143,7 +144,7 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig "Nereziel & daffyy"; public override string ModuleDescription => "Skin and knife selector, standalone and web-based"; public override string ModuleName => "WeaponPaints"; - public override string ModuleVersion => "1.3d"; + public override string ModuleVersion => "1.3f"; public static WeaponPaintsConfig GetWeaponPaintsConfig() { return _config; @@ -213,6 +214,7 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig await Utility.CheckVersion(ModuleVersion)); } public override void Unload(bool hotReload) diff --git a/WeaponSynchronization.cs b/WeaponSynchronization.cs index 4589d69d..c7b59022 100644 --- a/WeaponSynchronization.cs +++ b/WeaponSynchronization.cs @@ -209,21 +209,17 @@ namespace WeaponPaints return; } } - internal async Task SyncWeaponPaintsToDatabase(CCSPlayerController? player) + internal async Task SyncWeaponPaintsToDatabase(PlayerInfo player) { - if (player == null || !Utility.IsPlayerValid(player)) return; - - int playerIndex = (int)player.Index; - if (player.AuthorizedSteamID == null) return; - string steamId = player.AuthorizedSteamID.SteamId64.ToString(); + if (player == null || player.Index <= 0 || player.SteamId == null) return; using var connection = new MySqlConnection(_databaseConnectionString); await connection.OpenAsync(); - if (!WeaponPaints.gPlayerWeaponsInfo.ContainsKey(playerIndex)) + if (!WeaponPaints.gPlayerWeaponsInfo.ContainsKey(player.Index)) return; - foreach (var weaponInfoPair in WeaponPaints.gPlayerWeaponsInfo[playerIndex]) + foreach (var weaponInfoPair in WeaponPaints.gPlayerWeaponsInfo[player.Index]) { int weaponDefIndex = weaponInfoPair.Key; WeaponInfo weaponInfo = weaponInfoPair.Value; @@ -236,7 +232,7 @@ namespace WeaponPaints "`weapon_wear` = @wear, `weapon_seed` = @seed WHERE `steamid` = @steamid " + "AND `weapon_defindex` = @weaponDefIndex"; - var updateParams = new { paintId, wear, seed, steamid = steamId, weaponDefIndex }; + var updateParams = new { paintId, wear, seed, steamid = player.SteamId, weaponDefIndex }; int rowsAffected = await connection.ExecuteAsync(updateSql, updateParams); if (rowsAffected == 0) From d0ed0f4c0b827b6cb77cf916b44723a0986ef31a Mon Sep 17 00:00:00 2001 From: Nilsu Derinder <44870116+exababy@users.noreply.github.com> Date: Wed, 6 Dec 2023 19:42:18 +0300 Subject: [PATCH 24/47] Wear Support --- website/index.php | 61 ++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/website/index.php b/website/index.php index ea6f21fe..22021e46 100644 --- a/website/index.php +++ b/website/index.php @@ -4,36 +4,36 @@ require_once 'class/database.php'; require_once 'steamauth/steamauth.php'; require_once 'class/utils.php'; - $db = new DataBase(); if (isset($_SESSION['steamid'])) { - $steamid = $_SESSION['steamid']; - $weapons = UtilsClass::getWeaponsFromArray(); - $skins = UtilsClass::skinsFromJson(); - $querySelected = $query3 = $db->select("SELECT `weapon_defindex`, `weapon_paint_id` FROM `wp_player_skins` WHERE `wp_player_skins`.`steamid` = :steamid", ["steamid" => $steamid]); - $selectedSkins = UtilsClass::getSelectedSkins($querySelected); - $selectedKnife = $db->select("SELECT * FROM `wp_player_knife` WHERE `wp_player_knife`.`steamid` = :steamid", ["steamid" => $steamid])[0]; - $knifes = UtilsClass::getKnifeTypes(); + include('steamauth/userInfo.php'); + $steamid = $steamprofile['steamid']; - if (isset($_POST['forma'])) { - $ex = explode("-", $_POST['forma']); + $weapons = UtilsClass::getWeaponsFromArray(); + $skins = UtilsClass::skinsFromJson(); + $querySelected = $query3 = $db->select("SELECT `weapon_defindex`, `weapon_paint_id`, `weapon_wear` FROM `wp_player_skins` WHERE `wp_player_skins`.`steamid` = :steamid", ["steamid" => $steamid]); + $selectedSkins = UtilsClass::getSelectedSkins($querySelected); + $selectedKnife = $db->select("SELECT * FROM `wp_player_knife` WHERE `wp_player_knife`.`steamid` = :steamid", ["steamid" => $steamid])[0]; + $knifes = UtilsClass::getKnifeTypes(); - if ($ex[0] == "knife") { - $db->query("INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(:steamid, :knife) ON DUPLICATE KEY UPDATE `knife` = :knife", ["steamid" => $steamid, "knife" => $knifes[$ex[1]]['weapon_name']]); - } else { - if (!is_int($ex[1])) - header("Location: index.php"); - if (array_key_exists($ex[1], $skins[$ex[0]])) { - if (array_key_exists($ex[0], $selectedSkins)) { - $db->query("UPDATE wp_player_skins SET weapon_paint_id = :weapon_paint_id WHERE steamid = :steamid AND weapon_defindex = :weapon_defindex", ["steamid" => $steamid, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1]]); - } else { - $db->query("INSERT INTO wp_player_skins (`steamid`, `weapon_defindex`, `weapon_paint_id`) VALUES (:steamid, :weapon_defindex, :weapon_paint_id)", ["steamid" => $steamid, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1]]); - } - } - } - header("Location: index.php"); - } + if (isset($_POST['forma'])) { + $ex = explode("-", $_POST['forma']); + + if ($ex[0] == "knife") { + $db->query("INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(:steamid, :knife) ON DUPLICATE KEY UPDATE `knife` = :knife", ["steamid" => $steamid, "knife" => $knifes[$ex[1]]['weapon_name']]); + } else { + if (array_key_exists($ex[1], $skins[$ex[0]]) && isset($_POST['wear']) && $_POST['wear'] >= 0.00 && $_POST['wear'] <= 1.00) { + $wear = floatval($_POST['wear']); + if (array_key_exists($ex[0], $selectedSkins)) { + $db->query("UPDATE wp_player_skins SET weapon_paint_id = :weapon_paint_id, weapon_wear = :weapon_wear WHERE steamid = :steamid AND weapon_defindex = :weapon_defindex", ["steamid" => $steamid, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1], "weapon_wear" => $wear]); + } else { + $db->query("INSERT INTO wp_player_skins (`steamid`, `weapon_defindex`, `weapon_paint_id`, `weapon_wear`) VALUES (:steamid, :weapon_defindex, :weapon_paint_id, :weapon_wear)", ["steamid" => $steamid, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1], "weapon_wear" => $wear]); + } + } + } + header("Location: index.php"); + } } ?> @@ -47,6 +47,12 @@ if (isset($_SESSION['steamid'])) { + CS2 Simple Weapon Paints @@ -132,6 +138,11 @@ if (isset($_SESSION['steamid'])) { } ?> +
+ + + 0.00 +
From 72c5df53b598dd1161cbd51608c4402b8869c689 Mon Sep 17 00:00:00 2001 From: Nilsu Derinder <44870116+exababy@users.noreply.github.com> Date: Wed, 6 Dec 2023 20:50:39 +0300 Subject: [PATCH 25/47] fix --- website/index.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/website/index.php b/website/index.php index 22021e46..caf2d046 100644 --- a/website/index.php +++ b/website/index.php @@ -47,12 +47,6 @@ if (isset($_SESSION['steamid'])) { - CS2 Simple Weapon Paints @@ -147,6 +141,12 @@ if (isset($_SESSION['steamid'])) { + From ee770fd8c257ed0f1b671647511bfd1e1ca4bd9e Mon Sep 17 00:00:00 2001 From: Nilsu Derinder <44870116+exababy@users.noreply.github.com> Date: Wed, 6 Dec 2023 22:03:52 +0300 Subject: [PATCH 26/47] Seed support --- website/index.php | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/website/index.php b/website/index.php index caf2d046..c5065839 100644 --- a/website/index.php +++ b/website/index.php @@ -23,14 +23,15 @@ if (isset($_SESSION['steamid'])) { if ($ex[0] == "knife") { $db->query("INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(:steamid, :knife) ON DUPLICATE KEY UPDATE `knife` = :knife", ["steamid" => $steamid, "knife" => $knifes[$ex[1]]['weapon_name']]); } else { - if (array_key_exists($ex[1], $skins[$ex[0]]) && isset($_POST['wear']) && $_POST['wear'] >= 0.00 && $_POST['wear'] <= 1.00) { - $wear = floatval($_POST['wear']); - if (array_key_exists($ex[0], $selectedSkins)) { - $db->query("UPDATE wp_player_skins SET weapon_paint_id = :weapon_paint_id, weapon_wear = :weapon_wear WHERE steamid = :steamid AND weapon_defindex = :weapon_defindex", ["steamid" => $steamid, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1], "weapon_wear" => $wear]); - } else { - $db->query("INSERT INTO wp_player_skins (`steamid`, `weapon_defindex`, `weapon_paint_id`, `weapon_wear`) VALUES (:steamid, :weapon_defindex, :weapon_paint_id, :weapon_wear)", ["steamid" => $steamid, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1], "weapon_wear" => $wear]); - } - } + if (array_key_exists($ex[1], $skins[$ex[0]]) && isset($_POST['wear']) && $_POST['wear'] >= 0.00 && $_POST['wear'] <= 1.00 && isset($_POST['seed'])) { + $wear = floatval($_POST['wear']); // wear + $seed = intval($_POST['seed']); // seed + if (array_key_exists($ex[0], $selectedSkins)) { + $db->query("UPDATE wp_player_skins SET weapon_paint_id = :weapon_paint_id, weapon_wear = :weapon_wear, weapon_seed = :weapon_seed WHERE steamid = :steamid AND weapon_defindex = :weapon_defindex", ["steamid" => $steamid, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1], "weapon_wear" => $wear, "weapon_seed" => $seed]); + } else { + $db->query("INSERT INTO wp_player_skins (`steamid`, `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed`) VALUES (:steamid, :weapon_defindex, :weapon_paint_id, :weapon_wear, :weapon_seed)", ["steamid" => $steamid, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1], "weapon_wear" => $wear, "weapon_seed" => $seed]); + } + } } header("Location: index.php"); } @@ -132,11 +133,22 @@ if (isset($_SESSION['steamid'])) { } ?> -
- - - 0.00 -
+
+
+
+ + + 0.00 +
+
+
+
+ + + 0 +
+
+
@@ -146,6 +158,10 @@ if (isset($_SESSION['steamid'])) { document.getElementById('wear').addEventListener('input', function () { document.getElementById('wearValue').innerText = this.value; }); + // seed + document.getElementById('seed').addEventListener('input', function () { + document.getElementById('seedValue').innerText = this.value; + }); From 3ddbf7e11e1fa85e9549bf44b0dd9d402d04bd27 Mon Sep 17 00:00:00 2001 From: Nilsu Derinder <44870116+exababy@users.noreply.github.com> Date: Wed, 6 Dec 2023 22:25:19 +0300 Subject: [PATCH 27/47] Advanced wear --- website/index.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/website/index.php b/website/index.php index c5065839..a82369fc 100644 --- a/website/index.php +++ b/website/index.php @@ -133,6 +133,16 @@ if (isset($_SESSION['steamid'])) { } ?> +

+
+ +
@@ -154,6 +164,10 @@ if (isset($_SESSION['steamid'])) {
From 78649f5dcfac58d26a48b35bfd6520a7717d6402 Mon Sep 17 00:00:00 2001 From: Nilsu Derinder <44870116+exababy@users.noreply.github.com> Date: Mon, 11 Dec 2023 11:32:40 +0300 Subject: [PATCH 29/47] Update wear has been updated and settings have been moved into modal and added settings button --- website/index.php | 105 ++++++++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 40 deletions(-) diff --git a/website/index.php b/website/index.php index c5a39bbf..b3555644 100644 --- a/website/index.php +++ b/website/index.php @@ -133,46 +133,71 @@ if (isset($_SESSION['steamid'])) { } ?> -

-
- -
-
-
-
- - - 0.00 -
-
-
-
- - -
-
-
- -
- - +

+ + + + + From 928c1e1466aa43e23e97b46f5c582bb055321f8f Mon Sep 17 00:00:00 2001 From: Nilsu Derinder <44870116+exababy@users.noreply.github.com> Date: Mon, 11 Dec 2023 18:46:34 +0300 Subject: [PATCH 30/47] bootstrap update --- website/index.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/index.php b/website/index.php index b3555644..dbe40344 100644 --- a/website/index.php +++ b/website/index.php @@ -48,6 +48,8 @@ if (isset($_SESSION['steamid'])) { + + CS2 Simple Weapon Paints From fc42190701712fe374fc03a7bfb5df6d10ecc26d Mon Sep 17 00:00:00 2001 From: daffyyyy Date: Tue, 12 Dec 2023 20:09:39 +0100 Subject: [PATCH 31/47] CSS 121, languages and more --- Commands.cs | 60 ++++++++++++++----------------------- Config.cs | 31 ++----------------- Events.cs | 65 +++++++++++++++++++++++++++++++++------- PlayerExtensions.cs | 14 +++++++++ PluginServices.cs | 1 + Utility.cs | 15 +++++----- WeaponAction.cs | 9 ++++-- WeaponPaints.cs | 17 +++++++---- WeaponPaints.csproj | 7 ++++- WeaponSynchronization.cs | 4 +-- lang/en.json | 14 +++++++++ lang/pl.json | 14 +++++++++ lang/ru.json | 14 +++++++++ 13 files changed, 169 insertions(+), 96 deletions(-) create mode 100644 PlayerExtensions.cs create mode 100644 PluginServices.cs create mode 100644 lang/en.json create mode 100644 lang/pl.json create mode 100644 lang/ru.json diff --git a/Commands.cs b/Commands.cs index 2760fd1a..9a8d55d0 100644 --- a/Commands.cs +++ b/Commands.cs @@ -1,7 +1,5 @@ using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Modules.Commands; -using CounterStrikeSharp.API.Modules.Entities.Constants; -using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Menu; namespace WeaponPaints @@ -12,7 +10,6 @@ namespace WeaponPaints { if (!Config.Additional.CommandWpEnabled || !Config.Additional.SkinEnabled || !g_bCommandsAllowed) return; if (!Utility.IsPlayerValid(player)) return; - string temp = ""; if (player == null || player.Index <= 0) return; int playerIndex = (int)player!.Index; @@ -37,17 +34,15 @@ namespace WeaponPaints RefreshWeapons(player); } - if (!string.IsNullOrEmpty(Config.Messages.SuccessRefreshCommand)) + if (!string.IsNullOrEmpty(Localizer["wp_command_refresh_done"])) { - temp = $" {Config.Prefix} {Config.Messages.SuccessRefreshCommand}"; - player!.PrintToChat(Utility.ReplaceTags(temp)); + player!.Print(Localizer["wp_command_refresh_done"]); } return; } - if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) + if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"])) { - temp = $" {Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; - player!.PrintToChat(Utility.ReplaceTags(temp)); + player!.Print(Localizer["wp_command_cooldown"]); } } @@ -56,22 +51,18 @@ namespace WeaponPaints if (!Config.Additional.SkinEnabled) return; if (!Utility.IsPlayerValid(player)) return; - string temp; - if (!string.IsNullOrEmpty(Config.Messages.WebsiteMessageCommand)) + if (!string.IsNullOrEmpty(Localizer["wp_info_website"])) { - temp = $" {Config.Prefix} {Config.Messages.WebsiteMessageCommand}"; - player!.PrintToChat(Utility.ReplaceTags(temp)); + player!.Print(Localizer["wp_info_website", Config.Website]); } - if (!string.IsNullOrEmpty(Config.Messages.SynchronizeMessageCommand)) + if (!string.IsNullOrEmpty(Localizer["wp_info_refresh"])) { - temp = $" {Config.Prefix} {Config.Messages.SynchronizeMessageCommand}"; - player!.PrintToChat(Utility.ReplaceTags(temp)); + player!.Print(Localizer["wp_info_refresh"]); } if (!Config.Additional.KnifeEnabled) return; - if (!string.IsNullOrEmpty(Config.Messages.KnifeMessageCommand)) + if (!string.IsNullOrEmpty(Localizer["wp_info_knife"])) { - temp = $" {Config.Prefix} {Config.Messages.KnifeMessageCommand}"; - player!.PrintToChat(Utility.ReplaceTags(temp)); + player!.Print(Localizer["wp_info_knife"]); } } @@ -106,7 +97,7 @@ namespace WeaponPaints .Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")) .ToDictionary(pair => pair.Key, pair => pair.Value); - var giveItemMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.KnifeMenuTitle}")); + var giveItemMenu = new ChatMenu(Localizer["wp_knife_menu_title"]); var handleGive = (CCSPlayerController? player, ChatMenuOption option) => { if (Utility.IsPlayerValid(player)) @@ -116,18 +107,14 @@ namespace WeaponPaints var knifeKey = knivesOnly.FirstOrDefault(x => x.Value == knifeName).Key; if (!string.IsNullOrEmpty(knifeKey)) { - string temp = ""; - - if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenu)) + if (!string.IsNullOrEmpty(Localizer["wp_knife_menu_select"])) { - temp = $" {Config.Prefix} {Config.Messages.ChosenKnifeMenu}".Replace("{KNIFE}", knifeName); - player!.PrintToChat(Utility.ReplaceTags(temp)); + player!.Print(Localizer["wp_knife_menu_select", knifeName]); } - if (!string.IsNullOrEmpty(Config.Messages.ChosenKnifeMenuKill) && Config.Additional.CommandKillEnabled) + if (!string.IsNullOrEmpty(Localizer["wp_knife_menu_kill"]) && Config.Additional.CommandKillEnabled) { - temp = $" {Config.Prefix} {Config.Messages.ChosenKnifeMenuKill}"; - player!.PrintToChat(Utility.ReplaceTags(temp)); + player!.Print(Localizer["wp_knife_menu_kill"]); } PlayerInfo playerInfo = new PlayerInfo @@ -166,10 +153,9 @@ namespace WeaponPaints ChatMenus.OpenMenu(player, giveItemMenu); return; } - if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) + if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"])) { - string temp = $" {Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; - player.PrintToChat(Utility.ReplaceTags(temp)); + player!.Print(Localizer["wp_command_cooldown"]); } }); } @@ -177,7 +163,7 @@ namespace WeaponPaints private void SetupSkinsMenu() { var classNamesByWeapon = weaponList.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); - var weaponSelectionMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.WeaponMenuTitle}")); + var weaponSelectionMenu = new ChatMenu(Localizer["wp_skin_menu_weapon_title"]); // Function to handle skin selection for a specific weapon var handleWeaponSelection = (CCSPlayerController? player, ChatMenuOption option) => @@ -195,7 +181,7 @@ namespace WeaponPaints weaponName?.ToString() == selectedWeaponClassname )?.ToList(); - var skinSubMenu = new ChatMenu(Utility.ReplaceTags($" {Config.Messages.SkinMenuTitle}").Replace("{WEAPON}", selectedWeapon)); + var skinSubMenu = new ChatMenu(Localizer["wp_skin_menu_skin_title", selectedWeapon]); // Function to handle skin selection for the chosen weapon var handleSkinSelection = (CCSPlayerController? p, ChatMenuOption opt) => @@ -224,8 +210,7 @@ namespace WeaponPaints int.TryParse(weaponDefIndexObj.ToString(), out var weaponDefIndex) && int.TryParse(selectedPaintID, out var paintID)) { - string temp = $" {Config.Prefix} {Config.Messages.ChosenSkinMenu}".Replace("{SKIN}", selectedSkin); - p.PrintToChat(Utility.ReplaceTags(temp)); + p!.Print(Localizer["f", selectedSkin]); if (!gPlayerWeaponsInfo[playerIndex].ContainsKey(weaponDefIndex)) { @@ -294,10 +279,9 @@ namespace WeaponPaints ChatMenus.OpenMenu(player, weaponSelectionMenu); return; } - if (!string.IsNullOrEmpty(Config.Messages.CooldownRefreshCommand)) + if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"])) { - string temp = $"{Config.Prefix} {Config.Messages.CooldownRefreshCommand}"; - player.PrintToChat(Utility.ReplaceTags(temp)); + player!.Print(Localizer["wp_command_cooldown"]); } }); } diff --git a/Config.cs b/Config.cs index 042f51e9..2795400c 100644 --- a/Config.cs +++ b/Config.cs @@ -3,32 +3,6 @@ using System.Text.Json.Serialization; namespace WeaponPaints { - public class Messages - { - [JsonPropertyName("WebsiteMessageCommand")] - public string WebsiteMessageCommand { get; set; } = "Visit {WEBSITE} where you can change skins."; - [JsonPropertyName("SynchronizeMessageCommand")] - public string SynchronizeMessageCommand { get; set; } = "Type !wp to synchronize chosen skins."; - [JsonPropertyName("KnifeMessageCommand")] - public string KnifeMessageCommand { get; set; } = "Type !knife to open knife menu."; - [JsonPropertyName("CooldownRefreshCommand")] - public string CooldownRefreshCommand { get; set; } = "You can't refresh weapon paints right now."; - [JsonPropertyName("SuccessRefreshCommand")] - public string SuccessRefreshCommand { get; set; } = "Refreshing weapon paints."; - [JsonPropertyName("ChosenKnifeMenu")] - public string ChosenKnifeMenu { get; set; } = "You have chosen {KNIFE} as your knife."; - [JsonPropertyName("ChosenSkinMenu")] - public string ChosenSkinMenu { get; set; } = "You have chosen {SKIN} as your skin."; - [JsonPropertyName("ChosenKnifeMenuKill")] - public string ChosenKnifeMenuKill { get; set; } = "To correctly apply skin for knife, you need to type !kill."; - [JsonPropertyName("KnifeMenuTitle")] - public string KnifeMenuTitle { get; set; } = "Knife Menu."; - [JsonPropertyName("WeaponMenuTitle")] - public string WeaponMenuTitle { get; set; } = "Weapon Menu."; - [JsonPropertyName("SkinMenuTitle")] - public string SkinMenuTitle { get; set; } = "Select skin for {WEAPON}"; - } - public class Additional { [JsonPropertyName("SkinVisibilityFix")] @@ -66,6 +40,8 @@ namespace WeaponPaints [JsonPropertyName("GiveRandomSkin")] public bool GiveRandomSkin { get; set; } = false; + [JsonPropertyName("GiveKnifeAfterRemove")] + public bool GiveKnifeAfterRemove { get; set; } = false; } public class WeaponPaintsConfig : BasePluginConfig @@ -99,9 +75,6 @@ namespace WeaponPaints [JsonPropertyName("Website")] public string Website { get; set; } = "example.com/skins"; - [JsonPropertyName("Messages")] - public Messages Messages { get; set; } = new Messages(); - [JsonPropertyName("Additional")] public Additional Additional { get; set; } = new Additional(); } diff --git a/Events.cs b/Events.cs index c863f0b8..c6707ec9 100644 --- a/Events.cs +++ b/Events.cs @@ -45,7 +45,7 @@ namespace WeaponPaints gPlayerWeaponsInfo.Remove((int)player.Index); } - private void OnEntitySpawned(CEntityInstance entity) + private void OnEntityCreated(CEntityInstance entity) { if (!Config.Additional.SkinEnabled) return; var designerName = entity.DesignerName; @@ -57,6 +57,7 @@ namespace WeaponPaints { isKnife = true; } + Server.NextFrame(() => { try @@ -94,22 +95,65 @@ namespace WeaponPaints { if (@event.Defindex == 42 || @event.Defindex == 59) { + Server.PrintToChatAll("test1"); + CCSPlayerController? player = @event.Userid; - if (!Utility.IsPlayerValid(player) || !player.PawnIsAlive || g_knifePickupCount[(int)player.Index] >= 1) return HookResult.Continue; + if (player == null || !player.IsValid || !g_knifePickupCount.ContainsKey((int)player.Index) || player.IsBot || !g_playersKnife.ContainsKey((int)player.Index)) + return HookResult.Continue; + + Server.PrintToChatAll("test2"); + + + if (g_knifePickupCount[(int)player.Index] >= 2) return HookResult.Continue; + Server.PrintToChatAll("test3"); + if (g_playersKnife.ContainsKey((int)player.Index) && g_playersKnife[(int)player.Index] != "weapon_knife") { + Server.PrintToChatAll("usuwam noz"); g_knifePickupCount[(int)player.Index]++; RemovePlayerKnife(player, true); - AddTimer(0.3f, () => GiveKnifeToPlayer(player)); + + if (!PlayerHasKnife(player) && Config.Additional.GiveKnifeAfterRemove) + AddTimer(0.3f, () => GiveKnifeToPlayer(player)); } } return HookResult.Continue; } + public HookResult OnPickup(CEntityIOOutput output, string name, CEntityInstance activator, CEntityInstance caller, CVariant value, float delay) + { + CCSPlayerController? player = Utilities.GetEntityFromIndex((int)activator.Index).OriginalController.Value; + + if (player == null || player.IsBot || player.IsHLTV) + return HookResult.Continue; + + if (player == null || !player.IsValid || player.AuthorizedSteamID == null || + !g_knifePickupCount.ContainsKey((int)player.Index) || !g_playersKnife.ContainsKey((int)player.Index)) + return HookResult.Continue; + + CBasePlayerWeapon weapon = new(caller.Handle); + + if (weapon.AttributeManager.Item.ItemDefinitionIndex != 42 && weapon.AttributeManager.Item.ItemDefinitionIndex != 59) + return HookResult.Continue; + + if (g_knifePickupCount[(int)player.Index] >= 2) return HookResult.Continue; + + if (g_playersKnife[(int)player.Index] != "weapon_knife") + { + g_knifePickupCount[(int)player.Index]++; + weapon.Remove(); + if (!PlayerHasKnife(player) && Config.Additional.GiveKnifeAfterRemove) + AddTimer(0.2f, () => GiveKnifeToPlayer(player)); + } + + return HookResult.Continue; + } + + private void OnMapStart(string mapName) { if (!Config.Additional.KnifeEnabled) return; @@ -117,6 +161,7 @@ namespace WeaponPaints // needed for now AddTimer(2.0f, () => { + NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0"); @@ -133,7 +178,7 @@ namespace WeaponPaints foreach (CCSPlayerController player in players) { - if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || player.AuthorizedSteamID == null) continue; + if (player.IsBot || player.IsHLTV || player.AuthorizedSteamID == null) continue; if (gPlayerWeaponsInfo.ContainsKey((int)player.Index)) continue; PlayerInfo playerInfo = new PlayerInfo @@ -151,7 +196,6 @@ namespace WeaponPaints _ = weaponSync.GetKnifeFromDatabase(playerInfo); } }, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE | CounterStrikeSharp.API.Modules.Timers.TimerFlags.REPEAT); - } private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info) @@ -187,7 +231,7 @@ namespace WeaponPaints private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) { CCSPlayerController? player = @event.Userid; - if (player == null || !player.IsValid) + if (player == null || !player.IsValid || player.IsBot) { return HookResult.Continue; } @@ -195,8 +239,7 @@ namespace WeaponPaints if (Config.Additional.KnifeEnabled) { g_knifePickupCount[(int)player.Index] = 0; - if (!PlayerHasKnife(player)) - GiveKnifeToPlayer(player); + AddTimer(0.1f, () => GiveKnifeToPlayer(player)); } if (Config.Additional.SkinVisibilityFix) @@ -207,7 +250,6 @@ namespace WeaponPaints return HookResult.Continue; } - private HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info) { g_bCommandsAllowed = false; @@ -227,7 +269,7 @@ namespace WeaponPaints private void RegisterListeners() { - RegisterListener(OnEntitySpawned); + RegisterListener(OnEntityCreated); RegisterListener(OnClientAuthorized); RegisterListener(OnClientDisconnect); RegisterListener(OnMapStart); @@ -237,7 +279,8 @@ namespace WeaponPaints RegisterEventHandler(OnRoundStart, HookMode.Pre); RegisterEventHandler(OnRoundEnd); RegisterEventHandler(OnEventItemPurchasePost); - RegisterEventHandler(OnItemPickup); + //RegisterEventHandler(OnItemPickup); + HookEntityOutput("weapon_knife", "OnPlayerPickup", OnPickup, HookMode.Pre); } /* WORKAROUND FOR CLIENTS WITHOUT STEAMID ON AUTHORIZATION */ diff --git a/PlayerExtensions.cs b/PlayerExtensions.cs new file mode 100644 index 00000000..9fa41d0c --- /dev/null +++ b/PlayerExtensions.cs @@ -0,0 +1,14 @@ +using CounterStrikeSharp.API.Core; +using System.Text; + +namespace WeaponPaints; + +public static class PlayerExtensions +{ + public static void Print(this CCSPlayerController controller, string message) + { + StringBuilder _message = new(WeaponPaints._localizer["wp_prefix"]); + _message.Append(message); + controller.PrintToChat(_message.ToString()); + } +} \ No newline at end of file diff --git a/PluginServices.cs b/PluginServices.cs new file mode 100644 index 00000000..5f282702 --- /dev/null +++ b/PluginServices.cs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Utility.cs b/Utility.cs index 7bf4318b..f342fb0a 100644 --- a/Utility.cs +++ b/Utility.cs @@ -108,13 +108,13 @@ namespace WeaponPaints return message; } - internal static async Task CheckVersion(string version) + internal static async Task CheckVersion(string version, ILogger logger) { using (HttpClient client = new HttpClient()) { try { - HttpResponseMessage response = await client.GetAsync("https://github.com/Nereziel/cs2-WeaponPaints/blob/main/VERSION"); + HttpResponseMessage response = await client.GetAsync("https://raw.githubusercontent.com/Nereziel/cs2-WeaponPaints/main/VERSION"); if (response.IsSuccessStatusCode) { @@ -125,24 +125,25 @@ namespace WeaponPaints if (comparisonResult < 0) { - WeaponPaints.logger!.LogWarning("Plugin is outdated! Check https://github.com/Nereziel/cs2-WeaponPaints"); + logger.LogWarning("Plugin is outdated! Check https://github.com/Nereziel/cs2-WeaponPaints"); } else if (comparisonResult > 0) { - WeaponPaints.logger!.LogInformation("Probably dev version detected"); + logger.LogInformation("Probably dev version detected"); } else { - WeaponPaints.logger!.LogInformation("Plugin is up to date"); + logger.LogInformation("Plugin is up to date"); } } else { - WeaponPaints.logger!.LogWarning("Failed to check version"); + logger.LogWarning("Failed to check version"); } } - catch (Exception) + catch (Exception ex) { + Console.WriteLine(ex); } } } diff --git a/WeaponAction.cs b/WeaponAction.cs index ccecddf4..932140e4 100644 --- a/WeaponAction.cs +++ b/WeaponAction.cs @@ -20,6 +20,11 @@ namespace WeaponPaints int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex; + if (isKnife) + { + weapon.AttributeManager.Item.EntityQuality = 3; + } + if (_config.Additional.GiveRandomSkin && !gPlayerWeaponsInfo[playerIndex].ContainsKey(weaponDefIndex)) { @@ -280,10 +285,10 @@ namespace WeaponPaints } private static int GetRandomPaint(int defindex) { - Random rnd = new Random(); - if (WeaponPaints.skinsList != null) + if (skinsList != null) { + Random rnd = new Random(); // Filter weapons by the provided defindex var filteredWeapons = skinsList.FindAll(w => w["weapon_defindex"]?.ToString() == defindex.ToString()); diff --git a/WeaponPaints.cs b/WeaponPaints.cs index ef4158ad..9eebcf74 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -4,13 +4,13 @@ using CounterStrikeSharp.API.Core.Attributes; using CounterStrikeSharp.API.Modules.Cvars; using Newtonsoft.Json.Linq; using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Localization; namespace WeaponPaints; -[MinimumApiVersion(101)] +[MinimumApiVersion(121)] public partial class WeaponPaints : BasePlugin, IPluginConfig { - internal static ILogger? logger; internal static readonly Dictionary weaponList = new() { {"weapon_deagle", "Desert Eagle"}, @@ -70,6 +70,7 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig g_knifePickupCount = new Dictionary(); internal static Dictionary g_playersKnife = new(); internal static Dictionary> gPlayerWeaponsInfo = new Dictionary>(); @@ -144,7 +145,8 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig "Nereziel & daffyy"; public override string ModuleDescription => "Skin and knife selector, standalone and web-based"; public override string ModuleName => "WeaponPaints"; - public override string ModuleVersion => "1.3f"; + public override string ModuleVersion => "1.3g"; + public static WeaponPaintsConfig GetWeaponPaintsConfig() { return _config; @@ -182,10 +184,13 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig await Utility.CheckVersion(ModuleVersion)); + Task.Run(async () => await Utility.CheckVersion(ModuleVersion, Logger)); } public override void Unload(bool hotReload) diff --git a/WeaponPaints.csproj b/WeaponPaints.csproj index 76a279cf..b7fd0686 100644 --- a/WeaponPaints.csproj +++ b/WeaponPaints.csproj @@ -5,12 +5,17 @@ enable enable true + true - + + + + + diff --git a/WeaponSynchronization.cs b/WeaponSynchronization.cs index c7b59022..5742a654 100644 --- a/WeaponSynchronization.cs +++ b/WeaponSynchronization.cs @@ -1,6 +1,4 @@ -using CounterStrikeSharp.API; -using CounterStrikeSharp.API.Core; -using Dapper; +using Dapper; using MySqlConnector; using Newtonsoft.Json.Linq; diff --git a/lang/en.json b/lang/en.json new file mode 100644 index 00000000..9537d5a7 --- /dev/null +++ b/lang/en.json @@ -0,0 +1,14 @@ +{ + "wp_prefix": "{lightblue}[WeaponPaints] {default}", + "wp_info_website": "Visit {lime}{0}{default} where you can change skins", + "wp_info_refresh": "Type {lime}!wp{default} to synchronize chosen skins", + "wp_info_knife": "Type {lime}!knife{default} to open knife menu", + "wp_command_cooldown": "{lightred}You can't refresh weapon paints right now", + "wp_command_refresh_done": "{lime}Refreshing weapon paints", + "wp_knife_menu_select": "You have chosen {lime}{0}{default} as your knife", + "wp_knife_menu_kill": "To correctly apply skin for knife, you need to type {lime}!kill{default}", + "wp_knife_menu_title": "Knife Menu", + "wp_skin_menu_weapon_title": "Weapon Menu", + "wp_skin_menu_skin_title": "Select skin for {lime}{0}{default}", + "wp_skin_menu_select": "You have chosen {lime}{0}{default} as your skin" +} \ No newline at end of file diff --git a/lang/pl.json b/lang/pl.json new file mode 100644 index 00000000..b1ee5b00 --- /dev/null +++ b/lang/pl.json @@ -0,0 +1,14 @@ +{ + "wp_prefix": "{lightblue}[WeaponPaints] {default}", + "wp_info_website": "Odwiedź {lime}{0}{default} gdzie będziesz mógł ustawić skiny", + "wp_info_refresh": "Wpisz {lime}!wp{default} aby zsynchronizować swoje skiny", + "wp_info_knife": "Wpisz {lime}!knife{default} aby wy�wietlić menu no�y", + "wp_command_cooldown": "{lightred}Odczekaj chwilę przed wykonaniem tej komendy...", + "wp_command_refresh_done": "{lime}Pomyslnie zsynchronizowano twoje skiny", + "wp_knife_menu_select": "Wybrałeś {lime}{0}{default} jako swój nóż", + "wp_knife_menu_kill": "Do prawidłowego zastosowania noża użyj {lime}!kill{default}", + "wp_knife_menu_title": "Menu noży", + "wp_skin_menu_weapon_title": "Menu broni", + "wp_skin_menu_skin_title": "Wybierz skin dla {lime}{0}{default}", + "wp_skin_menu_select": "Wybrałeś {lime}{0}{default} jako swój skin" +} \ No newline at end of file diff --git a/lang/ru.json b/lang/ru.json new file mode 100644 index 00000000..f337dcea --- /dev/null +++ b/lang/ru.json @@ -0,0 +1,14 @@ +{ + "wp_prefix": "{lightblue}[WeaponPaints] {default}", + "wp_info_website": "Посетите сайт {lime}{0},{default} чтобы выбрать скин", + "wp_info_refresh": "Наберите в чат {lime}!wp{default} для синхронизации выбранных скинов", + "wp_info_knife": "Наберите в чат {lime}!knife,{default} чтобы выбрать нож", + "wp_command_cooldown": "{lightred}Вы не можете выбрать оружие прямо сейчас", + "wp_command_refresh_done": "{lime}Обновление скинов для оружия", + "wp_knife_menu_select": "Вы выбрали {lime}{0}{default} скин для ножа", + "wp_knife_menu_kill": "Чтобы правильно применить скин для ножа, набери в чат {lime}!kill{default}", + "wp_knife_menu_title": "Меню ножей", + "wp_skin_menu_weapon_title": "Меню оружия", + "wp_skin_menu_skin_title": "Выберите скин для {lime}{0}{default}", + "wp_skin_menu_select": "Вы выбрали {lime}{0}{default} скина для оружия" +} \ No newline at end of file From 7f41607d545868c39ed51bfd5316a29a2f8e6720 Mon Sep 17 00:00:00 2001 From: Nilsu Derinder <44870116+exababy@users.noreply.github.com> Date: Tue, 12 Dec 2023 22:44:08 +0300 Subject: [PATCH 32/47] Update & Fix now selected wear appears and pulls the value from the database. --- website/index.php | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/website/index.php b/website/index.php index dbe40344..191a127f 100644 --- a/website/index.php +++ b/website/index.php @@ -140,6 +140,20 @@ if (isset($_SESSION['steamid'])) { Settings + + select("SELECT `weapon_wear` FROM `wp_player_skins` WHERE `steamid` = :steamid AND `weapon_defindex` = :weapon_defindex", ["steamid" => $steamid, "weapon_defindex" => $defindex]); +$selectedSkinInfo = isset($selectedSkins[$defindex]) ? $selectedSkins[$defindex] : null; +$initialWearValue = isset($selectedSkinInfo['weapon_wear']) ? $selectedSkinInfo['weapon_wear'] : (isset($queryWear[0]['weapon_wear']) ? $queryWear[0]['weapon_wear'] : 0); + +// seed value +$querySeed = $db->select("SELECT `weapon_seed` FROM `wp_player_skins` WHERE `steamid` = :steamid AND `weapon_defindex` = :weapon_defindex", ["steamid" => $steamid, "weapon_defindex" => $defindex]); +$selectedSkinInfo = isset($selectedSkins[$defindex]) ? $selectedSkins[$defindex] : null; +$initialSeedValue = isset($selectedSkinInfo['weapon_seed']) ? $selectedSkinInfo['weapon_seed'] : (isset($querySeed[0]['weapon_seed']) ? $querySeed[0]['weapon_seed'] : 0); +?> + + Date: Tue, 12 Dec 2023 23:46:11 +0300 Subject: [PATCH 37/47] fix 2 --- website/index.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/index.php b/website/index.php index 9ab1d609..4141a1b4 100644 --- a/website/index.php +++ b/website/index.php @@ -158,7 +158,9 @@ if ($selectedSkinInfo && $hasSkinData): } - + + + select("SELECT `weapon_wear` FROM `wp_player_skins` WHERE `steamid` = :steamid AND `weapon_defindex` = :weapon_defindex", ["steamid" => $steamid, "weapon_defindex" => $defindex]); From 5f61254b4e75b1e155dba629999ce5ac1871b37c Mon Sep 17 00:00:00 2001 From: Tiago Machado <13867481+snowhp@users.noreply.github.com> Date: Tue, 12 Dec 2023 22:00:18 +0000 Subject: [PATCH 38/47] Create pt-PT.json --- lang/pt-PT.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 lang/pt-PT.json diff --git a/lang/pt-PT.json b/lang/pt-PT.json new file mode 100644 index 00000000..8ce3784f --- /dev/null +++ b/lang/pt-PT.json @@ -0,0 +1,14 @@ +{ + "wp_prefix": "{lightblue}[WeaponPaints] {default}", + "wp_info_website": "Visita {lime}{0}{default} onde podes mudar as tuas skins", + "wp_info_refresh": "Digita {lime}!wp{default} para sincronizar as tuas skins", + "wp_info_knife": "Digita {lime}!knife{default} para abrir o menu de facas", + "wp_command_cooldown": "{lightred}Tu não podes sincronizar agora as tuas skins", + "wp_command_refresh_done": "{lime}Sincronizando as tuas skins", + "wp_knife_menu_select": "Tu escolheste {lime}{0}{default} como a tua faca", + "wp_knife_menu_kill": "Para aplicar corretamente a skins para a tua faca, digita {lime}!kill{default}", + "wp_knife_menu_title": "Menu Facas", + "wp_skin_menu_weapon_title": "Menu de Armas", + "wp_skin_menu_skin_title": "Escolhe a skin para {lime}{0}{default}", + "wp_skin_menu_select": "Tu escolheste {lime}{0}{default} como a tua skin" +} From c0cf9210dc361c88c13e6a4c5d826cbc2e743223 Mon Sep 17 00:00:00 2001 From: Tiago Machado <13867481+snowhp@users.noreply.github.com> Date: Tue, 12 Dec 2023 22:00:51 +0000 Subject: [PATCH 39/47] Create pt-BR.json --- lang/pt-BR.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 lang/pt-BR.json diff --git a/lang/pt-BR.json b/lang/pt-BR.json new file mode 100644 index 00000000..8ce3784f --- /dev/null +++ b/lang/pt-BR.json @@ -0,0 +1,14 @@ +{ + "wp_prefix": "{lightblue}[WeaponPaints] {default}", + "wp_info_website": "Visita {lime}{0}{default} onde podes mudar as tuas skins", + "wp_info_refresh": "Digita {lime}!wp{default} para sincronizar as tuas skins", + "wp_info_knife": "Digita {lime}!knife{default} para abrir o menu de facas", + "wp_command_cooldown": "{lightred}Tu não podes sincronizar agora as tuas skins", + "wp_command_refresh_done": "{lime}Sincronizando as tuas skins", + "wp_knife_menu_select": "Tu escolheste {lime}{0}{default} como a tua faca", + "wp_knife_menu_kill": "Para aplicar corretamente a skins para a tua faca, digita {lime}!kill{default}", + "wp_knife_menu_title": "Menu Facas", + "wp_skin_menu_weapon_title": "Menu de Armas", + "wp_skin_menu_skin_title": "Escolhe a skin para {lime}{0}{default}", + "wp_skin_menu_select": "Tu escolheste {lime}{0}{default} como a tua skin" +} From 208cbe1aefd2a2b03fd5a928df72dc67d0388981 Mon Sep 17 00:00:00 2001 From: crashzk Date: Tue, 12 Dec 2023 19:53:34 -0300 Subject: [PATCH 40/47] Update pt-BR.json --- lang/pt-BR.json | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lang/pt-BR.json b/lang/pt-BR.json index 8ce3784f..5bdab322 100644 --- a/lang/pt-BR.json +++ b/lang/pt-BR.json @@ -1,14 +1,14 @@ { "wp_prefix": "{lightblue}[WeaponPaints] {default}", - "wp_info_website": "Visita {lime}{0}{default} onde podes mudar as tuas skins", - "wp_info_refresh": "Digita {lime}!wp{default} para sincronizar as tuas skins", - "wp_info_knife": "Digita {lime}!knife{default} para abrir o menu de facas", - "wp_command_cooldown": "{lightred}Tu não podes sincronizar agora as tuas skins", - "wp_command_refresh_done": "{lime}Sincronizando as tuas skins", - "wp_knife_menu_select": "Tu escolheste {lime}{0}{default} como a tua faca", - "wp_knife_menu_kill": "Para aplicar corretamente a skins para a tua faca, digita {lime}!kill{default}", - "wp_knife_menu_title": "Menu Facas", + "wp_info_website": "Visite {lime}{0}{default} para mudar suas skins e faca", + "wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as suas skins", + "wp_info_knife": "Digite {lime}!knife{default} para abrir o menu de facas", + "wp_command_cooldown": "{lightred}Você não pode atualizar as skins das armas agora", + "wp_command_refresh_done": "{lime}Sincronizando as suas skins", + "wp_knife_menu_select": "Você escolheu {lime}{0}{default} como sua faca", + "wp_knife_menu_kill": "Para aplicar corretamente a skin da faca, você precisa digitar {lime}!kill{default}", + "wp_knife_menu_title": "Menu de Facas", "wp_skin_menu_weapon_title": "Menu de Armas", - "wp_skin_menu_skin_title": "Escolhe a skin para {lime}{0}{default}", - "wp_skin_menu_select": "Tu escolheste {lime}{0}{default} como a tua skin" + "wp_skin_menu_skin_title": "Selecionou a skin para {lime}{0}{default}", + "wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin" } From ed24eb0dfc0de43796eb50a24ceda69fe98b445a Mon Sep 17 00:00:00 2001 From: Nereziel Date: Wed, 13 Dec 2023 19:25:13 +0100 Subject: [PATCH 41/47] add config for darkmode lazy to make toggle for player --- website/class/config.php | 2 + website/index.php | 277 ++++++++++++++++++++------------------- 2 files changed, 143 insertions(+), 136 deletions(-) diff --git a/website/class/config.php b/website/class/config.php index 14721fd0..34b0c046 100644 --- a/website/class/config.php +++ b/website/class/config.php @@ -5,6 +5,8 @@ define('DB_NAME', ''); define('DB_USER', ''); define('DB_PASS', ''); +define('WEB_STYLE_DARK', true); + define('STEAM_API_KEY', ''); define('STEAM_DOMAIN_NAME', ''); define('STEAM_LOGOUT_PAGE', ''); diff --git a/website/index.php b/website/index.php index 4141a1b4..54c4794e 100644 --- a/website/index.php +++ b/website/index.php @@ -7,22 +7,22 @@ require_once 'class/utils.php'; $db = new DataBase(); if (isset($_SESSION['steamid'])) { - include('steamauth/userInfo.php'); - $steamid = $steamprofile['steamid']; + include('steamauth/userInfo.php'); + $steamid = $steamprofile['steamid']; - $weapons = UtilsClass::getWeaponsFromArray(); - $skins = UtilsClass::skinsFromJson(); - $querySelected = $query3 = $db->select("SELECT `weapon_defindex`, `weapon_paint_id`, `weapon_wear` FROM `wp_player_skins` WHERE `wp_player_skins`.`steamid` = :steamid", ["steamid" => $steamid]); - $selectedSkins = UtilsClass::getSelectedSkins($querySelected); - $selectedKnife = $db->select("SELECT * FROM `wp_player_knife` WHERE `wp_player_knife`.`steamid` = :steamid", ["steamid" => $steamid])[0]; - $knifes = UtilsClass::getKnifeTypes(); + $weapons = UtilsClass::getWeaponsFromArray(); + $skins = UtilsClass::skinsFromJson(); + $querySelected = $query3 = $db->select("SELECT `weapon_defindex`, `weapon_paint_id`, `weapon_wear` FROM `wp_player_skins` WHERE `wp_player_skins`.`steamid` = :steamid", ["steamid" => $steamid]); + $selectedSkins = UtilsClass::getSelectedSkins($querySelected); + $selectedKnife = $db->select("SELECT * FROM `wp_player_knife` WHERE `wp_player_knife`.`steamid` = :steamid", ["steamid" => $steamid])[0]; + $knifes = UtilsClass::getKnifeTypes(); - if (isset($_POST['forma'])) { - $ex = explode("-", $_POST['forma']); + if (isset($_POST['forma'])) { + $ex = explode("-", $_POST['forma']); - if ($ex[0] == "knife") { - $db->query("INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(:steamid, :knife) ON DUPLICATE KEY UPDATE `knife` = :knife", ["steamid" => $steamid, "knife" => $knifes[$ex[1]]['weapon_name']]); - } else { + if ($ex[0] == "knife") { + $db->query("INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(:steamid, :knife) ON DUPLICATE KEY UPDATE `knife` = :knife", ["steamid" => $steamid, "knife" => $knifes[$ex[1]]['weapon_name']]); + } else { if (array_key_exists($ex[1], $skins[$ex[0]]) && isset($_POST['wear']) && $_POST['wear'] >= 0.00 && $_POST['wear'] <= 1.00 && isset($_POST['seed'])) { $wear = floatval($_POST['wear']); // wear $seed = intval($_POST['seed']); // seed @@ -32,24 +32,21 @@ if (isset($_SESSION['steamid'])) { $db->query("INSERT INTO wp_player_skins (`steamid`, `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed`) VALUES (:steamid, :weapon_defindex, :weapon_paint_id, :weapon_wear, :weapon_seed)", ["steamid" => $steamid, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1], "weapon_wear" => $wear, "weapon_seed" => $seed]); } } - } - header("Location: index.php"); - } + } + header("Location: {$_SERVER['PHP_SELF']}"); + } } ?> - +> - - + + - + CS2 Simple Weapon Paints @@ -62,9 +59,9 @@ if (isset($_SESSION['steamid'])) { loginbutton("rectangle"); echo ""; } else { - echo "
Your current weapon skin loadout
"; + echo "

Your current weapon skin loadout Logout

"; echo "
"; - ?> + ?>
@@ -135,124 +132,132 @@ if (isset($_SESSION['steamid'])) { } ?> -

-select("SELECT 1 FROM `wp_player_skins` WHERE `steamid` = :steamid AND `weapon_defindex` = :defindex", ["steamid" => $steamid, "defindex" => $defindex]); -$hasSkinData = !empty($queryCheck); +

+ select("SELECT 1 FROM `wp_player_skins` WHERE `steamid` = :steamid AND `weapon_defindex` = :defindex", ["steamid" => $steamid, "defindex" => $defindex]); + $hasSkinData = !empty($queryCheck); -if ($selectedSkinInfo && $hasSkinData): -?> - - - - - - -
- - select("SELECT `weapon_wear` FROM `wp_player_skins` WHERE `steamid` = :steamid AND `weapon_defindex` = :weapon_defindex", ["steamid" => $steamid, "weapon_defindex" => $defindex]); -$selectedSkinInfo = isset($selectedSkins[$defindex]) ? $selectedSkins[$defindex] : null; -$initialWearValue = isset($selectedSkinInfo['weapon_wear']) ? $selectedSkinInfo['weapon_wear'] : (isset($queryWear[0]['weapon_wear']) ? $queryWear[0]['weapon_wear'] : 0); - -// seed value -$querySeed = $db->select("SELECT `weapon_seed` FROM `wp_player_skins` WHERE `steamid` = :steamid AND `weapon_defindex` = :weapon_defindex", ["steamid" => $steamid, "weapon_defindex" => $defindex]); -$selectedSkinInfo = isset($selectedSkins[$defindex]) ? $selectedSkins[$defindex] : null; -$initialSeedValue = isset($selectedSkinInfo['weapon_seed']) ? $selectedSkinInfo['weapon_seed'] : (isset($querySeed[0]['weapon_seed']) ? $querySeed[0]['weapon_seed'] : 0); -?> + if ($selectedSkinInfo && $hasSkinData) : + ?> + + + + + - - -
-
- - + + + + + - + +
+ +
- + \ No newline at end of file From 819ac6233cb76eec55312438c3bb5fbbcd037edc Mon Sep 17 00:00:00 2001 From: Nereziel Date: Wed, 13 Dec 2023 19:31:47 +0100 Subject: [PATCH 42/47] Update README.md --- README.md | 38 ++++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index eaa5f34c..e12d5dfd 100644 --- a/README.md +++ b/README.md @@ -9,18 +9,19 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/E1E2G0P2O) or [![Donate on Steam](https://github.com/Nereziel/cs2-WeaponPaints/assets/32937653/a0d53822-4ca7-4caf-83b4-e1a9b5f8c94e)](https://steamcommunity.com/tradeoffer/new/?partner=41515647&token=gW2W-nXE) ## Features -- Changes only paint, seed and wear on weapons and knives; -- MySQL based or global website at [weaponpaints.fun](https://weaponpaints.fun/), so you dont need MySQL/Website; -- Data sync on player connect; -- Added command **`!wp`** to refresh skins; ***(with cooldown in second can be configured)*** -- Added command **`!ws`** to show website; -- Added command **`!knife`** to show menu with knives; -- Knife change is now limited to have these cvars empty **`mp_t_default_melee ""`** and **`mp_ct_default_melee ""`**; +- Changes only paint, seed and wear on weapons and knives +- MySQL based or global website at [weaponpaints.fun](https://weaponpaints.fun/), so you dont need MySQL/Website +- Data sync on player connect +- Added command **`!wp`** to refresh skins ***(with cooldown in second can be configured)*** +- Added command **`!ws`** to show website +- Added command **`!knife`** to show menu with knives +- Knife change is now limited to have these cvars empty **`mp_t_default_melee ""`** and **`mp_ct_default_melee ""`** +- Translations support, submit a PR if you want to share your translation ## CS2 Server -- Compile and copy plugin to plugins, [more info here](https://docs.cssharp.dev/guides/hello-world-plugin/); -- Setup **`addons/counterstrikesharp/configs/plugins/WeaponPaints/WeaponPaints.json`** set **`GlobalShare`** to **`true`** for global, or include database credentials; -- in **`addons/counterstrikesharp/configs/core.json`** set **FollowCS2ServerGuidelines** to **`false`**; +- Compile and copy plugin to plugins, [more info here](https://docs.cssharp.dev/guides/hello-world-plugin/) +- Setup **`addons/counterstrikesharp/configs/plugins/WeaponPaints/WeaponPaints.json`** set **`GlobalShare`** to **`true`** for global, or include database credentials +- in **`addons/counterstrikesharp/configs/core.json`** set **FollowCS2ServerGuidelines** to **`false`** ## Plugin Configuration
@@ -69,12 +70,17 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
## Web install -Disregard if the config is **`GlobalShare = true`**; -- Requires PHP >= 7.4; ***(Tested on php ver **`8.2.3`** and nginx webserver)*** -- Copy website to web server; ***(Folder `img` not needed)*** -- Get [Steam API Key](https://steamcommunity.com/dev/apikey); -- Fill in database credentials and api key in `class/config.php`; -- Visit website and login via steam; +Disregard if the config is **`GlobalShare = true`** +- Requires PHP >= 7.4 ***(Tested on php ver **`8.2.3`** and nginx webserver)*** +- Copy website to web server ***(Folder `img` not needed)*** +- Get [Steam API Key](https://steamcommunity.com/dev/apikey) +- Fill in database credentials and api key in `class/config.php` +- Visit website and login via steam + +## Web Features +- Basic website +- Steam login/logout +- Change knife, paint, seed and wear ## Known issues - Issue on Windows servers, no knives are given. From 5e62c7c597dff6cd4829b7d23c221e9e93186e09 Mon Sep 17 00:00:00 2001 From: Nereziel Date: Wed, 13 Dec 2023 19:36:37 +0100 Subject: [PATCH 43/47] Update preview.png --- website/preview.png | Bin 145519 -> 128786 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/website/preview.png b/website/preview.png index bc02c97d8ad1819649ede0102414843808f80281..1e2b3f4b12610655cabeb863c82d6542ec09deca 100644 GIT binary patch literal 128786 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�_qfuy6&Q;=j;5Czb4qMWsA0Dg)^{ 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 literal 145519 zcmd43XH=8j*Ec94pi~j*O+Z2Ey%!M#1*A8ncj>)DA|SnqNKtx+(3Re6=$+6d^b$(w zAwZJB_kVjo&%E=lS@UIP-s=mCtnBOTv(Mi9?DE@*R996b!l%K%bLS4x+c)x>ckbLP zxN`@a`abUMClimthi?C1xoaxEx>GSqw|o1~Jsa6~vUl!O#}nL`;oP}{X?iO!tL^uXfcA|FJY8mqXuyC;UZABt^zj~yMgPrbj#{Lhp7VZ(NbjP#!e zLW5fn@y}B{!}t8P{69}^oa~FgBp^>hrN0Ck!LbiOy(P{&}-4gto1S217kU+iCAT@M)0vvI0Z*5kPqo z!}WI!`DebD&|OY`hODQ%k;lB_13TZ{E_J7&gqFrrpwcPt|V|R)J_AhPO@iNqbZ?5!(rrh$?B_U5jt5Qt`nTeNX`ftVM(<)T*xN$3{*Zz!}CKm_S@h}b*J1(=}VYbIL=@qsa(3|ynQq&g)x zAZ5LqZS62rXKpE}LJ2LkCZFZmb(a2q3;J5jxb5#Ly@Tw%D2RQf>grrVfAqUmMd;`6 z*8HcY0t~`3kkq!1rF%07@l=tlrTo#%YbbV8Q=EHQSna)g!51Qmh1aLtehf&keNsIk zx|*_a*@U@46MY(wyJ47BOa2{@#Zo-1E;)u;H;eUv=e5P7-0$tQw{!i(bA0{d3gyvJ zFuYApKou!zKvK0h7e$f}Eh)@-!bU;Qoz$WT=_ zOZVBI9_3|O+{=$c8yO^&$7!(hmT1F%sphX!{L5c+}(SDa(e1)~17@n=7^8SZuDSQV&U zK&ED+&WLxOmZ>sm!u?|kVNO6^55h5eUl$eB)2L8YJPgmpHi0=0a*}asLt8E387+s8 zHuP~ziH%W&St!1KxOOgHil$#y-ibugrMImQ<-XNj#{$cJ&2}{wQB>^q3931TWUkSV zEN8ggw-`rw^!!kkQam&vre=K_CEn}aBFlM+< zP^pq_Trhr|#>MF-TyCwqSf;*v!eC(4I*beeS9*Tr3=qtIibZ?wy;Y;zp69=~+IYl{ zUi9ur--K;y!#4@CR(U+SozyeMJg^zkDSdSjo%buQ+w&PPge~jQcElS z$B$_vAo}VcX4Uqn@RGOcy;C!HRo5Gb5nAF^D=-aBD>Rp?WXlU>Kg72ut(r06I-X_i z@M#x>^cD4tC{_2FVfU3~n%LgXxYWGy3Z73dLtc1-Ke}W~MmsPrC&t9olZ1@$V{9;f z*luGOr4qd2QTLqc2Lma1^6-d<$4-v2Sr6@-33p-7=OUE4u@brq`~VU?-kEdn6#7wp zT_li_+Rs+8sCDA{OH29Fm`l1Y^YKJ~HMXDMy93ZvoCO&~pjHCw7v6qjON_CfCkF%n2J8pZlnhCyn#?l>d9 zzUc=;7n{^)CBi9zI;OMSNwUC{`!kP$_2m8uHY#1^z|%(O~mUKgmV|p!7rsUt1(G_VyDi%9tVld|8@yhu%-7I^Wa` zZe8ys*4th(a5F0l&v%Bh1gkK(&Jo-^mQ%*#q$jIDn_a5;uGt~7SJgM3|DR1Oo%{)N zEG1dl&F}&!o9{9`(}VtV^D=`TqyxhK9|SF8|1B1}Rbjh`+1ZE=tW+t>k=kEUqnkm& z%mQoUgajv*{B?h$n*@W}5Y2?lYu>pVu3-jZpJEJo#yQptN`$k9x@BiqFvmj!Dyx=P ze=m4Qrk2uOO;(a{!0@MOjYH@w04*~1FK0W&b~pu={h6iVkLjatgimM(od1o-xFPsT z19Y1FDzR~D02XBVUz+}c53(=5Pp8XWsHy;1X73&SUFj)EtmrYeW>O#YZ@AW9zCf1o zZ{(*#{{K$wcC8nz*~q_nakla#JMZQUF4kRA;W@m(*iFt8Cv2 z{vdIPK~0%-vF*i-P=#Haz@oumfmV~o*|FD3C5|1b5?SDfWgYa+y(!n+{Y!v!IKaVV zBV!5vSGL-->0wd@Pu~bqY!_B`&?96Ho8Cd=I)c>J8{cNc1}{n$^xTUVggk^vPk=Gxdzk<4UxxuwMdtL`B(B0apDJm zq>>R0estrz=WP-F^U&a?MTh|F-6FNSI8k>8F(AVD1j~ANd(FE=Fi?U&#>QL)>UBTj zeUM_IqhQ_hcEBebzL)D1-7!ReC91us+MKOz`Gt|6*okMBY!~i0(SJlDpD(#f5&V*_ zvx2`Hsk9e!74-6K`B5iVcUi0Ar;dlJA5{OLKjT@I|MzsX_x`-g;lqHLpW?Y4lnU$n zZiG-KHqm&~ev3M3(6g;?3laHeQQ^C>wi9>WsJ)n=9U@^O1HJE8&y6h{(Sz!!)zCF@vS{k;11$Q_MdBv8AL&Xk;r%rUT1Hf zxynB-wln0|MpBQ(MH!!n8c&|e58wv`PUt(6zn>FCJq!3b>SIUczCA@X+3yp~qR%Tu zFU9Q~gL;NYBERJ3X=Kk@fTO>X*$r;$}AGJnp6^M(oL3Sg%5r3C4gw3RD7K!~={$vQ7PX9OK z@~|$pRv+wcC?Z?`*H3KdmS=0rCQvgF)}?>BQ8+R9*H0bj{e&@eRD6jY@VJS<9 zQKQAgj%{MU;DTOT!Lsk=4#DiZMw#43^(uM#`Sx zoZ?Ps{AD~I8C+~WOe??XJNVg-u3gFF+XmhMc&s)%A1+xw2?ws)zs7i1-`3*lVS5et)km(ma_$@PjNJQQ$->M|CPAVcOJX4 zqHmUA!NGT}aj*2=G$o#~?H+k_x5#2B{z|s*ul(%zO{U+d4`xtDpdE`0f;wAdi?IK% zUt<3ArKp7W;u-0xy9vG_s+9j1>FS7Z@jrj4;!$5P2~Th;!3W? z0vh(FX3q!Dns%(zna!AC8Rwwz#$2-K*U*bgM3#HFQXb3k3GuoZo&MSQ6OSEnV)$d{ zYi~$^bnb^${b0N+Pq`vKM$_i%NIU(;8)Zz>hbxhiW#41UrKM^DZXeI0;~VH!2l_k0 zOn=M?<_rstUS6_ssPyGj+fnHjRIc4Sv$q!~9`4Fu@yiQgXIzL4!1Iqe5IQ8R)XG51 zme5zO-$g2Ny6;W<^WvHSc#P3jBKzP9&p1`XO^jC{En1vb$-~^n4lTA@8$5TAPXMlBOikBkM z1Vz*gb9jcIDz*7>`9+)OVeB%@xg9odt4|5%y^!>c>y2MOVWW5=WGBhsb7E~c*Y6Wl z{76^Ew@o#rkLV|jXJgdGTaC^4YUdxu_S$>ViSI_krF))z6&akaWFM9(aB3-b5f*d( zAawUtUwKj1O71JiWIgg3oaP++_D(9kUEhE@X$A;46P@zWc#_)c*fd4Mep{c*A`Vdu zWpSB6VUf0a)_~8|K;QHM!xHaF0HFieaZ_4&MgTjV?BMw3bu1O@rNZlFni0 z1WXgzJhXJkfXiwRDiaXGXiFX7nfB~OS9_x38(Pb&-=~gq>E`MkvPP6$P z?Bxgl@GY0!QI5MiQD*<8thTdC!#SU%%kN$)@dkk(Kkz#mICPRN7K>7S(7g>;Nir_u z&0<%^f)7ZRL>{CNI>mok3emqnviNwmRl8?E9$EZGUe7$B60ahl@nbU|I-7MpA7@HE zLKoWMWMfj1c>M2BNBt=zt4*%;D~GNcV2;|)n~UFvs_#H=l7o`DelS~)syOfp>)kH7 z*8r>D?6(zvpe56EcZ3T}pz}3oo(G^|29M=VRQeWM;8eeUqh2f5YD_iPRm%f$g{YJ! z9XluKDeeV}SgfrC?JSH(eJ&R`nyl@f@Lmg_Q_r|-j#f)e*^R^^2@LD2q>o;N)08UZ zFVv7Ss?kIr!!x+0qT>%@9w;Ge8Dbu+gF%Zzmr#Pr)dO3BYP(+Z8y6Fi%csNLttssh zkRpFWc5A5__*k*qCfm>Oo8MMW-J`PE$Kn%_=_SY_@Fe=}mK11Fb=G|^molqwGQF^2 zb+6zuaKYZkd{E!e8}9CNJu-H*2h!tRn2X=>$?p`0fxN58rCc!a%GF!jV@DSR-s?^F zaIi8v7plz<$67}E;k#ilEv)SRna4K#1xabZ83W+bf*PH9O~WcU0-ixQbpYE``7X*q zqCGtnp!kSadx`k>r|%>?+mxMshnfQbj7yhGF9nq{LwL6Ia+s4(2%Oq&X^1b+y4|M^ z6~Yr$fN{M%D~(Ckmy0n(&jiIP<>&R+gbSA$%HGwOb+mftY_}&Jmgwqmlj2wO0BMl!@PL$-43CX@;wPkC_oeAU80w7-HhzSF zzv_!)$%(&*3{xcW5mIpl&BmtXt zRp=Y63g-hy2c7}ia<-NrMQ;AgOe=@j(Y`5})-Y0Z=-km3!R2ZGg8puQzaYaSowvAK zqlvdSlbq8(rjpu31b`k}SE?%QuoC0XovMe8RbFmEh$g^p( zv5Gsp{eDMfhke0$gIx_49&x=RBkl1FmnKooYsZ%fEF)ODlxqL|(|*tp&#U-~isO90 zc5e}yN*Z?0vb@C7g0cX5JrY<6qSZIf{>Gs>Jf$>{*aCLXV#zLcgdi!}NWk*O5$N%C zeb8sFQ)Fc`iM+?tD)Xx*uA=ZCKv-p{2mZ^fIB zlq7jiUxpGTx>>z?Hk#sAaFaXAPDJE;A2_2M+Qt9S+j!~~Dv;yo8s8#q^%6N~G^5fp z-3=R9wWTUWNPLI~X*YfxCigg{TlamymigpLdzNeWoA?=5o3VZGcy+g_tc9VQ8NmkU zC54QxOC~oL&M$Zqxt0aZdRV+ksRBBAT{6rByP|8)yI#&pB0GP~_kkAT(&ExFalB_v z!E@IIk@Vsz+RM{l2<7FvgwH;?gRJX}Rz$ zd#-aqZ-Xvh6Hh&ExmzK&%FwNOF1mFZSurLSyB$sX;R0`_g3x*gB@Yw_Nop9xV36@AdsK$m>Y@L6AwI6_kh;2I(#RLSbkPdhu2YO5#y z+c{x_Wr-fSE!!P4dTc9S*|wPZVEju(CbXgb-R{RbsrE-^u3K^7a6OQF2i$hYWhCT^ zio|e=eEHfNq_KiqsK;HvxC5tlj@Q-=U?Qm5=rGlkM~)%e=E&|mYM#Cr*rCnO zymt7 z!qV7UwJa#f4c)t96*$f>cb2*i`C(^TBEgzU(blA3mXrf$sZS+%EPT6rCP(wFvpdzf zvr_y-?a}~-G70HkgJ`4c1Eei+odkgZ< zw!f%oU%!~OSx>X4VRk>fPW3CJVau2q+{yys?*e7EIIqwjfB5VfEG?4u8w%1wsJ#GP zHNaPF{r(cmoxigfn9^BJ@oUVkyA(8q;H@d85y1Z`_%fC<2a=vGiI?YSg-X`%54~tVyaV;PX zbUE@vL;U$e32Yfh?Z%C-)gZ!$I#PXot8h7Nz5_jPq%Xm8hAU%JDt`4=IDgjbT6STk zcaT?kMhQ6YemJ!8A!gGP>4I0bpP&|B7N{Gk5)#&SrcE0R? zgYmvHSH zhCq~k@L5u^_lWzYGx=SExL){bw3IMm^8xO~S8@Av!w=c?U8zjM>a^~ua+Z56t z6f&G(D(dWfB^#bawU=HqX4$7YshV4sv~B~u9^9_4IR$g2Ro-+=nkbWqRLw7~zvlp! znI~+UnG$7|b9`WeHI0I!*K!w+J+dZ46ARSTJTwv2co)sWrfdMD7K)y$vWvxv;OCcw z1`CyE!H;&^&9C?+g1ONo;3V<0!g`>*oopN`m%+xK zO8af}q0T##gp^o0l0^FGEl$w|fu8C+zbF@11uO}N2u8i0K7&30O`8 z8Q>{dRAHDeZhGu7tXh|Yd5x_!Eyc_Wd*#W!EP$P*C#YojVBdwCCher^BATb_+!8zO z(Y-cXny=TAp_OnhEreJF;mu_)V?XEouVh$TqDrZbF4-pJMM6xzCXW0_O{n71In`CN zFA#bvUVJS|=R#g&lOg`-+u%!;GP@VJp}QZmZ$iLDCC#xst1Ukx8~3!nXyowud+$kM z_btath$C69hW5tgw)FhYMI45n`xaK-R7VNfmw;G=igL+w{X~R~r|J4HZ5}N9UN9{2 z*>YYHQpGSB8CkrEUx<|gWjyw9s>iQ%N>u^%1~ON>zR~u!m08xl^8T{eQ+b}$9oVe8 zG?zQ|ZvMvo(LUehUihpl#z>17-PO4>RVl){<2ns-U#l^Zx`M?JCc^YsrM&ke74=F| zh~eGb^L#FZhwbGoHx_N+q*i~6x$;VjQJL>nVf+){3kP?V3qo82B@=!*FvO5bf=Hza zP7ls`uYNEogdW_gh{e}PsjS}p*FQC_c`g|)8ARd&?nlRrNk1wY>rZu+`#z>o|G-44 z{t7i`MYwv%@AR3NVfY#%wc^26IJQing&1w$keKHeH3*6J-g&Z_LbmO zed-9vkFuFkVkk2|h6$(QNm;1@#+BMO_uVW5xrp&%-K%d+(aWV{-{yYV%iMO{z8=E93SaCq;V{y046hDSxq`jhL_s zmbkS61xm>MOo`Gp-SU!=gUhS$fFm(cA`!#WUB0_oU_%QlrzXlOKH00NFK<(?w)!V! zXUQTng5a|h$vf|rJ)r|d5KwXT3G7=$rsf7sxr9i^Xz30u@82P%3MD6I zFM%GDr`VaTFa>pXl=n>|&e86C(R3t#b`c*!_I3SR@TQBh|I*EQmcAHDL**ku8R~>I zhS3GfDNV~Su0V-?XvCqS;lxWR-}EAbGJ9+jVG8RKoeNo9oy07~U1wz!`zs|`ot6OF zA;W-L^=q|TVRJ{Djkp>!Tdmb~lnA($_}gZ#6n=U%1n^3eZL}rL(tqeODk{l$U~_}IUZf&RhzpIE3Yr&p);`y-b?Bq?vC(MCB-@6E zLRS(+vbr%9I?&x<;UfVrSH5nCyXN%{6)wRe!@~~i6R!7gi%sNEYcs`BKs-XS#rUQzx?Qfk$ExmFfUk}8_vt^l z`epJ~=#^xKCV%p3xS1F`x{iV#trow@=kmKtGJ>rCp?T(9Mc7Km61<-(bLuTR3G5xS zcl8K%KbTIUW}+l|t>~HceN{Xf52ydVH2E?Y`z2u@hnV+E3qz?kfhrr~Liiosu`BGi zR^etxbS5?p;Xg)!>lulRTP(~hPw1gu6((A)t+ zt&MR?m;oCJ*j!Va)phP07=Kb`r*V??)Pzjg-TNS-_kinyhf@q}skNI{$3!8VzIgD_ zNvwW<*ettD@xQ7#t}WDqv`Hm zyK&0lvmx%A@YJgj2Z!f;osG(#LopebXE`L*h10xlDc>4Tt6!5ZCd3-*tulva#3#S$ zu4EhoX~(yABv6({LCtQC_lnqog-MKIo$dj@Nky^7mJ8WYeyL;O1>0SvQ5~dIkl$ot z1j}IB%}IBB>-KXpa}`HkhtyI$^^Jy!om?8qQ)A!Oud&e&V*satDozQ}1!^iC2UdP1 zS>nV!JpS~0G>JcD(EI&sr7JokdYd{qhJwL>3407T>ir8D(<>#<7^TwHre_(<6PK{? zSawH3@Wvv$)41v-T;D^8+#J;cjsK;T#{pd$e=K^0EWaF^#fviuceP(C{g2kn6R_<@ zS6SkX7ek+@x&UTG8*3KeYe7eS#wvjqiajYD{D4lBb4AD$@QK2iP+jB?_DZ`iEPz|^ z+m`O^U9)(p(1~6g`SZQE*ThOgt$ZKqxglV`arMM7+JF{Qe2_W*F6*0=x%Tj(qm4;Pk5qng0p)iyHk~-}k}+85 zaJ~Z&0q<1~Nep7NM`BF1^HLUJR#&~9iD1p0jkv9pQC7^_w0JWfUU3r_j^^>n&R*X} zvSfDTp$Z_<=8fNqM~!}%(y>5|5-UcFSt@Ax^nsc~qQJ|nHrVGoWdWORl1G_)wcdwq zRB|@+6HDh_a(7szU8H`L;11N3Dy&`IqKU2nXqY1vU_j*(QKph|0#3cze=0bHA&4h# zrAmyoXh6TK=&L#ws*iaFDFbaX+O`!p#2TdH4%DkpsP6zLQL=9XX$tb{X@w^v_rpjJ*qKh7l*1^Wa$SEH0l+=}_@+&Ka z!7vhD+m1VgEdlt4DS}0l>-34-;A}SZE90qB*LI2fGaI-qE=F1#<$m}f+?*P5fyd&9 zS%UZ}ZSSp(Lpt~xtRsl{WB@@07g1qkIzHJfcaIz<+x3(C^rfC!uOU;& zXO*84nFy`P-q`KyMiodMW*&dHGqthU?Y3}nkHsrotNKu6F^g!BLmjaAA$qf4OjS)e z-!tZt==9S$)e+NqPKm9!0y;&lE?wd)7@h~)LlF$U{Rvrj={erlrCa{~ipHUB@ACH7 zmH_s@-m!IH?@MhSZ}bvx6JMURW{2K5{}@a|f7d@<%h#eA_vAE+9I^7NSEu*>Y761t z0s3s*y!1o|P{?UhyR6H(5r1n;tmyla6K`g#N~Iu$c{s|W;|wY}fo;$^+Ba-p zwc#}gx_-5)RaN9_6@FFwYAEcJ`!3#P);Ful_D^aY0&HvTE4Ow2g7TaD@iNGmrq-~M zcFw*+eVH0sph(*lN-e;9uW?ha1D`t58{kJ?`S0_RbAWOsB=^x38_yjlP@l=tY7L>R<|`=wvXgGqx6 zc`wYyl~aMU^P0$m8eG*5TKKzHB5?hitG0!Tw%PQh$V-m}NX3JBCCx0%X&D>!k_=zD4KRj&yBe`*Tfx9IN(6_ z)WpJmqrk^;MzNvL^8IU@a8^nSA7u%Cfe>XC7vop8gE7JlsPb)>?(8jI`VY}!S*3yq zH0KRD<>w%+WpHrTA$Rpc2I2Z8u>d1u&-uAOU2GeioV`4TC&n|TyC%<*LS#fqYC^&2o_%LoO#HE`6YB6Ha9gB*WE!ZXj zBJU!y7=u6KFg8#2>_IJTjzSwSrSC7RhNIf}w@1}!&+2PXGc@` z)I|H8LG$`ob8~K|e!61}!{6YX^#~i&2@tAEWVaG?(T?$&Sm#IjT6g1*5DZ&ADV37V~E!F%N!AgZBfM zMd3}`yr(HPu4r5FW9ol!HxQ}xopjh!)t7Iy|84F6i#vb}Q0~9@i$atCzni!KQ8)lS zpDuW|VE>BUmiKUo!~W&69kIMKUQO_~NA5IA&u$XeGbC|~xhtxDT;_9*d$tEUdEMvK z;8;=J2tMc;>W0{;AbWjo5nwh9$<*>5Tc+x;d@43mTR}=_({1ayy1^anG#A?%0eei} zQ}X;b2{_{I*!r*YV;8FCOu@cKk{+8{mjbmo+|6*lB`;a*b)4KOo!6T|O`Zqw(v-%L z62`ZEbGvMH(X->nOE*Z?)T`eyMR3~q3iNFsPu>I9?7OVIHbsaggmju#D>HylO~#kR ziE5reV~gqrc_tqd=w4Kft?_#brup{Nt(lq(k3@bpO!uinMo>xxeOw!vOivh0yKoEh z+4p-U)zP?(&&@)01FE9>qiu_}#M;r`x}Nz8W{Y|(lD(us(ua`5(3>yp!Z~s!9i@MU zWTE=)Pj#54myvxSeIx46mQGX&#&iB1{NT3ii#Zc82653!ckAR;^hoT#e2qj289T_^HkUZY9zqGtE zEPKy~=NxYzmF~#6@?!wX+&fnfGQVv)%Rl{4_mj%N&OjXfzLoyLJPz1BSBagQ1iaSd zpVhtS3VN=D%ORDkC z^p7lKWJ`Wn+Qz%*PPa-CP*-rt+u#|n5C*#8F1PhFj>bB#?;_ds5|oDe`58zdI28BN z1s+VbHPauc45S|4Xw&fXuE>ikHDVYY zRQ7pxuTB(?*Mst8jWP-)q{9kM@x}>7#kbY1BdvQMHa+>cQmsulW;yC<>!)=n`@a+lFE3OgOptS zPYGH_qpYS!u@xxRtd!{X<+R{c1kVej@rM~o?{Ljt%000;y~^8d;NKN+qdL@CEINCj zsTMqrpqK?9h`l%Sl{QoC?09duJ@*H8I81UNA5mg%%@x1)JaZFozfssPL2#E#kXhzJ{mH>`QJ{ieysV**1LOR=aH~|+j)R! zNq14W%PbwGIshLoY#R=|BXp3-w**T&*B0T+$39 zs&AmU?kQeflc1eo)xgRg)KIG{iZ*Mt{Xk8UF|Kh$KY$-265s(BsyN<=4+xu8#Od@s z(i>i|-k)wr;q#8a7QWx8oWj)E&dA{F_4L)5Ds?Jq=T!fL3qvN#iWTh~We;66$%=KZ zu#FgQgj?8^lyb@!X+F;VK}AxlR5;e*&THt%xUirnb^JPwsR9*NPcp7pq75i1?6{yT zwjfcUMdFkGKHu&y_qLeblZQNl)SUI=Lqr>{PeUSFtZBA@ZXy+lXI=+#mxtSZ<9+sQ z(tb9P%jE;UUx}NWmkrXn{X3IJUX5cq8LK_Eel-|5x8uM^8wfJKM25<6mE{mTiXAtI z`z^;EOgEB77M2n6a+dg3Z>IFEA5OggEL#G5{>x1P8WwrE7MDZiG8ID>hsEeRYq7Hz z#ZJw+<8O@T$rz4(ZD^pB$njKU1tW}W(Un$dR&(9@-^=-8#^IzP+Bw=M>;w}f$uarb z>p8_aXYbE%UU~d@tD?|!tX_sbwCTwfVEw&hT(I@%mBPx}sFfcrgYu&N@ukklU`sn? zlZZAV{K6vMo`94z!}hm~}Sw;A?%PiJW-O{-Wo;uSbNPE+Mh< zBuT7`=Tt?>+56sGPMhB9QM2I5-WO3Y>SN6>!8rAtq6RmjDi%gfxWRHGqbqG|L{kVeOn`G(c)hFA0?&4e0_bvdB1v9TOX9h-e$}Z3jjJa_?kas<0Q5l%2q9AHU)1n~#d9Nug zHnsp+Pya!Z5awW3`yp15&1NW>>nhVX@7{MG%()0 zik7=eyz7)VmF3IY1vkuO0n!cdIiwrZ6S_L)zt|wW+inBg9dp}0D9%IHbDOn^lE>*W zG+5nYA;$W&7ID|h$$8fAMknZsoOirK5GwYv$kXQP9e;^F*^D}ry55pEL=H%IREmjT zIE2kOUah7dd2phRxP}p&$AK{cR64TDTzb;<93Q!LF4H?l0F~Wuh_8kX9D)_h*RLOoYN!uZ6Y-eb;<3oCR(fT0KBNp_ zgHokA@Rf9)weKh`4hH#Ht6>Xz9DsoM@{-S zifU(yk&tj}`6d$)u1^euoG?+?W^eX_#U7+E`NrP+LWLn&h(di7r7Ow40 z6mQ&G0!GSH{l@hQAVr2)FEp@sf3YK<@pImHaq5NIE2+-zcEI3(*D=Rsn#A-gOZeK@ zZq@c+pzE&1P{NeQBjfg|`w{FY?tKDAhn^ae`jW?fkF;r|HtyXusAHdKP%jA_QMqA# zUzp#2|1+q0rxsY6uJvk=B(?45xQ`}vwP+R8=Z(C)P|P1EobksA@BdE4tg8&G*N>S5 z-V=#yy(4Ele}9Z(c0IB*R(6GaXSSX|<&pp+g4SrsgJQ4_TU_n|1|6OD&E}4YEBvl_ zHU#1EvdHN3?QbpRQOOxJ7ALd!%6S-N7xyv60{8?{$K=?5n zv5o(8=I#D=0H%6AWm9k(J?XwCDk$*G0z8uEy6q3%UJ`vIa_AVN3YPAp44u({)%YCw zOQM?mFqH8t=Z#=x`97^e$uCH8+>8C_2{j%$z_r2AW=!)$j=Uj+|BOj;8Oq3KqT2PT z`mmZB|XZ{Whf0XvFt z_}@8&|K)tSwmm*U>Ca$FQc{hB=j`m0A}@-RmUpIa-}ombQxz-OAy8A(JD)Z8I}3DO z-!S0a+ZO8|9u^GNsVJTL8Gp@Qo2Gqb#fV!L5ix40IJT!4-k0IDeOgl^7;riwqTlW( zWId7|smS0@=-hfvaR^+IUGm#|>;P>uO3^xm!_jFQd^+*FGX~Cq{F0JOK7MZLszt{U zJ7=cCX}1>;8xxZ(RKQu?=ntO1`qsZs>kx-ySGHZ?X8I$E_7@llFe~Z~`t=N$n_ooD zi2c4gN6ZspVXTr8+p6t32pR_l?a9X&Ey;quiqwA%V)tGz+}JyVn}3(}Bp-?aDZhIM z+4d@h_${3xOQgf~&ZrOXOoheL@&OMe%YIQ=B^k z4;!~(C<}9Q(>nc?&CP;%Em~-M#s7TbMPbdknBQmAK8F}%`2@ei^}Xn&7!MzVs4p2w zA^G{*`}56=7zbz&(VmDIGOE4Gbk)2JTJ>OW*14w>d*XKdVy4~I*oh$>2j?o40`l<7 z^-q!duCBaJ_QuGsl~W~s{2?&*WNICj?%Hq?#<Sb_DJha! z&5(`8<_*`SzORmleTUavuxl>Es)u-p!`G)2TvdUy+>b@+`UeLmp?bk~65faEPzlC8 zVW%D=Oe-ETF5uF~zk6&<4R1+_A%%VHzeWx+>E5c#7m_f@`GjL&ZED`0-=lG{J)~;LDfCI(tzKv}|s0C&>n9 z?O*!V>PEf%d3MR%Mo~f7c;-7*OgrOkiQTo&VB;Cyt?X`Rr?*_@Nu?~{8d-is!M7@&R|5{fka3CDC}+IgO9g(KJoM^ zVy@gD)QaqCqZ5axHP<(WCpTR2z|2zu^wqVqrM0zbZ&Xsl`o*H_YAEH-NzR5?ASMVq zRR$>S1)q;rlSbYPRSj=bU-J+()!d9N)l0G0_bpO28qc?Nzv07>=xZaNw0=qxcKm#O z*h+$pN6b!6m`8T|yBq%}|5+5LJzP+vZD@Fxm{E){ZG$rs}8sOSd3prXDB6-w&}~14wK5vtWXHaj32a| z(fcJeE$r;f!OX()kd(BN0KUN|_GmRXXM(CmC~k3q*RXH|cRk_vruswR0dx86TGI%5?>*y=8hzv6hR zu&2v4(d23G0bizfT&&Z=G_Un#S?~Jhmr)2zPEC1;J>85Uoc{&- zso;01^j&?$r3-ctT2-ZP2$3yM|E*VFzpJyjj#YG(wTvb>Rolcegf z^o;S`sSeMZ0C`I2x3ru;-I-K>&glaBP-0hBtbiXA8ly7FG1yK2S#!}-eUSW%8%}|C z$jbvmL%7};z2E2yJyYdnj_bGGvaQ7-I?Kz;N0X;1{p!R;3<{Gq?*%?jmxilD#vj~2 zf?$<~5z+o1Y`tY$lx^5Hs?r?_NH<6;(%mp1&Cn?&-8FP0&A`yz%+N?TDBUsw($d{s zYwq`X-t}Q^>nB`wocpoY1&sjn<5G#_-(-nCSYMv3QqLI+0|MXWy#L=DY^0$<=y~)v z91(B&`fwe1*(oCKCxChfr4T5SX5RekMqt=YbttGeG&~}ws~ZNQD$Ly}|J=e{x&O#v z?N6PRfU77g6IP@iirO})>F9{aPz(W9`Dwe z7Y(BzDaLn~w%EiN>Q+I|<$l}QawP;({r|bmKI05y85eAo#>VKFm~vmGD9Ctiqw{+} z&cs4y;QSQi!f1w{TIEP;Lir4mtQEfx&{G2R_?bkskfcI%&JEG>X6CYTIc$z_^W8UIL%9y zX`&?^UK@Jk!B*)fjYMH0W>*&>3!7ohtbQ6Z7m47M;+o`nd+x`@Mpk=M37R)RJ*NH# zA$Qw^mP}yRL+T&{$K1*t*kx z|E~&t0@p*S#ysRgc4Ae3E*B;Y9str$-ikIDKg4jFFGKW-Lig^@2bx5uGbw7Q(*_?e zB@ z8A9H&x_tn_BEm@H-<0LzDTXLRSMaX1bZ(6niyxA0TfAGCQaQi|1wSM=#b*wN;VtS0 za53)JZ3nF~84OyClAN8;JlmFXBe{~IH=PXKf2?dlmA8z87+%H!7xQ4sgu z3hp=?Z@aq#-*IKS$^=J3mO+&kRK|Mw0G9!o$T>azP2yo{A6hkBkv(dzsMRqPj= zl%G-!T-JJC7nc_AVqv#^QR8n|C>(vGSI1i!@Zo$*G5r2LhGB+?GSvQqgr5}QIR(n; z)mdLs`g;Ma^J40%fa3!abgnEz0hjU{pWXEU4PyZYg_4mxKM&)P;bHYH1uD?>?)Pe= z_O-y9WtdK^#|x#FPpJU{uMVG{I&bU^C$Y&Q;7-M-9 zn%(BGg-MpbLM{gTF8NiOBVmOvsmg~7gT0%PF772&eopsYr6(h*T{tYwHptzoe*^h{ zA<}kBsDf*yFONIPZ^%#+qh$H2PJ$hkYO}Djr$b_cWu@9cMBj3v~TP$IyE- zv!Nl^aRB4a8J+cOuqOB_TbpHHOb-Ge&DaAee*3M!MfS+1rmh zxL@ge@>2g*Y{B$j6dCH`DyzqOy)WKooietZl#>jrQ4v1JO3c~`66G?PJV`2Y^ojR- zS03FduO4iB>(ev1h#V-+=GPgs=EM<_pd%L2L5YraDzR}-EF-H=0Pl%T-DkStn5_b& zlP2}bqMxw^<&77tXrgD;V4ox!>sLc-uvZfS(^#TkoCQSPYXM9%5WcX=M+VUr%vdVJ z@NlXRYB=td_sxqmU|x;lH}S!c zjzl`t@LAX6CO6CL_y&$@b2mY^e9A}N>a+*^Y87q3#BfNo-cC$RC%NCv(Mxf zrjV$#opJubwQuxaFeo_S+dIZXIrn`5MUoRb=ei*_)w#N|yT6QQ>3rtAiI1Yle zXR|C8L4h%;-)1TiDD_)@s46$Ih$#JAVi>>vk8i<`9fnuNg3a$}K7S_H&f+56Ft77E zq1vpdR}9gvlgFhOqa`?VaZylJl`bg5#^5QCv_PgK=aX@C>|%ZE3a@ibOojx!*%N|UO(SilY^59Jx&bt&ftqyzgHKYI=8gXg|Ti{-T0tMf+c^d46H@yghbsB z{)~IOyV}}T@?W6lsQec!_iWpYYa5{aad*0bciPKLO~=K7g5oLs#h6$ZuDpHa44i;Z z$XPD+e={6%TVUWP)uz>HvFtNY?KCNHe5YtSTGgV@bpjVr6aPsK<2wwm87EhdllI6T zwLhO71u4->R%a_oj3FFV2A_CYxVag}SX*_T9ioz7>&z zQH}CZORp5OGW4A zY0~6O9?#mfPm2s)+BJ|$ZUEgMDv;yPr-`QyI4yFH9_NONGT>pte64&<=<}?#X zPD%bhIdl~cFTCRV;GDr+Kh(gNPU7nhLk5a?Ir~EbUH?Y{Dt&6iqOD;Wf%gXKQR~xs zaNiqJI&=Ye9YYJlU~>3p_Z~$etbNjsD#U4;?%v9nXPMfHschx#Ga8OJ7yg#CnlriK z;a+#$5)$*tR=fcPhivGyC-oZ~%JKJyO(c_YCLPZY_sQ0Vy*@NVR}GP=ndJW!(OIqM zo^wE?$8Y3cYy8$oO3Q=k>?kQKO^-SdSXUQ1Ucyw(@L91s0y6FBJv~i#uimA*FUMIcOBV?JPo;u-yG04`Y(L9sV^FVq z4`WpGt$CgG31^r*kM>SL#>PiHzATjI06w5t3uoq#nyaY0!8QeZ2&a;vXu09eOJScM zkWavU(n#mk7fw)1gx3hlTUZ*YF)_^8LVC!>60X}SK}Q07^X4h;yafIE_3Ll5`55|5 zj&a&mfAi%NE-o4Nt(8m>-iAWV+S*&MU0QO*VHlbFz3Knym5>{s>IA|WX=o&Syt19_61{zl;d0(oh_w~D?OFX7F4As<*DyLd8VcZjBQgg4XQEq-1ekes zs)d7Tld@aV0r$)rC#RK^1S#Y@N~9ORE+8Xf+9R-x_@(5KMlySo1iUX!n;n{d`|<0T zH$1U+_e6nt;Qw?d#03r(GAg+o;Ut?ic=wGiM$gmN)q);&TTA++4`{iVjqpyI{#Jgh zIH%E>c>F0 z%R??UWWI!=6)&BXXvX|c=>>#-$98U<_fhh=B;+JOne`t6=Cht^xZE^>`d9g1Nxtrl z!gdNmC5W3UFk*H;`GXW#rB|Y}e}Q)?oj;B$PR&-`wGCQ&In~@1KlQsj=6_W+VUw_B z$4}45hPRjDbE*ajY=!3JWE3H(@xf0|6KVaRw~3QF97xL_rxLav6r`n}5UqjK{9n3| z#KgLUT0Hm+8>=z?r#qtDNU+sYDlT)K5&DX>)L8&ic)Ab4G@e zK6wZ)RBf6>?vjr3T4dhsA~DBY-qEqz&IRvE43 zpwUR^-ZujwqJ!ETcQ7(v{`|@Cb70_L2S|GYdE08L!iYE_hFkSbFj80K2ReO+55d7y zEF??(b3`^6#sH%)+PiM~4hKyweuqQ~s-qHP>&Tm%TeKf((9M|)!%LMji*#&H9mQnW^_$IO zHjFx>4>%ODJ^7|a)G4TM5E`i9=GXp#aq_yrmjGa{!1}=f66{xqfm3d5P4_K@_=~{X z-`yGUizn@9%EFD>mZ3&y-|e-Xc1@$JYBkSvV5yx49*JX{{j0 zC|VJQTufo>i{PxDDZX0)YKJ{&HKt+N<8p2?Lpga%h-ZY#UA`J?J(vP6D{igB0IY*&Bovk_1tkP>YAn z;q~*H*9=&0F!zm4mWE1=7iOAYADYy8PrLeE4qKxqM9Ij6qN3wgN4^RSyb`1K4_qM9 z`8pPM_U#W8g`E$ED+cJC{Q7CnY+Ivn$jzu4uvbWJpti<7>&84{$K6~omb^Qpxuw%3 z4Y8{j@&eKD@**h_bIqc~^hHdu6p>9kxuFOE$-rDz{_rKN{0RYfQ33*<(N8bnBz;Bte6lx=l+y6FRhQj&ZlNbs}1rD|$3`FKrI2aIJ3%YTx?%Q{7Yg#as(rVQ92H|#! ztn47uilw*ekXEekNN9LphYL}@Ew#|NG?De5AlYg#;I%ZS-lk*e}ytdZ- z*9;@N<_W}Q;oy(MR1L<>4nCa8i(bbu@a2jBxb;R%_7=gV)^>;ykwa(WkYbd*7jO zd{W{MzvTqaUDb9|Vi>C@J&3sW%WcJtBKFSZBVPS=`M6!%p<9OdieCfXgAuglq0N}@ z9TWE9AHtoRZ_zcSU2(s#UV{2Lzv~jl!S)MR>D5&zi@Dzbt5alU1~*6OXO0@>6LJLt zr|wR#@Fa9~k#xUkBVI=-lM+u7CPo022iMNg(C|Pjr7&H+B$9Y>f%tb;lcEz;H)6G)Hsu*-uB1!DR3^`%@_3i)y_E$zovRDUn}?#J>E^(|SuC9JgoNe^`RCWcQ87w=mwVzgqRYIoH4HvZ zh~}@ZyL)nWqk4)izCeiAmTeQ-e4{d1L-Ud=8&$xjnW67dKkGWm;|dVOR~}leH#9(amSUomNX(AFCx7CA|}7X?3J7O z{DeJr>%!!R6ymWq{1&BLhYS5RC?0PZhB7ofEUDs&TYu!aIl{rOsw%?PSzEkQCG>7a z1H44N-^MKT8W89zwiAw4_&byxOzWdQiG!ene-_JFSXq+QP%%EFi;lPKMP#XWo4s#x zT!=6U%qlXuXuMnNdoAsJaMWBh^|eoY@rFi_g*4CaTkdz!`J07{+OkF zs#f;O1cnkvFE!Py>cnH~gIl>0u-`xH^$Ae(!Di8$>@1XSIk(Q+XK(PXRq(xMFUtMJ z0XJ$%yd+pg()1(PFRPE=11t4O3{QOG0jpvAF*rdyE8eC?B)EpUo1(i*fDB8#<85Hq zg~{gO3(n9|sjVP}S|h0k@*KW||L^Z(zls=Bs?-@U}67i{YK1~G_^+ttVnK zGIC!fUc|nUZAV(g(amxY1_bbVP-HOwP(#bL(9qLs6BhkUPMV~y_<(4h$XuFAys|mUguv^r9_A+%+#X6}4RyEv`>`{m4?z zEXk1ltmhYrVbq*)0ERy*UIcuQ9Odrrqn^cOq#dK~+?~`i%)gV}7+vcq`?S{iX?Y*< zL`R&{$OX@Q>%MlAcH0jJk1yh_U)iHyHtv%?G#sMLw}}(PysX4g2N*_V{9fsQ%o7`U zz*~u$q^3yFn>2~`nOuo&t?3Nz91RTfU$qjL;eI9mTI;B^CCGJk$x!;02ea$k1tDoz zIp@7wuM}kvCMKptscpKo<-&9efF1&*&~5N+?6O_12P$$@k>t6h3DHiyjvjS{|(%L9GbeM8eRn^UH652p@5! zzm!ejfsY=aI<7q1ZAJXfpUGP`N@1t#29rhBgCE=-=H3TTWqcze4T+jEy?xL%`ve1} zyRc&@0uoq6ds|1GP&8(}(=T0Dt+<>-P2krKSyx@hj|%Q|E-o&*wax?qcOIODlOcDe zQ7SseZF&XRH`-P9UZfhjn=*b<*#U#iE@F4sE5%g~G^a$VM!fp%&yNgSW|TeiKbHTO2X3SGhq1$B6K7+1 z#YH0h^(1UHh$n0g6a0KL6+1O-&cf347(Z~S-uh&jHe>+*eWG903kyqOKTJ^RZE}dL z^;kr$)N90qZFBUD2wt2n)&!a!hse&?d9Z;K7)ya7& z@olQCCoBtCbFiB8R6U(j%XMt(EW9_NA@GAFX!W*)hBm6k zO#ND7YjLr?`or*)iwV5tv|fL`ii{jlEm;fgV8MAa9Hyaqu9_xj*298PleaW_UyR9(agz+-$_g3eAc;`UY*>Qz`^ zaEYXq=IZg;xq=T?j=oP}Rbs49*|c~Ryu#qIr3mgRH1w{J(b3&~g?T8qC4))fkqomT zWCP1ZC=MDEzxC@B1zHZgp%#yUA`6<;ZI}Pdj{1?_Ise0kEwxfFrV|!0IO5~>$^7`_ z_I}an^ooBq{{t_IvOnI}xj)}zcD(yb#ReBO8I2*QjE@Z8SB+8tr7rx6n@kAc@5O`| zM@tXtTeYSWD?Ec6b+ZM=oUt#+{&zeY@&kKfmjk z-9~+S@4BvF`0FjZiNhVM!i=hPM?=7wf$n1+xl}UxW+$kKJvN9M0Dv zonf4@QVP0k5$XKzyQXxW`vpcL$Blu@a~-hqOgO*MGaoWd4`4{SXu3Fwv)O;1nh! zWXM66F_X--eMdrTH)*kMKeva96DwZ_5p}NYqk?-&F?Gtf*!P z7M-lPbes=quPb~mdf_Y#%xbT1a$cuHMnR!xW>e0IT{PPofe2AP+?Ky>8{uEjBR#!o zHd}7HVr7HmY7XY8;!%nOB?d|OXN&rJ9wfJ4@`<<#pe`&dd|Rr0-SpA`w3KehZPDIA zDb!a-HVG78ev62r_*{#Ko^>H-SZ;>NiLlA)+OU&ddrK>3$n|zZ26S;nkrbAHSAI+V1KnW-}?F3!7PZCEjMY0OvDw*DV`y2H=q5oNA{L~1RnCk2YKO>Qe){yM_yAMNMyVs7A5G1-|p$yw>?h&$$ElIx5a|=K6_3nkoxAPbYp`k zHj^2`BslE2)B-32>P0tNCpZcay2%i|%fi>-l2PT$O39Auc@*Dj zjv2`)m>B&u9q9o|lhBHX=6n12>;dmAx?Z>1a`tPN)Y0~8wNB957#h#v;6qflI;$O* zp!G!~sj;!{HU+!q!~0vAs~`Ef@E)!yfBs`ss{3}PQEAjn_32Zj$J*%Rqz+nB@ywRk zjnci>j`aF5@Rx|X{}_3h_|0A5!!^fzlLOmfhNl3qUxt%UG2ra#O8xFvB{{Oxc?I?= zo7SBSXZ+@tRgqnDF1i{{biE}Z@X-F4r7_?MRa8D7dv!u#QX;^(y85>k`F6K7eLkuGeP9 zBqlTCU}OYoJE(tGDlBMQY=$nrBg#z7+zX{E!&qU$vk>k~%(j**{2h`)_jV>mWY!$+ zt&Z`LgX`7!Q%LPo&|4*3@BXhGQflg$GeD$(jX%rtKU{KzRD$pQm=6U#5qq%d>Pjc{ ztdr~_-~p#0Ft?g@Tri)e*+ z+ca!L|21KV&Cvh+D(UK4Kk+%>`89fl4t0N|N%`-}b^?wi=F>z3=LYm0yv}LEIx5(e zM#scfW4?A4Sg$}bJo?-CE1HOHsz4LrnzEdJGe?9YBsfL_xmdp3ySL2%X_ARFDdo4(3nMT8=mC2&UgWC|3p8S`~a>|hkta+ zeqOrD>z=Eeg_&+!Z0ix+X^iS%_n7kECFuKvi}o$f;y!kh)&wwC9v1FfZ=i zP7L_1<6d*=b)dI2S;gC6t`91pAnf6MJWr$+MkUW21%BDa@yW4|b zSQNxn#(udx;(vuh#`n`&Q(P_xj-*U3LnD2(qqYT*F#X8c;vGBURy{nmyaleLc|JqT z;+wMmNmbC9qQT(zVcR;|qSY zeinhaFacK=QGzs}HIYbP>+h7&D@#29S3_C#S^)G+9OlxMS^( z?j>JLE4mM^Duv3;uO@r(O_&2M^y@pD32COW`Lv3zNKvD1{wj&>RMwDwY09Zxe?|<= zZQ(@tSBvyWKeW3%!wcul9Vn`*SRXx|*`(X<;8jSdd(lfU8uh^Lw7Gkc0w%i3>ywAXfzhQ_2c>SLl$yy$za8hn+bT&B{f7gJk1qLuONqa0=Qd6x#T?dV&JTDH5J$>`kC!s&^^WF?303iKbL7K2XEKk%x>oeYc{ z6js=}%k$Vo1SNu^zQ+u%EVsKS#MjWDM$mk)CF0ewKxTXXY>&w%3ndj(_E4abH1wT^W^GIb>y_lCQDG!lAu1 zzhW5NJt$PK)Mnxh9Y&k8JQ7)Z<}jQL2)?A!S(NIlr#Q*U1(Pmv`%m+;Bk?8BF<9!l z%-ak|>H^KB$d+fu@tqaDq^*Oy-n`WpN6yfzZUtCWR|k;Z5*6fnU2l7 zoN6=Q%i;7zuPOoi{apNx-5b45#IU>8-dL2B9boWYU{Qc@Spbs==jT8aa(G82g1}M0 zV{+2jQeXtk5jm>*2mgd;jZ|C&T~>p!@UIRc+gX9p*c@Pgj}zR+ZVCyLVzmdQAVqD@ zBy+`Iav(*(6dN-$T1Eybs679&Nm)r*PEnCC;Ngk{VKshg@rjR*^VzuEnaHc@yn16b z{`}hI6JSU?&L_i>wr7?4Y9HlbAV_vgxngqorP!4v8mu-vRq<$Yo zkNY-$6GX?(j!jyzH#DMtMAqnk8uZ+9SNei*|8L(nFm@}DdCfr-|77R}Ld6h6|3LTZ z8Ttdw!lV_r-O=r~;~7i!2|Lu+L+1BDk^o`e8((8F{YGJu7vDoc^O_lAcHe2qYar2Y^DYrGUEmcpzHYi7zyYm0-7AUZC|QCyesIb zAS}(^*}Bgt(|c-`;75)HVX=daMJ+<4Xg&>xAYZNQzQdZ~7F`?SlCgy{H!bOiQcImCOa(L_tL{y)0K!@&1>#F(Ot_P;d|Zx8m>X@rp)h`=frNZPgAs z008)-(@V~88`k}NZ=kbF;p0=g&;irJPr|zRb=LY@)7crnsRDB+I5g4}=RA^w2HzN} zZl+;lm^V3YC90+RMrLmS*fbW4kg+ZIjFq`lR5^KfUnL6_9}~cynWY~(Dv6O%QFp1d z^z<@VDGwz5?>o5CWDE;f)-!2zBK(Fv9bHpw!fr<0_Hh24>?9;6n##!WNpwzq6jg5< zi=6Lhg-J>Q`#W6j-F7CM9E40S#1E1zC$%O;u1@v#)IW@d8Nij#6H`P94m}z=h75Pei=J$A?r}F_=m;1{kg8Lo0IXR)AD`^7a zl~+{6TozEFq#vJWWN=Zm-JrIxwCbyzu`#oXHkP30_>7gYFW@?%6OMcie4t}y#?oWK zsD+-F(9lpIWOOJy#;mAtNa#U@-!$%WyO-uwg_P*5!FdeA^ULMymW)`N3yfWb5$^fe zzaEvBdJy*mm6u~46YeLh*11j^V3*zxXR%cFTcoO@OoWV%jtd;v6vlK`DRUHPw)^Ea zI?Ngvii$gMYDRKk>bcl-6st3V`+syMnyy~6`FhZ6eYC;eKYduTf0%S1BHs!Qv$RQ3 zn>O@VooOX-46!8!fqCeP&+-Xvx|qSgJG3_DkQ+c$yCy-V;%FVpvQfiLbex0H_K}yh ztWt%4nU#oMMP7*DTi-}TDE`2Rm&z%}nl+yr`J!U<$<;E5jzZ*}X7;W}381U?K4Gby zRlS-s(25J|(cfW6D_rw8dLU3j@bXcpNG@(ByojQPHRH^_udmRm<@fwa;8-Ml5zW`@ zM5YXPXHCH{#Fk>Gf6z)AB$pXLLGiAfRoT-JwD+xZwo^d;K4os}vwo9(FdX*sXVJC( zNaz(CL_if!^(Qo12T{^IwBCPOf7df7jvpXajY$H;xscsb_$w56diUP1SM73d8Ldj| z{)?*W+@8jE6^1%a5JEFTp2M1!1nOC;PAq`pE0zqGqlVQUtf5aO{KRx&zb-xfdE*JP}0rHj7NHsobK&U(0@`Os+duX=+0Rg!q<_pl*MPyP? zg5=a(XwBMaK*s(ZxI}5XQtva5%}h%l=39hsZ6HH(1S^ZxtJrI(bo{_fXG_-8stKwo z=pmuV8C$M_1sx~}?IDKGq?x8*w+{_X1jD~--+dqv^~Ia&FVamA>S|yy84|pBj-(pM z^fE6`#(tpBo8gk1ciS-V2s3uK#4Re^*m#SL>JTYxDWP z?)!T6C#J0!=AQW)?s$gvrsF9wsb~ZR&N;+&u6Bg`xgPE{RCn9^d$VMRF{NHgqZWCn z>;B%3-MAjR{f4@a8oJWtaUc$jPyJ2-M3pQhP}c&_b-Hi$eV?skc>0W|ML8P#IZb%H z{qMT`js;umQ&vrv%;od)H~3PcZnEfH!D!M27e#RCxWT?>=kb_E$l*@4@9>d5Eb_m_ zvBF{q7$TPwvG4L%P>=R@cPp^gY#6tg=v5s{>(GQIQK}T)vbiE&jT-YeKo|;&)z9L^ z+STK@OTQTC>5%JXuEKa7&7fHbprq=OPBmvIsRm{s9R0xF?WsJ*;<|N}j`L2qfrsR}+DRSAz^oxtYm3c<5^u;$z~$*` z&V1>16d{_JtNF0ALnn>&R$J|wp%zEeZt{2DS?knVs!v^j#P8!J1(YNtc!4H?{vpSI zs@Q~nIMw|22hNlk*5Tq|^`8@s61;z}CGzegH@60V)7xb5RI3{ayM7CPPMiDhIR8NL z)qf`nkbXaPpEhipsaHn`o9IcdC@rjk(-ipZmd zhG>P(nNao{+<;wCl3g#0_g=pa7D{9k&DGw&q$>9{$ry)r47+ehA8H{MAqFO$`QJc& zu0+5r&^Yvm7canSlxXMP`BBN z$n$tU-e|G;>&C#}OnpUfLmm*qgF4+2t>%+FP-e?{k4_VOnLjWnsL*ybM~ZC$wxN?IB9s z)&+)-wXphYM8Z4r&*b3dQ*jPmbL7Gr#n)Tu?mOOa3bXQ#pN5Jb{QS9as0O+;Ekj zrQRE+)y$cZ-Gb>5G5&JNUBIjO! zp?$S9S%ki@f9a@G63zg~;fx!U(t*vsC3U3zuI)6WSfDtC^OdwOCHp;I*~r`H z`tu~)FcaS+ULjE(UekTtul`T)x%nSHF~>L>#bn!Q@szMg0Y(PKf7xz-VI!v8Gj8V} zFZ1mDbLNXcS8v(feGEa6N$Xbx22lf*rEYL2Oy?K}UF1NDWwzYprY57V!ZdZkD+$F)bq%GR=z@3dBu{?%X53M6!WvJ6A2=z{8- zB;r>gsZY?AS=CqebScNEvBplqxW}8tC`hcZ}8B zmzXX;;C)|abEtIy8QJWO+^QQHy-rTHa8tBIe-!@ht?o3#588d0o9mM^9)s2Wx^t-> zzYGObLO~rvGh%JQO(fBZpT23TaL0Fqi>J35Gfu*emQ0uOR2#?Eh9SMAt{|E=gF{B! z7`&FkHq5qP$Kz>X%)-x-;;x|BxVQB%s)o;U%MJ?fk>YXui!B|2Jxy8$#d&GKs4>+_ zXs3N1t#Jc}&V?ZPdKIZ7W#lhtav3;XG|}9z1=uhA06Yy8oT@#hrk3s`Dgtjkf^s{#l}ilxUwa~Ep4&46=lGRN{X`RDMk_hGaze}&bPRn^ zI6E&jcx-2obA*1#KV0w7AmUJwMmr>%2ULy5D_!4~h8k~`7CkAQZxP`|86JrTm7Hm( z9vb(ev`As(9ReMC(bjjMl|A)Nw0V7K@lh>^kbeuj)CkdS7UpZq9bCUYS=g zvE(n+UMp_;CY^j2W%wP`h=xR5i({yuEA+ECUXpSq{Reg_)j%4yi=*XE@<=?{1EQpj zjjxE9H2$8?!NEz*fzG}07MdQdc1R<4q=TX@+1HJ?Qx%$Mlr4oD<{9cYEsK8+=Bfq` z^Q`jKWzxHLITtkLJ`O{xq$5ne9x7XY`rtv4%iUTD9pZO{qbLhs%9RDA6>K3V@mMj!Km+_(+K)$kB6*B8C6T%V*re<&&hQz*K$Y^ZhO6+ zyJ^TRaTc~C7yRMDyCOQpP&w&)?v@i`v$W`sc+YG5U`}N~2Ae zyS(k})3a4%<(~OHRbY;Wh$z0VTJ)HeP7Qe@$faQU$t0TsU?9edgBlsO7IEMd)ZI-z z##(i~w@7lf&ksS+?#9HM+oPL13$-v2`nUPkrPefyoEJ(Zdn;8lY;(CeRa|_?v#^@U zJ8(B5l+nxCYD()SPx)OiR%(&vfSZmt5=tB^A?+9FERlZx=StMf-D^~r*b zBM#9ohI=NQ-XfPhNZ!wPJ5!ZD?=IvRa~$rZX2O;bKS~C~PcQD|TlwA>9Yb{?%8#hEPy z=h|bN#d_J4uH8iNKcT@L3yb`zTqOCVNcK+TvbKu;9`*8lL)K13Jfj6BlFf6tXGVa` zb{IH3LqGR7l}d7RR}g0&ZF1gdcB=+w%Qnr})K$r5u?z#_06i_AN*a)xx!`i@YU)CH zm`*JmM0e9vWw4iPypQta`>{J^5CJV*n*G7RoP*)A+?lJU``*`W4N+t2AK3Qu7N112 z7aPkQ4?c>g46gga3s|{PP@ptJV?UGnU4|yc`qo9;fgAi75AhTc^nw_BF4LgCzy0wD zLe7$w{llwuADi9To{*L=s`eAOLGZ=c#)e*~4iPQ4>MkiGYccSOi0TnF7A(V0Uxk1LPU#kq!iddL^Tx zVkJUsr;4qu`WLNa3xujKbyJ8kx5qNSBSLCA!U}$TW-9`2Et6L-Hv?JTFGUH7gucjv zdJ7;FF+}$4^;e8X#V&9wrc>LQc)ExW_dXm}wmOLb`eP*3m;Y4UwhrFck9+H)xPIBA z^*!x*&92*ljPMopJ?2@{^0KHiIyZ*?P<6-0OiZ!W-UBinH4lZ`!(~z@e?YlqiQczW zeuKNc@}yQ#+X`pZ`I6f6i zV9#1=J5^s#LBC!E1yZO6gn?}h!T@=$Z`jt6nYhtg&>L!AOv-V7Ps#!qiWU$1amir6 zL|V4f0stsVNRDyN`$g9Z3`bMIy{{*gBqQ-vHwB(**{6?Lfm%h*D2 zBPG(&n`E{n@as)vC!IVJ;(rPE&&KnV$srQrSzRFn)A=mgmG?@4{9ck0TXnSpg2y~p zPqCy?aY_=duB_*-+*1v6K2WLT!W)1lj_sZ-V=}kW%6qXxDTL>agVz=8ISFa9+fnJb z{THJErqOCX{?B(t#R@4t&q}i$8wH;H8cj8yY2>fK)Fo*1*t4Jrj*qnGY6A9-&_bm9O>&5 zk<=RBY#R6L>PirqQ-K_^8H(k(K#`SBJIk1h#z#OZ^l(Dc;I_wcIO)P$(~i#2HyWH|#%MNI*@ zhl!(E{8T_31y)&;9sAQS_OYc{>`ZP94Dgn;L|;5Zmu2&ih>A)L6y-of@hrzh1)}Jm zmD>qq8ceqBTprF9(24+6FgmO4eZ#p|&e>rK;6qp+JH96%>GJ}GXJ>M;!P%CF*j!KV z^#VH2SJv&ey@1`Gp^aY6a837-I(^&UAtul>l8Ux6CarO9v1*G$o2-m-Qa(PyXv=!3 z5+Z7#q;F&%e2lsp)B0@sGv(m904c41h@#qCgC7m(#M=gh*Rm7B}3PXvcg2sb{ z%vjY@i5Zj+jzpU`-^4x9!KYFoWroE$D?ZgZNCjWI_Q%rY57!?l7T zlCShHk}6xe`%1nz89hW7KS6#bu-Gips`wVtW%BU~pxR;i{8ps~ovWH^`W8Q(n@Gt^%lPAtr3 ze+Mdwg*8)vis35Zy*Ts8Xh?p*$pkXuROk&GkUlv^5egLEvIte0rJoVt5~Axi<+(>y zubcc{56-^$CN) zzK|rL-yjy~aC;(`6;8cE&Dk`sNBkpz(Cx*;?ImlN=rbP~fIi>`L%1AwW7~0nSOkD4 zli}e$1$99X1{0orJ&3FezN0X%YISJ@UCBJ{yVIpSQSKvA_G4Ju?Ku;GQm2sfp~Yl8 zM%88yaC~l8_!+-Mc2aysjUtUvknrcj$Fs;5JR(=Nm`MbcHlu5E-5y>Ims*qR>FJU1 zu);LmBtiw^iu>YG2+;(5+%AOnjAxK|4#cWx+PvM0-X32H{cgj2=+X+uOmQv0u1`e- zcqOOY*y%9}Mx|Oi)M6+cpB3rTx8IaF;Bh2Jcx|FSa#)eUkcuF*!OQUeAU6&FLre7PK#f(f{_;SMFN29i1cu|K_D1bk z_#0o3q&wW%N^`iRiJtoM6FvfK@ruv)SX;F{Rmx zcy%or1(g6wIa>%dhmu@CprVNb6z2zXaIawzz9=c>5LA)Tz90zuIT`NmZcb&zF>csF zqR7g-9;ER-Ewxv{gC7e@RUEjioV5gTB@{ckrq7!|SCt2OaHzHt;6lSrbtkepb2qwY9zP&_H%OKO@&D zbo%v`b;fJ@hSwT+K}2&fYR9ZY#ljLFIlzel=i7qby^|~nT@UO#opz)9a&c=NuH+rXNv_`XDDL??Gnw# zo_A~UWnX{4u(~{K#+rO&T%u3NzT0mzjGu6XE+`(T5T=c$+~d^(%e6AhMS(e6jc63{ zm%?`C^OTD>XP9aBeM)bU&ILuS(oC*z+fzaAYG57qbnUjz?Wqz5e*}V% z7f&yEewWxMO=7W_d_)2cRH-Bl<#4ir)Ihf?Angnn!05?`zf!YkLY%fetjdTveKJ25o~e69Ky`d6 z5Z4Ayd6S5LOl9|{tB|)#)tAu|zV$&eP-e(a;UGFOC8zgOz+!wJz17s5cpQVB8=buj zn*4Xs6_i4p(?k&+Z3V>OL5=!dbNPtwy`G;xU9!n*nIj8u@HjbZcOeAAUfSMYaVRNd z^fu79qnpfz1_wmK!#-I_cOCAkFe9|vgEcH{%rbT(?8uC5fgS6T7g4wy6~Fh5goA-W z3lbv*xQu*@<^WHV6~g9FDy2edx_yCC9f@MO!1!-fg@=?*DV#msdYSE5V&$Fk3Kk~4 zZIlOIr==$MkxXS|U*9)-;sE4YVXs0;pvesE%q6#;p8j^aJDlRz{v|$?d}Hie ztbRf}P~(krJ)m*XHi-x81#NQ8PgyZhA__i)~|qtR$xw z%!|gx`J&R16EWPfB_2GDud)|qVsV8GgQG$nYQfp_#2_>=kYn`up@nXDqhxle`yHQ2 zy-Ts{R4>gM%&tAMQ{Gggo7=gmd|3PbVrc)lT>_nEsdIk0NTmpl|72Ifi0<(uCg1Xg zOm61ZGJHhl<aN(czr*AaZ@4d@c5|^e zFjA$^&cF_{QJ8>xG~mcoSdys!8s}@q3Vhh?sZqj!)Y&f3x?vwSXg!I z>!$+xm17HWR^Tp=e8NrK*v%K^OYSLC8y%;leWAj&Z_3J<UtvZ#A)L9>X!W~>*7TjmbFDfJCr|14b}H{6CswD zXNiCer0fbAvH-ZuEO0sssFqj$3#&da_*7R~(e)RyaR%qA&AsB|zYcYcWm&&oh=@pc zZv`9VJ>@YAy?iGr@bOaM$eUu)fe(gN*Fr2y7pGI%*>-;Y$yr#D=ch+u=@tL1(*f= z?Wmh{J(az28QzwZ#yiChN*L;%p5rt4aUPNP_&42sj(MH;4uJmbKcH6iXK4v8QI(l6 zZXKI#c;}qR_*?Q+5$>$x{##p7#uSf7=Y8-Fh?WK3`sAkzZ{C!aJNjCu=6jh~WIBQr znxeZsV$9H_&jNRsMPNv0Qa$BLR?7_rz9rAq+Mb-y_BC2Y=SY>BPSNkrRU;F6-oJai z*5z%wK9!h1PQ^W$_{~t}IwcKsllwk3h(BHK{F)A`6O$0*+c3{V$u>_3cZ z{dLxwWN4{-x?7vO`oVEdM+=VD8?nl1u2&Yf;%Wedr&Uj$gxyijgsGBl zYmGH%8XWsQ!c`0ClGyzHCD5-bpuZo1PQBsn-POU=*7Yv&8}vySEg2rPNc=PXMtAbabT zAZ%*5EmvxRD>lYQP}$M)OCG(dFR?JSG+G;&9CFLuBI(eF+^j6(PdpN=9FldI>7X>) zyn;hqHlrW>ui>`WSc;VK&tRXOV1}@c_=X!n=+^*XCY0Ma4YMtR;9zuhm)7h!5ejznbQi_V1`lDG1z+93qg!WIP$d~Ze z*&pR7w?`9&(={$snX5sHJN7IayvlSLCeYJYd!^C4U9%z~iHScqpz`)J`e;@y!|7Po zCmk-a&0!D_v{(8K-r%ctf}daQ>^?_DqoK|aeem18>qQ5-N*CW2W4sA?qjR?TX?wDg zg}K6J`M|vB^eVp~fk7Ji5Z0G&2LA(IVaYc~lO=+OyYtRK?KVd3eU;xPV>q7q{@$tw z(J9F}Tj5dX8q0Q2v)yl%GA=`)_jsTOSJk2#yK}0AA8AlsfQsQatNqLL3XASX8tr(z zh4IDlm-rz2NqAPV#(s^I z?xw>bqor(0ki)^x#yR$UkQz?8%_-P)9KKChBgjBX1AZYHFyjKnbq~%yD`ZKp?)D=R z65PDdAjh2`c!ma)+8ZRN8@Szf=K4QfcQn;V-s`t#sh^4KZ(!tg zIx?5bYf$o0e+-oN=Q=1r{WajGZ@idH!pa%FsD%tpq*^nF>RiRvwD`caWu3Om@I7Qz z_pv3~jXl)XJM53sp26JS5mAz27d&bD0j2TyMt$A=e+KcNh`31c%S3ca%{s)}DaqEt zZ{SN!uLvGIHVHxaq8*x|E-^`2wmk}mt|LuSxNqRD`&L~_RW^dEY19-aPe1p#+P_Qh zLvq&Gi~|+oknHRfe|-yJk?$`TJ02`H!m}6-3IV0~rW-t9`fYTL0fXPBa~MrcyxF^7 zPE|I~%*MFivn7S6|xC8Sr;8ECfn9(5)C z)}92!1%%0djR@|CjV-@@_wFSgQdVcz4tI}OP=p#L*Q&spM(4u9D1P_&-{2uNc3lzbDS?izIw>YghHc{BjRWe7(BDSd7R|M}O zurP}jqXWMN$C0qmN+?S|!=}Y+dSDNL{WkBkd15Rw8C&gS||)i?w`@f@X_C8_&=lxoy;VDq$l}qEw=qiF1NMm&}C> z1ONn^6kMWfaBcxqNwFiFXuHFd`6ZtD&%;ZEp(G@Vi*%#32R--R6v)a1ktuRfh8jn# z1v)K)Lk}3ZxVhnKgWmzTHH*>z9sE%}e^UszC-2!H>*RQY002_|BFzc;yhtRdrcaPnwKyHgkNLX<7| zWD5<(Xz%fARjTZWG3XP*rEuA`fei3EfpW_yO**wIEY%t2sYJS~y>~zzK2SY6-KYzX zL8l%_=$V|YyzIJP)^!2>uB>kDSDYq#TTFlB@xk|YD3pEmYH4dF#r3GIVaciZ zj?lvwQ1_9x5c@C?wVuJ0DL##Rb$Qe_>G5mSd7tM7Svy%#v>n;ES* znZ{Iuqs_FnGlF@CEZjEs?Z>Lw+!S$LymiM+06Q))QHmH^JAVWEW|3CN68-S-~ga#%fXd7x=BLu8`ZQ;fDVnM0yL zMy_$40fDnS9i!_6ddm>>71x_VRTF?5xjW|tAVDAS4oV!CTxBXf>5eZhCd{eWeasj5 zP5F624rbw;YPt7URl+k7Vt3S z-+2Ne3=4Km~Qf2tLhNwo9<6G5Uv1e+_ z0OGqYoycFmyh|fweQ)6MhOGJbl+3O6x8>aUwz%}R))j9p$w7p8nMi7%P2cko5+*b> z+Rc1YyznhZ;!hoqz)P!{{z^$8phH1VFTG^QNl5}-z#|=hy4fP^PvOBujSRI5lg+!l zySdphcKZq)#Nsw8^=Q;>6o3=q8EN<`LHBqjL#Nt=XX~jENWW0G^N_0+-o2-?g@Woz zWrEU`=*uXBJgy)HqeybV5wT~u-o!m#A0(%u{zi3-jkV8vnskJ!w=a$|a4<;n=*DrO z*$9OoEYn(KW#CcxpZ{CnjefyCx-B!A(cD~K6>Z_HL2zz_;=WDE>jAJzK^hfl~ zw(Ptw3=xM0$A17DZrrpec9zKc^Y2Z!t4~o;(JO~L#Q`~nhB-S+M5NUru%yiLYmq1w zaIng?bs^}^yF-}hm4r=;DQ}UH_$@4uez94+;`8)0n{PmGetM+rH>fy}2sHVz{Nu%& z>)V#?75YBmoyu&xynHEXm2OXcezh)L)hj7Rej)~9kOwXQ-&@O^=(Q+Oo2?%#T1}e4>cYFBqux7OS#i0eP`*A#~LS1>p z-L3s)JXFdCzM>MV>|K6o)*NOTSd42VF{<(n&R5|KF+D+*egisH63Ayxmtde?H2{e~ z$ilA~tN+e{T{Q#FS`5yM~qR zK}Bru55)j--Xo%;b(h41geoI{S4Txh1E8ah#I#&+E&Rk(bTnV&w29w?UAv7w?_3TB zuC$FbsIolVj`66rMB@nAu>qpXM^u3LLx{)q#C~xuo-t^IYGEX+;LQbLKHmA9y1Ja6 zbX;R^E#;Hqm8kbzt%Tvc-CF4N>e-O?K=m%F=#DY>j~E7!EEuAGf@}DB50?v+R>>4@2!pf(3Gj^r7%;9i$@>6Zwpw512w< z@&DLQY5L4rBzZ-W3h!QE1e&}Oq4V>9)3&|?!|o$TiQNG$oGWgnWGJ6N9sL-AB`PB^ zrrh;0c0^24NN6HB(??7=%5uI8p7-%3^npy_%uIr?ThwMJA?gCIq;b<^E(yS#VsQ|k zAPTcZ*5(BuX!1Rqk`990qCnm1M>d9(<7mw6^L-0HuFtvme+Jp_Q-Ekk6CzO+E_VG^ z^K(-K1O!MjGLea?spT9G^BOR2l^OvfF*Q0CV2Jg6)G;Sj|NwD{!yQh*94n+ zkA{1*q+raFa^7OJ;V0SVGSLTuXz6i%cZkje)?9^Yy^ANEKQeBZSTwyzG=%U zJXk9`8W@Sz^xogkWg$0M4mNJH%qaqn(z{{cH3B# z8C`}3-bZd>YN*fK>sC10lm&^W%C z9^l@G&6bE46Akg<4*nV*6*anfw1t={?!kjFx}*5Q)`FmcTT)ai2tZ|+i$Ea^2TD!x zjg5`##tQbko1?ACmQ+<$oen0Hfi^r|*EK3a2TMibp6Nko(T}`4C@j#_qkIdVd2@)u z&7{Ek=K3z}q5eh!0O{^$W)IJH+3-Af7wRH$LDh8&8urzKiE(0OrfBH zVg51@(#z;-!1S*>*fbb6ti8}wd&uanwX75&m`jalpPTCY;d0TS7eF8mlD=2{mQ2N|rtQ?n<5QKuxT`qd5tl;>9*@!H=5y^;pjjK=1z+7RECC?jJ` z95Q1`K^I~I=I7Pt-KQW%qgiFeaCo(Fs3SM+k_E8ViU_=#npSc@{aWnnMxFzVBW7d-S49<(wla{FRufhIROB5XWQt?*7)56D(i3s&;8BG$vXgRr=9>i@6^&>El)=fq#x>Xo0j~tzqc%J?p18^>r945 zge8wBQr`Px!G^0t*XzSOPN$s>b3+r$EQd0>JlR435c8q`YQtRPd`AT$YH_YNu zJ_H6rUvXud5CI@QdWHshWTaZkI4-g=P>%Hw|9hEBP7KSq3uir+g!$z|w6vumkpEz8 zW;(OmwH5c-eudFOU8o10zD$W_&S7}{`iFUkt%c*`lYohDzUsCwKa7<}#Fu=~a%68r zsf(wHhxLJfKi;IvG%{VIjbCLp6Lj==giEJh`XyWsgj(CW9IQdC!e@&~Jmh&#LJ?6^xmewT3Rr8&Tv5?L5%F{F>-_d6 z)iGf!M|OVtn@;z-BOAl_u(C(LuM{!5O+CwNv-Cyd0Y4i|t)3s^rl zYot#iZ+@{N^=mD%D`glysqa<=>6GWRKD*+e{3qH4FI#^eg-yGpS5RYFW>=a_DlY(e z!PMa9wp_$USPApywhh4OT|wN<1VCSEc6$qeR*+!rhUfNVBnP;Tj*bl{yRWwIk5E%;8L;{xf6W}T_lr>5Z5`~K(qe5$#~d&#fArdcXLPR?stAJ$s^vY z6j1XAtW*Sc_TwJxV-O0zL_9f2<~%N$g09sBqn8~jN)4y#p43RH?w6RJL=)yLV!xDyLxYNXz=!L~&3#b45gC%FmE~ zNj>N%YS!0Hhct3mWuNQ@6_aR!?Y0%i^GWrZ?QTN|xiEAoC4G~6;NyVkgxt5R$M|@y ztf^XS+bhBPVxQ%WgQ8vo;?uJtBNWu1-mR~M*v#_y`f7bdb@+Y#{PI--MZ!8lVq!kl zK&QHKOc8M_{R47KG#Po8zGaCBW2@`Cm`2{pQ6LbqVkm2Hf;|U+dT?=N<*`L_d}MwP z)ik0Kn3zoCcX!8CV)(d7b2&}?RR}0#auwZuCvq1QmH?}Xz8KB9k2vroD-nr3{}7J$V60MxI~ zI3AKk4Mn@_GGGTgn9pU%>FI(ONh1xJ+KlW3zsq73euZsR3geDbum94x`0edy(**)# zzI#SVX35~ZFsnni*lFr|fQ=V6j0#SC@lZ9WlADy<^tSYX<}0s(=&856s{<)D6KcM3 ze(C!9b|f){W3>3?!UdjnbdLu4hVVSIE(Oq(!1(D62p>;L%^6Q&oy}}EWUr(diTb4M z_;b%WkMgxsT|uTSYJ+0!Cn_7S;2um3SEN7F62iLY+j1anUR6?RN-o?hzT+PROqIqD z-<%SIF20$}7*wQ?sH8P<@io6NFnxLds_l5BD>cnBm6tNPN`$ftK>ST0Wjj5SakGZa zsOtnesi{q33uTuL0)AzPeC3Y`lTWqYQAsT?*u|>)$WF@xe;3f^8Ya_IEXDSXygMJ+ zp8@JFwj$you+bw*1P|`q=iY0unOr!ZEuO(zRu^55IQ6?CGuw87atx>I6;;s?K7Zd# zxD|^C4l6&-k{3YB2t0g7r`y$H*Q2g}HA~%qanA4RDBylnTmVaXv|8v55T}t@Dt~?- zdssQ+{(ci8F{r++WPQYl>>)u4V{QH1BuxHyy?0z$+X7M2MAQ}Rnw~uV;W)1|b4!{& zXlbKH#F_IuSP;0qZbO-5d^M%PF4erH*w($_d?SesqI1;{ z1D${H2pXwyc$+zk#RO9u67_RK5t58uuNhTDGONi|Lw1GqNR>O$3t0eXG=yIfXDWsS`eJI3(=(Y1qW8+gLYlszvGLqv zX3)U}vSz!sx>{;DL~y=6f(GFIXQyP^wgGH|H3dgZ6K*Pw3J!T*nB*oAA#m>qCs}6F zdWxOsyBSaVJszNS+F3ro4=fh9Q|Vy@4ILewa~Od523$}~2XWL9n3e4xQDIrVtJ?`t zgH*K>@K0!e`&r6Wwo6l!)TPn5evEcYu@kno`}oi27oDuxfi!*s%lwTA%h85DuENCO!Ej5icC;nqM4 z$X-dt_wR!v;v$Q1+=`FSPW%+<=|e9!$T*P&u4 zE2TVR51ae9dedUQl0X-Mm$8?{wuj}){!7f4noK{T@Nr)=cEhd3 zCpv25z5>+!*9E8;f8XTqEtvCv`o)Ak-%^l4kwpjhT^qc`RUg*0$s5gSb5Hd+k0$(_ zCo@mLFa-zL!-ARrdBTLz8IuNUO%kUi`%pC>ki5ALunCxfehxsA8XN*XA|ugePCWkv z;D&Sj!$>>+tT^Ah;K7yJ4YgP%uw*3C7_CaN9PO~R=p;kR~g)+k56urq@wRT*jTwli2Of$ zL=JW@1m{S74?%i~^m#pcl;}VkvjkCr95NFe8?!j00KU5WO+LQsaVtvh)&xZ==aGT^ zQ%mgbO>B+U5noRoDKND0seMcYPeE!wHQO_3?3S23TtY?+`+Ak&4n8n2@68^ks{Pp8mZhW~y*J=zrc;P3wRfOin1 zZR59=97TOA2^ew)Ts?{F{0!jZ+X@ZeDuJqs3uc(iTAoB|h*3NAlfZ&deoO%1VgcSz z5x-R7xtnetoACl(Bu8K)GkK;t?dL9WI_?RQO7hw6IT;Qq!QaNHUI|N>-{T)nYO%v- zXq;7@bZ3S_{^U76{h@um(SI$5(L_#?VcQ1sp=bQp29mAUY^a=XEi7*mk!-gLnKHEu z=~7XJUvC)=Jr7#kVm$ev3a_WM^VQt3Ib`RuV)TIE78$m~!@q1$nfLZ)a;?Ns+Df|p z=7N$qc$v8ea7f}6vgCnm{%X?&N=5s~LmCdn*apGqFfcGwRY#;CE%k8&My%x>ZQtA$d5_ow1+_o(jc{iEJZ_(F7m&HrjA0~qg9CacD}*w z9snCNIrrPPd)L&PGN~=AlptbdgP9e2?z>ONPpqXZon>T_7TIWt-c!CaYri zlW!MYG1Md#YVpoZ^Lu^=Ji#J^Z9xIE(^n%qppR$){ukc+;e$wk6yH%XoF;rbiEZ}* z*vN|CauxZtQAe4}!OL}kg@mh_LU3sqI9Q@y0h2hx3H6fq{o+Z8H9zS7;=4Xu78b4@ zF(Z8-jS@t6&Flk0##wruy?EJR8oZx9>_|Su6e4Flc9oTJd}$-{F#KKVqhfYegII0! zDdk3KownJq-=BYofJj0UZV#(L1;~ZLX7QbvsPx}9nD&U zoqxxN{4*X5_dGuJd3@~xb4=W}z`qB|+W#|g@V^FPJ`W6k9@qiP850M6O&uUlPypWv z3x*4U&jarZMj#{KJ;u*C_FX8wIESD61dGPMS z(y8CRn4P(j+xQOy4FcfK=jNfIp{bT%EB@>Jz#Kn}XR^9a#bSLz?l&=A+LGjZ!v59# z8@_^a`bsWaM;%}{MNRu4M8la{gM?2?A3uIP{;+PR+4HKhoFGuLIjDz${eg&hRFXtOHw;$34gwwcgtw6#8bjh)I1Pv+R}9?P*t(;%tGY0uB8cp$SVq) z@5Jap_+yLeD1g@9_Xe@OGK{LSyS zKbTk3gvw1n2@CirY2SmechZbm;hlDz)bZx8Uct_!HW6h-7fci-7<#Jg%<}#~-yP~f zq6T3D%Ds?Xor)m}fYpUrs$jy4Q5^VZ;3$sQs>*fSXN=pGUy;_o}zm3*1^um|ygzEVzT0ieD9;0K1 z%FDoqTzGia=1+7FbIGfD>()Ehe5r<*`4rzF!2SSwnwJfJhwuxrw!rMi;DpaGAeQ1M z$o#5_OX)lpKO17aBt!SdbGuMC?UG%H|FyDq(uKTvn|2R$7Uy0%Nj$RxvZH-dc(V>g zUPQ7FjtxPK9Ud*RCUot<|T zGiIJbiq&FtqLS(yg)HKwgL!el^5Q#ci-=2If5FECuR@XH>b9EtU)oFsAMLhJIOB}9 zyIOwH+fPl`pUG8OGvVVojzWfl?EA)`U1(!T2iVZDN#8#mRMalCIOCF(_<0Riu zVHHRY$SUX@{6|Ze?@qXyHT)B58B2d9y&>84izWbGqGJ%wu&OiqeF)ZP!M&)XJfq%p zJJI}3j9#~?cL**$`D0K;It2)zmH0pZ*`G~rs0gES#-BOV zc={deoSGrxby=N6Ny+q7M5+YiaURb1rduS;tR4voj$%G_EfU**3$_wY+c;svj>P|~ zX($*O_E3YUVGF-&hoW^hYGw6md-AsA_8>LSn+L>Po6Q7qWk6@yCN*w| zaeWZW9fD9#M0}%3y?OO(j_Z%OqqJDTv)1Aj$-fl_sNsA-+yH3SE}*WC2S{EBslH}! zQ~Qs0fs0l^k`n>~FP?J>`>(2QF#(Nz$gi-aV?viA#hoUEBt_NE($vzcvTylj(wHfD zVT30e79faE4sgop+qfM%br=FGw`ffLkVxMAwCKKD-5rxZn!0hnRj^%_6R`fOMA0OG zf5DoH0ts;sW`+;~0cx>!=1kCVvwJV|d&}f=5Wp&f$;r%x01O_OSh~SYI@i8 zXNIw%3-5Jr%vPiEO!U=(1Nh@4_EuK8P9^|#c`5P*X!Xp@%$%Sn3n4LG?~WncLY1GZ zwlw^^Q_TN>eDZ8!pe$Jmy*g#dV-CmNWveV#wkL?}4`$5(;$rsd%TIF{OUDX3U9TUj z=b8jLnk^kq&U_%L)jSrU>+Ds?C6CUb6id+7?9tKv((!7}rvOa8=DsGuMcc8Hsi8)( z#^(Jan(XtvizDmXf$T-CmLrC{8+4B)8J2NN?#HB^eo^PbyyTP~3Tx|h9o?-b1CCf3 zTn*{u-bxSkoRAn7tr_MlLavTD*^M**sB&Z4uI%0n7nvOuyRfA99L<^|Wt-On`iokJ zSMd+`@0&}^8paaycC1@KZ8@z+x)5t=8d|o%yuS=(f(tNI>`|-q5-oeU+8{3nL?eP0 z+~53?suE+c-pJXqBa_E$EMRlRz|Fr*I`z&VxL#uLO}Ap+8oX#98)~o`o|c3NgO$;f z24#byY0TMH>Uv1vy+>CppY+;?3L!SQz7kAw!`kG*^&a#SO>7kx4WBYN;nd}R2#H_| zSXCX!7vBmCZ=H^+Fl8=WFlLgSA!t9bM?DwV4YMz#ArPid=6ljJB&75(YrYM3S$J21 zC8~(=-_93b6L$CTZS{#sV{xG^?`Ko}j0xQ*&?x3z$-WmC5F0Sq^h&&|x1SNH6G3fYJRZ150y`&K}ix}nKd=>5(}G-X!CM^a~% zg(vP@NRNf9i2qleT+s0WoDqPz4zmX+m6`IOY;K;A?*765e`DPmxaDMJeSyT)JAw=- z<%UdedQ8`@cNsq>&Kl4dc!KGlRRye!K_ZYRjd}VRpwq6eZxZuT`w5!mi;Fo&+dThD z+rDi&4c_aSTDt_eLW%@{7eW>|2OysR*~TlieS@rRW@UhG_M+p`U1%$qj*ZH;UZ2Wb zKc)76QTC^ALPEpvxn>DbBik+y=2Q)3L!aqxqUZv6Ld>mrqbxIig#piE0#p|Cf_=cR zGW+A%2^7ob9{#Uoc|Lpp8b*Kbp?Lq8Syb z!VQ-FGZzRDcXA!-L9q=D-0kh{e}H8qBKjftK7TMW{dZUp2{ZGD!^1-tK3$((=4UsA z^v8dQ2iq8~{|^EFUn%o{%ooWLK#Q92tSmC1UhxmGR97eyh$$&4$5&S( zz%k}7V;q7A4gPnC;@O9SrR$T&y;TE*Pn?~d1AqSXs;}qr_V!*rI5795mRkUFJ$%N; z6-DWUgoSriP=H_e{XhR24hiqemoG1X41|3KVrUYO7ZRD05{B{V+jbXVOLfAG{A6VN z$N3jh#;=&t?kAz6qf^$QGtyWk^v1ojwXIEX$Y!!@?EXV0yNhfRRIB48^s3aK;)L0U5Bpg}VOz#eb%_R0Dh>f3FOhw!aRNzc)~u zf82D>HyJct{~Ny8DhFDwtp44iDM_uZKtuz5Cp3?ywhl-y0H0D_`Zv=s0xQ`e8TWUx7$1FT0_7KE6+5h{RH1vi9Kg-8AuSfip z7HAOuZH5A3>8}I!d16id_OID?;$?<$|En4L---W^fmw+}3;&bV81w0?`gEF$x}I(=**X^mVkvLX9rt=Mz9+s-5Bt_JLc~AwDo~; zPRnIdvCV1{{M3@_NznlB#xRk40z;SpLjBvLGq4&NW-B`gKK-! z5m})G!SIR0sn4CUM|Dt+Z!=#zzJhROfJF^QRZ7i%rqAV+qBk_e7|K=nZu+`Ix5llV zNPWB`UmYby&cd?OSN)&d`%5CwJr}1$nseQf1{~%+Yu9ZjPF%ae zPDYyg)pF9qaSeHVw;?s}9b<~>2S+2r4Rt!V)K(@fVN;r(Y}D6HJ-wmvXHl=;irgn> zWPVF;Q~!o~9%r7uIVv*gtMYoCZU6uXy|iTP-OisHF9KWK@zjf?ihewxLZ) zwI4W23BlhffJq6Od9Bf5c}u4s69&b;SoV+WXP-N`W8R%1Q3fa4sIP2d_#HLP>Ug@5 zFeN;!$gj^5LWYgqtI@eMAtC83e1C#`zt|CQA|mgo;D$6Eyi%N0oRL$iWRj{(vfs*0 z068HPJ8vKhxqkk-Gy=U}^0YiD&p&NdDSi|e1Ldh?(xho0ulxR5!ou$0ysBa~1%75Kr65uesSTl4F zMVz@9seRJB7l}U!9_f$phQMoewP`62_m~c}4iX-A4L41#K_IG^5XJiJ)ge_~tCK8| zla5Za&->%Z3gAqYUgeS;3(>_3T1}Vrxn69k1HSi`MYDnCK%}Dzh%Agbn~QuM=o)ej zLa{zjb8_4iQh(aE-?sFGnv~*$jpx-Pucn@~4|{OGmePA7Ybj#A4Yt1W4LH`dBl4fu zGxb~meSRw@kS zR47dnU)w~yrk^8ppu23hFv|7?xo(R@f;$d+Po(9Rj_sNO4TFYH?U{Hr(BqF`(Mi9@ z_#v#K<2K7=B2PMhKy_6ht)cEe5N(euZ7qwf$vV&FJd-o`O3UYEsc7R$7IVav zU%XNzUaXsGs`q_xwi9T6DLhBdm8vAj=sYS^xe!FTbdS(n^7vs4_jN$C%R@};Od{w} zV`|k4Q?qSOYp{*4AbzduuD*rVn&~j@RxGnp;vSX${qyI;ULcQBeuLFUggS-5%C3 zoV0I^9Pd6M#4$W!q&8R~7f+R4;_xP(!FkjbR6h8{=F;f?GGSOSw!7e(_9AeyIutK_ z+F)t;v~gAEdK?u~;Hj+pi1vD<`@5$P*42BZ0cUh%b;IzP#37wWhO5j1HH1itx8!-)^AKyKVgyd7Y!8G zXhu{Fy$`vJbbWFvQsXpPZ0S005LZ zM{QDN_nqYmXa8opYhoR4=GK^7S`sa2VelIHV9=~9>o|n-^^`Ky6B2a$Tf-W{^zzejKH;l}+UM)nE8+Hfq*c!{v;y{ji zIKHL){I;Y!G$L2+__v%efUJA>s7m5^(UeR3SK=*mm+qe8yammopTvGRz59M#-JWkD zhNke7#u3?3h5z0sHfH(I=E9(je7fwzpsYUhC)g!be)nh7QHINKkHc(Aax05)m|>19 zelTa;@dt4d%Md^5rMBX=s2#^#LoS5?lOY@TH}P)+Btxl3nSczC8}sNW&a488dV30t z)}z5MnoG80eXPe)$6Qacz`q!;sr(>84`^T@%C;px1G+h`o!9Yu-|Gaf@z3;r*!I}X zmWh%6;y}xKT8+al5lR z2Ari1q9f%9ne>WdE9mY`rhyF^gYgYu?`3Uel(65N0j-l|VUf}VMN9D9gv8brQO!hb z_EoEfoCj4s7j(OyDVN^8yNlUQ_B*C2V($DFx#u778jKFeynZq3RcKLd<-Ao+qf^h% zCr(}em+KbYf(cpV`Jt(kvg2)2lBGRwOGCf3e%Vo1as@@lyHLgH8Q!CY3 zt6Tq~L2smP%GzoLt{+$eD(wEs-^s>n7wXjvY?R#)T*AGR^tfI<=n0?wUG=+Ej;Qo; zDOsvLt6h(6OFKFv{QCBDl6?OVrJdXTMy}1y`;b_@h&hBbnFlN`BEm{pX4>TH9&}Kt320EF zxInBr!gTSTzV3BZ;;>4!*4+#3&JO^0kkwjBs3+j*G4(F@4)XGv&q>QAq@3mzbx19} zZt_tcZAv`iYj>NIZ0yalz3@NM^SsrK))>KkOS2ps_2k-mslqQmI}_*_agc4sKVTg* zG7*0tkPq46E+K#CxA8Xv!P>SH?jNwxD)wufX+L?yNJ*-=@(Rm0*lMCp@QmN8r3hEO zj4&Fx7hG=!`Bl>Wkl3JGzs35c%iSNbI|0jBvu3wlShF1dIia92-yvTBK8Sj-viR+t_(Z8zNwVHz*xlIhcC!oeLHfT8-C@ZGr&_Rm-p^oxN6}J z-ILVoOY={qAv%EAQN-jRIEqs|DqtopUHCBjNAgy-tF%V{CUV-Xb*$;J~<65nCh~yu|I&MsN55B~&VJGso<6k%Q4%2zS(_ zSqwgaX1(^&{Du&0C@xJ!Z}J%xM2pX)mFD;$qFeT$UHey76C)8* zMhprg)a!TdnjRw63+B;1;}jX(Y*}%a{k%9uB6OG;adpu3!PTpW8T}X{H$`+5B-d{` zjk^XTE&PMA{@F2m-@Sp!Ia68=+zE^aDSgnwcnD(xE$2>kC(Ol(U|k}lxDivv$C`N5 zn_w&XEYjN?O3Dpy&{pc&ZVfU-DqMs*p77$UKh$+a%&d7Tn{pfmU z0PDTZzY?px7pwTP*j+o^dB96j%V?m~IKVIN9S;4(e5{_~lk5vR#ha_xI!T3U%WKZC z<;%r|x!<*W29-A#+hEL_t(!E8n1L7Un$!(+ahPr%Ef5ChO!H+IpD}v;&OCh_<`Azr z!Ghh2N7ph>NvhC~HRU8NRw1arp69}c{Q$&5mDOrl+V_d0rpwgt^Rb7nJuK%Se*wes zpeN1y=;nVIV>GDO*GUzKCM?=2Mr!Zhh5N1H}~5=JoHP4{&Zad-FkMEfV&9#=HocUwUf z6vQg_g~&Cs??Ie?iORIF;OE=kMd3}5RG!vPs}GO!GPwP^Lsw#~%&P{*L}aHFHdVpk zKXE_lb!5D&`*xux-nr9Bt_L%E2Bh%nDvgPuNxJe~c#yVZ=s3H>$bQ&EF>XidplhUy~QRne$^gtO?%ek9_YCO4I+5vHoTABb{<-HC3S+IT=b}gSA?`8 zQHVZfqAY2Oxa0E%kkUYP`dKp_uB{v2i#XU9(lp|?B$7KXlJ)rm0((Y7nw3@3Clsm>=Aa)L5|n63Xh)#l9c2aZt%`b<#ZPtfuajvyY(H2XG`bU*-d%Nv~fP1 zkq3E(O>)rA;0>)b?fvo5%G^Khn|(dfacARAX4DKIH5fOL-($#fCH3eyL$(Sc z6ry4*x7e9r=oX3yj(VO0{P=0E+BB?t8hB-TF9^9Qa&I5l@#3iA-dTq&@NKK}t5SX9 zZFC8S0vb1tp9;Yd)YP+d>I!bG)J-$2GJ8Q4+&14ZHk6I-`Y&0Pa9 zYk5;FCiW$~e4z||lfvbwvRy^(zq1(RIoJ@r`@-sBpqX7Rf%dGCiV=)g^1gSZu-+$C zBlARDaK6|b@8A$8bXIt|O{e>%yo!ktp)WxuB>H0I>^ z;b5uFf|_QRov*;QhMz8rr2^ln13cBoRxt}f>5v0WEq8y~JHN|hYD<%0+DP2y&RB?{ z;4i3NCX3^trgrbI*%^G3g>|8e6jSFuIb}2V3+pBQX1e&V(;s(VUHiOunQ?W=bZrCC z&G8+WUEpGZ<~v(%rdh#1Cz-PkKG^(WZ~^Gj4dkZ_bC~^oFj!5VC5*VM!Gcv!=1FB0 zP7nKjgkodR;Bf|#Rr;+M=63z!?XdswP=cew=BF9sGeM_isPXOk&t1wNpI_!eBDpN? zn4rOfn`lAzu(N+lZROMWV(IFpb6<(_Dwoq{ABcb`cCJ{gYaKlI@*|2p~`z!LaG%5?o)xZVS(X zkL73=VU!D9_@h5FuIx{qumnFJXsVU}8B^D#ksH9D`jf!-wq1qM!_;<-9Od04l3+>t zw&{}-!tawJ>!#mrE8{VIvGh+0{%xSP;Cr=HD8rea&U5NB1lH+B@2LNv_(X`Hso!3F zHGv>E8ysX;9x1h%ULJa9yeuWxVrQo}kmYv_j1g2d7hm5}%H>^emu17>jAoYWZ8&~* zfVt|V9OIP_GI%_rpmegq%zHAJ*=%EFeD6u8$bGgO?Vf#U%}X0mdg+Rd1q0LJHh29) zvp>3RnN1rAYWu+AgPCGi`%yWcb$tdSkZ5%`^R%-h#l%D6pmS;2&iWcVT8YU5FH`^W zfyo~!_R=1QN_|KN=91Swre_IG)jt59ca=PT#7(c8Yh9c6FxvWx$GP3%9@ny#R_{CT z6b=X1Q`?WaiHt_n%AbBdd=7pE&8$))6Cd0dF+JW!(SH&%$_WHgpBZ+@1TY+#Qhfd( zZx)C5x5}ZFCbz3yqe>wvC6Snag^8ze$a7COS^?;=R%m(Gop6|08#F3!e9|wwOW#Nb zU5mcR9<4O+v9Pz9`sNBg+DpKUgJ?ozJ-k$E3&c@vx?Fbs3F2Jq8&fZi4-(sx;JVEB zOIH(unA;e87q-gf#w7@@7~Mfk@jX3GV>!+c+DITTF#qRf?94v0{LkT0vuavkr_22B zMxT(7yPrSJ>rwS&H6lbqHz;h#MJ`Rp?&Z38A~XiEiJ1XPyBq|<+#h!O6itoZ1Rs)S zgaX$Hf-OY)Cch|zw{`#YDAb^J(L1{rh!pgBZwocJnLe2qp*HT6vuK19*m^h0(SjAF z_FXFli3k5=GCH~iuU9-x%eZEx)&1uGeevJUv74arcQlF2q3?3rYrfk0y>tA_2M+8t zH41)zeC70=z8K;Nx(0eHWCx7tSME0^Pbv0KiNmV8=V8O-&!sT}Nv?SJ&;aldf|FkHcl zMfSgvr@SOL+V43f$uae~iBNeQR!mo?J)lvUe#W#K(%m%I|3!?ti3-f!RJi-#mTPS( zzpmv}{K~b~Omcy;*FlT-MPOkiZLox^RM*hcnC2%T@dP7U&9(1Cg)jA&pYU`JotV>l zO*8bxfI#M-ymd_QySpj~iv;|ss$HKe_I=M3T3$UrJ4*_!edcYPA;G=Ku&fs@~jHG>Tw+}QgaVXIE*#)Pqp<5zjeiqSy zyK#l&eapTT4z{iXww|Wo@XL|7E(zDD0;FiZD*wC1`y-r4)U~lA1TmSHBUMIq^sDV{ ztO7XZwB3^81IYVb0>gIj$=M#CZHZ!h$>umk4$mTx){?Ll{@$9JS{!sFlxc93MUu0m1pJvWWc%?n*=;W)zD7J zUVYf(&iz3xjWLd3f3SJQmmrXi{PP?t#h&cA-c0lnPZP}DCU+$vaPqrnWU#sq9jg;; z0B|HIEG{OGHvFm4NOioT!!geQUR_8iNEWfVGM~?n2;j~M41D=9d~m(ZnoFb0wNi7n z2M}~Cbd*sleEV51<#sZ}uHQ9(I+lS9fJ!D-&@n$FX3EZtY!aX7vqE*9X$q)=#07$a zsXx-|^zQpHK|}%|6u|We{;Sj8>{6eD=YW(Iwg@nx>r~yJFvJ}4?AUZMq{Pc5ijjsu z;L!KU_;UgerVR5yY|P*{EiLvLX1E1UM^q&r@m~f)eTm!{nTU&SBk*|4Yxu{^r}i}j zODs9Kyi9Vl#An;|M#QR3sh4!qviwKBYJ>`sM9KA@ly1WMK|=Uo#mDXTMAIv6`5<*A zSpLh%;VC4~wqwchk8piApH<*3X2p3-!eIl-+lC%A_$H8e25-~|iRB3Er`r7!gIZ1*Z$^|umH_<6AJgMV@d zpBUxJ`>|Q*UYic3t6vcDC~u@XmgcKt-pPo$#JMWyq2FDf!9tVL=JQ4?=XK-VFodv` zMW%&o+$#7AJzMEYEWshsjGTYkjrhTJ5tHFj$$aqRpp%=wX&Z2pYUoJv_gEvuj6Kk| zQZQ0Ru42ojfsmNNR!xy6`;LUd)A|b$EP1`H4Zx(o63Euk= z_fyHP^sw}+?9VszF4sH}v^bI|0p@*6(L7ErcF|n#v6YS)*C^!m^zmciBDl^Hxa!+Q@>M=$J$nxK{KA$D+yAHjF8g!?mRl8uTyuBtF7&`njHP{ z5Wdv}6i4B7Oc?FM}CNiXt`rSi`22!AAMTyh>JL1oJgC_QE2eXL| z1Z9%8(UYddmY%THSo(v*oQYh$T%QT;=C9}^CRe%ilK&U?ta3e<<(&F*wrLE#P9 zaOrU)B7B)y%oEaPb-q9ea+p_7D+5XI`1}N8%W7bQcRZhXA2Eoktli#>! zZ*N-`bY1ps&&eBgybr=-ROx*gg~IrQm|(_Sa&a)SF-Bkp@+uqBEq^=dWpx}Iwh!=y9(+w^2kpGkm+4FQoIDS;h^tr<5~N2FOi0?UE|7>+WQomX4D)dn z56bekkqGZM=X!Z7XswsXH-bdo>=d5n?`v#Jb%0^Q_EiG6QMR;KBbWXiv0!2C{Mk^Z zWBD5zxyh4ThShi9gwA76`A(!L^>zpW=4yj`+|qw8yLOQ9xJYSjUkBO9_@ zL>PNHfd@c*1m=p&ci_5klWO+J`1Jl!t$5*WX~$=>Y6Rg#i^`499q>flxatN6rd1KL z&s4*x;y6f_T*Y)xaDuhv@J7!Mqs*8pJV79&lhzm`25l&OK|RS%wM~*BwP8D!Nb+l! zlHrV3s2K)^Z_ zlC*jtr6nl-Fs~AO09_~3u0E%sFYXG4gRbmM>1E_DJ zn4RO_a?LYwFQ>Jo2_){*K{lIztHA34YtzYb|8#%<4HPZK{EtTPpH5K!@Be5l{{5|NknsQhqh(TZR>LgxYnjZyIkx|IunGHu zVQl{i1^@p{pOr0X_QQ=z3t=keQ%$zww~k3bzv6%2wj7b(I*CdD_Zqvm*8hYgQzrF( z8{T~N=6{%hs%8<=|As!sF4A(s*_bE#;wGAVe!_@lfOmgehUPI3{+rGr8TdCGRp3QE zN&g}@kU^*7@c->~=gP;NbOJUo{f}yvECaVmZH{g`pA$pynLB)VIW5JB#4qaQ4GhFU zkrjfSgouM$dCq?e1b*<}^p%v+D!;JHw6c~V6nQBd)%>p`UG*;drPq-HS+(-*&97O1 z2lS$^X8)Q~LEHPe$F8o@V2cAz7pd>j8noyC#4pPFVgD`YP6S2BVafU^!VO^BOm%yF z>%veg@8wYUwSSf6zc-Bd?;&{0z5EdgjSRd;fbDH$0dpPt^8*ssPgy&s6z1~b*cMBv zOk`_RVq)13dA+%^KnC8w-LORHd}Z7HGi1FX`z@N)t<{@c0B&PC3jWs*@>*S=*2BB( zNLYur(h(gbW*>z3RL<-e*s(ec`N`F3tjNbKCcDCBeE;k(xCY_=ThA$z|FoS`0rq{z z3M+pOEnJqYplpm9|E4rNbp#aO*7JHmDgizd#ZDgH6ik^O{BjLs<#4IjbLCGHQ&02$ z`gc0a&ghtjSDpG#NVhm_0~XwNqxKRUjCwtjKm6`w?Cq1sXF!SbzzUk30_0 zJq4i)m1UcnytwyoH45tg=b4`UG_e(}fz4iYga^qbs)m1n^nKGg>PFTv&HB_IuLvd< zCcYPx3v`SKF)6gD(>Cyf2JQRJe=vNV<1PGj&oO-RZoSo55@E7jbqR~{1GNxke~l=X z=a-F&n9WwnYYz5Jyn%O#za#ti?nA!E&7GnD?ZnFeMkaTIH8m6>(N$NM?Uv-R0_kFf zvT+x{cmKD3HM6;77R6QN_I4XhQyA3JCj0RrflJW8SNHg_iBM{` zH7*Yb`?m>|XVd;uJxTi>IAK*!Vs6V$5P-g{_daJVVQ%OD1Q`5T&RGtD)6>&~OXu)T z|A0UGV*{dNrom)OOP5lb<{kv9vUP-*c~F!*;=|8N{-7E%2Lb1JfFUsQgHEeNvKcPI zv-04M*cT!`*E9;gK>9P#2g*gu%l`;vfm+yeSWF1zoVAn_-Gx#)?RvAy=sH!m81TjV8oHRNOswTKFp<&~Ag%l`y>@e8R~ z{WW$pw8Ja<-yq>Luq#hR9Sit1mfw|>?4w~)gNTaqJoDoH`dF9*hV~|tTA&H769~ZR zJTeK(`v4hmCrdRk0%s9Q6=SE~b|2+F)9D122SUPq&@X63fK1V^iu!@W27F_?Gn9_ronvE3z5@iWIiq#YB)BgALy$Sy}n(f`Kb(ZFIQ#uEooWW{r zOl5+Xw0goVZzEG+W3P;`vvV2`tg)xzT5m=tIn^b>>MR0CDj19spUyOo+5=*9rTqVU z9?FAWcV7NK2cl6uYWQcnS#;(KRgfuiX zD2sw=W5+C}Xs&3sfwzZ)N&l(bm6dnYqzadpm%ShJKWcPQ_V!(9HTC-+{VeuSVfc9y z#36URt5j4{vitd+7@Z^sJw?^X9m4g4mJinFk6JYAvA?-MWYkraTt+ac`4A7=9(ArS6vAe^d{rCE5 zrdVX|{eIV=`qqXJJD9BhSY99X?@NpmNsh8Q;{9#P%L{dUhz$s3bJ(9~SZ-pX+gTq& ztcsQkUqK>6e{L*|?Hu}YqtVDh^?Y54gRQg(=v{UA?3#G-i}QVe41=_C&RMQH7cWbbL`0+QV8jHLi z@Nc!YCnsFFR%>5bYs$p5Z8Ufi`UalCk11SMnn-ddFb~9G9*j9bHxM;-?dd}{Kp3z| zy(u`-MR|5*@4FWuhb169uu+32=44Dy$9nlnv-bUt@2!wKmOk&Y837h#>c@Tp_06HI z%y1=L)G=h|F18)$j9oySAQc+Uot?N>j!{AUUQEl=kg8+HYuR zX!37Ykr$j;+I|OpPMIobDS&GSQ_`1}9BK7%yOj3Y!WkYOeqQq=4{IAiD;z9=jm*4Z zN{YgM>-YNl`}YESPC|0Fsu-8u+7Fvbd;Z|o&*?>UsF7Ycdp%J0`TKAPj*}aWjByj~Pw0(QZpUQ7uwG&Zo(MTvihQftmj$dNlVYo<-pox_STK?%W zYdeTiUYbP)>eRl_;2bs!I-*%6`~C<~mM>sRN!}mU4(z(#krEk}||7hT~g1t#+uy4|$cj4X6eyhFMfD&&=qh=>pm< z)dKt)Q1*8NB=ogb-S>#;M2uaPycx4ovf~@+#aP(*8N>03p9etBc7#A>t#Iwq@10gs zH18IbeYk7JaoICB#SPP7MrlIb64GxCnS-z74&)kWxED>-e*gZI*-c&Lq4Xw6@OC=> zSavIh-?Z5V+2eDybtm_$8iEePqI=6AXAcZ&TYf>I^jM8W+c(cDr=onL6`Q6iZ1y;7 zc<{ZghFv${de-O!ba%-p=DPHGXh@Z3xN1V|4T%xWrs5KRPZBNa=B6NtXQB|0IIx!A zOV02(%q*6UfQ(u7pGA;Di0BQu;ZL(q3+ZeRiHVi1JUrA}(4VU$*3dwIR>DfJUM!g1ylL$%b;cQ@ zog7k8WfSJPT@(y{-h;(3hsX8Ecne%8LS4kEX#6(|mP?$RNEFnpL!E0|Sr*evSFM!; zJ@FrZqpStN`u6P`Rvso-X>l!HaB=i$%4u>YMT=8!c&F^{ zdvMi5Lc$&_v1_u}E66P1?<`NqJ>)*agdWmh-e$9lfaw==Y)J3lWUfNJQf-M~iKkc; z!N3iY{vYvnq2c`tkr&exzRx5;fW#*zjTqvHq)Lv*3EEphcMY{fFIKl(*MAdm!tom&MPWjGVTfF|;dqg3%W z8s>7a(s}=b0ZyeUii{M%sOVc|l7QgAn^WN!O$ggt9sh5RIJ zk!UU3@Bt;^6b>%jylUv77`8v(-P&u`Uk znIH|uZjk$?)2tlTUM}N_{ljv(0(?!yE`MigC8a$7`p?uTD4ie85~Nwm(YZn!duxl7 z50=_#P9B{MjsEVZNTIBJqu#1@Mnp&$S)FUCZjvUYZ=;SCQSyxxLw)gjuygcaqV1>Z&Lw-&Z`nR~H^(>5+f)V&Y z+V7|@i4Ab>b#2ItMe>E&k75)x6d%{w5mriJXv!5Wt3%Rx&fMULTHLX18HTu}YKvwn zOAzv<+}-U9KC_lLoqoJnpLDzB=mfSxS&&CxvBwtKIBNW}S2jCsxZ}7zmL=2ADDB%I zUx@{??GlnDO9vY$VcFQ^N!Iy7G*oQ-(qtsP^aTNBU{ImD8c<7ii8iY4w+FvKSb4NO)6!K&$WH?f22Uv2 z=Fk#a&7xUz1r}Ra{%FBbtvj%}&_fr}UG~(VN&qfS^@MHWM|DVIp)Qv_Lm4a&_g(Y* z*eY{D#WVr)e@L7M1W~&aew-jCD2kRQCBJUtnV^p92!#Q&h|ikYpINER&P$ly;JJ+V z!6d$17w(rYE|p7fNX~_gO6(=1Pz*k?XSK#hnkW^i91E<+z0BEIy%VgKQbtB*_C)64 zUF+eKm7_;Q6`&Cib9wldG()!WN0_dlq!`vf{H1_)RAF4IcL&&Y<;Le~ZzuN{RnY$` zgeeZpzkRqD!5L3qc(b{HTni59B|4WNBaW*WH4b}l3EG-aH@oi^5Df) z{R5q3<2}o{G{1VS_B)cqy1Hjcx>>uM#-CZbA1u_~f1N3+x>@quU}e3k^zv}AlM;A? z1o--<5=P6oK-=i{-oLMThlMrefJ=KdOsY%akIJQE^s}GUavW;KN)ojGLo>AAbXK|H zi@N=JxYFjMl*lBjPdY_?xYVfh`0m8=C_HP~|vZk{+;(t?TH zKlz=7SVPc!QQ&E{Y`?HfUw*wi-%N(ZbAJj%K12GA26?~9wcf$aW)dsPqDQ_R*I8ZP zdp=l;BC|OjTHj@yVz`r}io4&6H}nSa`xFl@D~TU|L-H#5$!mQ)@7wxQ5icyQ zQ=%k2`{l#EO?((7U`(2 zp)v5O>^U$$0qg&gRU=RkGn=7Zj@uixZhRn|Zje7QJg#aT(#6B40>=GIDsSoj;D&%j znQ>C--u?Cc{c4)0e0+61yGP+Qk?T&ygf4cQCE!m~Rnm1?>iJ#*m5v%ut{B8z9;hZT zMT0Qri9exD&xBZTln6)$rWR-qlH9 zNbBtrLz$hDDzPF`Cb@^qew1Nh`@L2?vf2GpdDUfEqa1PO`WezKo-b6*#i-LFn)>3) zaliZ?QoXnAA;C_6x6?#WyXepm?Gi0uE@izI`l;n=!{Up}5^WV0hkrGOCEItB-^kxp z>5)_+vGiHm)MbWi+&8VH#PA?919MhJ{r-qIp3V^V%Geunq{5Ma>I13()(&-BZ}=jK zpPWBg*uCSI!BsdocjxNL3i{#ajx(+Qn;1#~KX#;u4S)?Q&i}9_50khq`5LM#2rbJl zTN{#KHhL5pB!g*RXNsvLb0+Ps{;UX;duR==Wny3;yu&d`eI=En^(8*O-?e$Qq*}`X z>o}SqwK^(FoSj}~*Q-{j1}30lJVuF*MqNX>LGB|L8Y+fC$Q(ODukpBk9T*m5G{;N-CGt|mG@n>ecp z8WfYY1+| z?7i5lZhn-|`)T&Aot^zaFdsm6pUXFv)nqd2@dJF1L( z-uIqmn@mrA@b)sG{>2x^ApE!r9`eZGeWh_7ae1m%C(Fjb@64|Z+4y8ees*jsoq2Ei ztF#baDjuK2dny$k@*Nw=m*MET?G1S~7hLwq`J+>sv5xWBg7tq%dFh0%Ble2A{^LV+ ze?D6j#qLf8ps>QXP0EF)g0}hD3pOP#qUn69r%#Ijn%8AT&z`f;#%C&9U9JK>g#E$n z-CxRpL9-_&988?|d+q_J%1l0pewcck+3YeEo5w8W{qO{iBGfm1E$~y2&B5HDU^-8s zBrpgB zNRM9!5X^Cu{@eC|k#vvmFEn%TcnvY}lJaA^0p0If-Vq5o@3NTr^i$n-ZZq?(G(5`K zGkN%kxI2$kN!ZCsURhNcy!k`ja^3y+kC!~PzJtf>z2`fR2LG6}3BljF44uq+#>vi} z=vkzEO%J893{jDNt~}%L+Y<{Y?)(yP-mMg9@>MY<+|J%{|3>wrkD$Bz3NA-3pHFz^ z4Jr5LX|}4tpxR4TM(elRe|sEGp8_0{w@yBmbH2~i;QHQgqhO4_*E9Zdr%(pbVCk9MDZq6qCmmTKAdOUD{A#Y-C49Cs z53}CjpJ#zOYG`nIx624wS@7_1bs&xMFYKG&GyF4wuFP9Gc8R@zG>Lj^N=$l;z zu-M;F5_)kK4msRa9*cK=@VKHaebI*>aG0sdT6rvjJAw@`Yhg-J7n^EYaW@RRKHgPr zry40PeuCFpzI7I~aq*jj1_Xfdz=G2&NSZ~DrXb`&^6;0fK08@+9o3zYM$$vR8$w3)R#!*Cc)T2 z+H;JhhS7|NC zsGy>vlD;*`tRq{;a;J|iFN?$j68e{_g#+~LB3gX5v%%kX z@r|h2rLh)e;Wx;W>jNF1s1#zQ^EV1KPaNJ-$BE{UI8D{De)J*~0%25FJU%SE!E)eYb;aN@2C#?J|D@}7CSNDcIt~*`}D6bP$?_p|LUD38=O9!EI(6e*{?!TkUuGK zF53L-4U2L0KTjn%nVq{n_DHjC9?Tv6Ew=fqD%|)Ef$w74N*OeF7+G5B_%-xjCWX_z(YPibY<3JX~m{Bq%7HVt;n;5Zb7usCW-+lyZS z{E%SwaJm?@hyEC8%9w@Zq-mX;5yg7eiW2SlfAcA7^F0cktP$dScV5~>sORh)b3`5+ z=MoqC>s4o$T8>HrZ$Gv$HaGjZ@K8_REDvh_v6yqO?Ay~k-NJ|1>Wu!1^<0DcixmrK zqZdur9fqK&w6wz4AgB|^jXot<&2*x7rHM0@p*?|77QAG(E=5<6Y-^^^v@_1Omxp#6 zXXwCeXG-|5;oMvvk?2qZRHZ%hs)C=!#ba$?n!`5>=s^7DF}1dXaAf|Ue2c`;ZzkH)>qTB>)J!5a zhN?4dt?DMJqykW2K<5a~`R0ZV_r9Bydm01m_gcwlS8(ap@+r^_j@t6X`g_iPsQjWg zh~DOZ2T#(^$e_DHbr_5V1O~E5a;y6}2LuFm8_fb{sxu?Gjm)g?kltI{}NA0LH0h`gd5dy3T1JkcXV0#qRQ z^i6baqPpPFVxhj~YMh7j$S zOGP!(A*Fl=2j$6UK-4DoqA%@_YO$I)x-Zm%$_LDA-^9EW>^P1iq-93ZCr(79$X8nx zdwuk2t^0caXv!9V9P0jU+RWm-!#ewpTT$yBpUC4ntMQ=-la;`LALMI$kC>CCdt~6< zZ`NY&hznYo4^gyfY`hvEKRJq*)K&A5m zLeKRlb}wum=Z9k%=`qWdTEHx$A=>k>aY>WK7Nt9y}Nzz zWi@Wk0{rAcYmNy6?^mhZAl)cZ>-j2KV~FQ{Y<&(q@dgX~*-hYi^uUC^8#Y0cv6z7^ z74f%u!^^@3EOYIXx&6E3FoCbZoUebBM`|l|E|i9gMy3i_CLrm>HZ18~;ksD*RMoU5Xj6vSiz@KR@XI{gCA zGKu@G&X%8ySi@w));3&26Q`IJRDOG$WzQG*REw3-EWWxUrS8A5wBJ`thIL*flK6kv zMmcR*t2#Rfoa%GfANvhfe2`OpqvoL=JhS+}*Hy5YJyb72Ed5NlrJzl7n;tTQHIJbTl|^0H6fX&W*rbAQ6~3xw{9FAPzx7$NnZR=Is9s~lAHOn0s;(Bi z;sD0+GP2=L|7X7MNCRoBRGt}oVN2O7E@qL4-q~Gz_g3e;{(_NkT{N9bVa1j|2bZE< zLkhd>x6fuEG}0eF%%wTLrq_=x6e9j&a|O*epJa`lUB z>X+Y{V#AYM5AYuBMPz!N66$|6W8T|)ul9Qg>o{A@Wbn5-C+#CYO<-H_DZ3V}e3xA( zL$+1LRZ=>2g1=9KEr4$(!`3CM6Lkb(#?~BggKuy3ul;6TWE6#~l`N>ct-(2wp@;tb z%^$N;6IcO@Dd*h=XdR5Z#|wBJf3|+0eo72iK)m-WYB25B%^<7;1M(3e-Fx2sg`1lz zRv`)6#gC`Tf0<9;nM7&K$$XD3H}UDTQa1E0>GFs&W4wjBcc~T*4Sidnpq~BYW0K9t z?IWc@Sp8PFJ1LBol0N(pYY&(Un%h7z@38R70;q?ywoLhVms`WZIDdf1w!F1hqVJbz zk%~&lS&UhX`8t9=kI;#&5hD3aU%_>?K&2d!q_Sf7rK&kGtqUGEmX?zA%t))1P%{l( ze6R2!3H={{^SftnzkPcnU%+~+xZnH3R@KlM{~7)4;1`P5Zy**zW@h&dtk}yD`|;Mj zT&(O1zA18sc+QzF>%zy8+c)bTwY8yf42C4)rbLHI)3=@#r3<9tgSem^&XsfZNWWAK=ZvS=puuUK8>oXBuFaY<57JHyNPJHU z@3S#ry~088-#yJ=w{Cl8lOZ3JLQO+2?$+@Ck#rVpZFb!fZYl2WQoOhpm*NgZg1Z;j z;!vF8F2y|rD-@Sdin|7PcMmQn@A-a0u4_M8d#{9p+e)&n>V}|PhzVQrMC)2N%rIF=bnJmMz{b7Lu+b_m{JWTKe{urN!rfwy5)I8 zeQty63{gG`R>EIKs-P+kia=0`peM<8(Um;=<^6WTb_|D@6j)|Xm#5fj`PR3jATkn# z@(j`2r?qm08WGlkBqWQwy*X1ckF)IpCj@e9`{?#U$?LZ`TaHlrd7HbrwQ8@k&txNwi$@ z3JO#9B!obV2C>&=;WDj%Rh06%rYx0iTmR~A3sS$4=+S$Ii*P`rswn6^!$i_Ob9;^; zI?&+!8es7}~Xk7hBxqKE@%6oZuGQnx~&wEvqsYkaITYyxuToaibL5@!lS-6Xlk)MnR z>m=^rWy)9A#H^6OsO`JtN}V@(T%6R)quIrb4VS-%S;nE4M7IPoCMs;HMD+D*$ysE< zx0Jmohgo8no8e2bY}`q{@e+Y%O)m#p;8YxcYe&6TLHi@vzl5&zBV$ihV8;94_rXW} z`q*on?}8=3M_>-P4`{G*!-%czW}$Gj(TdAp>JQ9x zZGjDva9s;lnrkwe2~ibMogY>Hf5bj)o*iL^P9b3^!{{4ESO_XTI8()F@pP1Zb)p87 zEa3MV+wPO)gFmc3{iP)n*GZH4{Tg?*3`xi+?qYu{kA44O!L7{uKZ|6V(zso%{vUn% zSqdmUcvqm)b%x>Qa7M0Hm_E{lgpIz0R#7O$vNt8?RGTe3*`nPco`vhM2V7}rq41(c z&te^;IR5g%BOvit%@HxwxV5T9DUSdQUi0-$F`SSRD*F1#UUQMK|8raRO%%%^y7$&k z#>sZC^W?{iRHy<)PYY0l8+SWvKVMG6oMu{6GbuB#rk0bWIQep;z?4{PF+GNjpqoIr z$x-6&OcaNVqI9?`2K@VnAR|(ptHR*$*vxUs5D-(Lzw~8K$ zu5!jvqo>vIQQf@&0SkM}<1kB7W@=Q_h0FcZvb7&)zJPmd+;Cdyu$R96FXr+~p?z^& zve3{-%uUl-2|8e=f`aN4BmRn|x;xaj0!8ceogRmx?5LuJN!k11S;yAj5wX$9Xm*4D z_H)}kLIriM68tNSJjzpNrLy$&Ks6fB@aExDz_X8ErJ=BZZtXI|2L)Wn-PuyB)b2G* z)~IQpstFJV)Dr)8AHd}OHjNK^qJl0I1LMg@T4AX3|IT~Z2!q*Ba@WP*mpk(>Z-H$; z21Bd}1l6~TP9$_FkKuI`6jlEva!FwaY;Z=SMFWx@A>kZM)G8ccl0{&lF*=(7uP)M$ z*pg%=qDrh_RuF?G((`iw6koK0e8n*K;^Icu(wnxyaYmJB1ql4~+<3zguuGz+rIPHP z(#{Be>8hRVG~mBEQ<@B zeZ#fF=c_1Gwo^yGUiyKBLtM>ES>dXHs?w5vAQ_Dca)zq$vuRd<8>~3*cqFPJH#mr! zb!Az}60L3vST8;cym)>3uk__qj`@mmU{C(ja1-M(YJcBDVRSnCpXsTFz4f5Wr(}BF zPCJY$=B%bnfLmTpRKJS;-=2acr87#n@2<8)KaDT6#^5q@|2vwF?C|dJV2;o)_do;9BRdO(-1BV-`h^#6(qBlWeIzr`^ zzAYU(LUlVwk8Yn`hURB@rgxD3s-*cSIbp(;fWLaiE605%-9Zfmom4*dmt6Vk%`Ya~ zRNB+f?rsr-ndk5Ryu^nh-GuY$H#)>=SL+wK52(VNI!vT|4!X>FVYGR6?XWGQr+RZt zo!=dMz&A4zR7zaju*(eNXzQ`;!x5Zzzd^VlMlo+8QXXl&UOkmV3KPS7KNkz89`p)n4o)Dl#PHZu(*5u{d%#9|6AXKlHG%(! zhQf86WIqBM`wAd6 zw7e0HZT}9pljnxSzSB7BG1-L(tE7F8+Te5Enu;@urn=Bp4C_LQLF|K*t1U5Mv?@sw zCw?3C919XtH#^V<8md+ayVcDjstEDQZDXoTX??yA9y{%#g>OX15S^FubsMWbi`a-cEZff zT#ukYrlFz1q46nN&`uGTZ;C1lv^Ckgz0$H%qQ}KxS*e!8N~x?fAmGMcg;~8>Wu5b= zIOw|*pGc>ON4Cm%30bTY^XA|{nr3Zp(A!fgSRkI&&VaAb#pRh~c&&B}c41ccySssA%r0{I3YD1sSc$Bmhd69c0MAM+z_wWdjz#Sp^eb$%9+X3jjakvwaCzDRi zv7(?j8Egj7TdD931W>!W)n8@65UpB%k&xg}BpfQL1Y1Cm$KfoA_}J5XmUK4FbFa|a zd~r#s9uG!M4Y#7RST{{BI%wEb0a(fk*{#v7c$myXRKpN>Ms_o3^k=ci{W11NE0Ybu zw_4gH$fUQAV#c28(f8?)IAxuc=*o7o0DM+rH`A5|ZjrTt@x6`u+6rL$yh@fo0WGMlfAa(LuTC-qM=-2xfG6qz z0dtO(wN2r~WMI@?BfFKdrXwf32Jj?Ut;&-N^vb3_M6(4|WeV2tkp9b~kp8hj?xAV_@sAn^i zwhf!ZpiL_vfvm^^%L2Oe8P+(7=IePJJe0z?9P~LC8TLgm+Mrt69nEu$cJJjk7lUJn zNWH^N?MF3woB1n3YpLAu>XVXwp~Nt+_bWptFOy}1#OACWBINRroK1WQu!&fNPG`VMr;oTa*Y!Lo~Z` zj8}$|9ESfjF`H_;Ixu%ipwmQDW@k9LTM6Yne?+#&iBCUWE^y8UBRkDwZyphwCYEe{ z;YOu2Kszq{z3J1aWI<8XT`Jh%F5>&_jNV?kLd&3;tW;`bo#P#wL8IhNQjaY@b@B2tOe2i&l@1I=#PIOr7J%Ix)HUWg z*~h8mw%A%j&y+P#?ir~9V*SqA$>K=)WeXdz)zy(EJLRy#fMLGZO^%OYKB}^1%v~Ud z%BzDjC&_}Kk2}$p2r~7=4b>~{@{-lJ3J-jwv78l#lH*+uj}PYaXRE+@PV#Dl25Gk& z)xSHXOHvZv7q5PRfEH8HxECB7n);HGW4crW7r_MsqxTKI62JHU{-71 z$pX}Dp>!2|y!ZA#z+^q#NMpHcZ&?T|_y2u^0+UOT@7DdWWaG%qmQ7;*a}zM;`72`e zT;FnjGfT2v0VXjMxBAv8LGhC{aG}^@f{98<8l45E@DE4@dVd&fr-~53uaCm; zppw?0E@Ng_$FK;{rCD7jG)p*iD5{+jjO5o;@u7h^(T%W1W`xjov8>T9xXEwd$iJCY^*4c&*>(Y?bnRGz>?Jcue&WCh~x>mqpN z2CJ*h3tc%8A@~IPH#7#Z5g`o=-&{Jb>At_rSbQUdk-+N(+^4;h4)H4qtNMPm9#3`v z;QwXwf6{wDVI*YiQ88q4In+Oe`n}@C5qcL!E3`CC|)B+hZ$HG=)AdNkq&l zc!7Z-%ah>3JAOUzWPJS9@F0R6!O`^l9}v(@qT!NLq$=;z*Zd@_(U71*j(T)KWJg`9 z0O6bN**FaB#Ep>5)Kl9sW9OSYcsNT(9NLe$R%~P+znL>iqo0XZDLR{$Z{}E>J)tls;jNX^$2S&@~`{OR^=_QWr&%=}riisF4GUoa8 z0${TO4SPO4Bi62_WmgV)>a8wV@slNU!O8q_GVrOduO1P_Bg7mw)PcwI6(w|P^c>tr zq7BBP4O))>ECK6TY^~nUbptpgB;SJ6PWxJbLeq%Z-&Y%bBcx-}9XY?>5VJnGbH(@e z6>2Wu$}20$NZLg!rmvc&H@av^viYbNC^pekB{jNw&{Plq_D|2FsT{5Y!<7#IGz1uu zsGPa*kho|C>^U~Chh7#(Zbl}UNNPEt{`;3*^z(7;v%NO$*#Z}A)v0Pi&&C_3^HPIs zvHj0|mdx$Ag=6QHK(lP4wsgePczgHoy|anl^5oojhAIT*SE_rr^=Q%;)Yrf6#brZ+1@YoY!;h!%b&NgP+#tb^@xo`*Gk5G~%O8&U z9kr023|E7-5={!$&clCv5r;=W1R4Z+gZh8Q{&Uem@+1M!lxQlJT^)v$XA5h{LM))! z4ishVpR_sH>iLtE&yHS<+~CCtP1WWQFNdoq2O^0h1T&52gs6`v}zqaHj?4|;eY>jVi;pJl0mD`XB+6se$+Y_TG@q$x^k72 z?WoR$A0_FDoq;&BFnVjN{ZaF%O5RmHjs+0h+hR>SY$|d651Dmh83BumA0F|cVjiD% zF(<-*UUHTL6J8{GlB*U$L-|nPz!N{P&)CI_i+bj>oR>UNg~gXn$sogD5Qevy>Xb#)l803qWLHoHPq9So&&>%nlKpB zUp-nD<-FH%{G7Fy%6J?BJWsrfj0ihAXG!-g*Q%$!+pXV5gq+Rj2=VL}Xbxb)8k(_g zoU33rCx9I87R-KS`ZS)4j8Pp4)99(^i>~s*q`h0)K2i%3z(mL3<_tzsIlhoZa~V5X z(&%agV7W0zZjDBnV_i|QIRVnNApPp^!*3Nw*Vj-7bUk3}*UoaiV?OVlt_f=B1!dLi zi(HHMp%sasXw#oe{kckQ;kUpL3KXfPWW?{ye2Eb>Xe8@OM>Jr$PjJO!)v_|h(B+|I(ExjHlMzZu3!v@6oo!vi4&&hU0#v;MXKd1NIiTboT+rJWZ$G6Ey+z4(f9z5- z6S4EFA_J8eQxgoGbP2b$;wTr(hjW+fq7d);8(_`=oakZL<09oo>#SYvm zQrVpAO&C#+z&SFu(+ylgceV*i{jt4y3!(Dks!|%GTsI`ESl!fS%!oUqjoCtjME?$U z3+3LELf)oDuM*GoV@05XgwV^w8qlSsi;k;qY|q)iY~%-@J|FoeYv9CJoN_P^nl<^z zgtVPHVSTh4z#so#5vhJ_xQ{PyQ4YO_tL9LcT|(nHMx>xPk)G3{t**llnVe+Jb!GU#mxL|o zVp3wo_38B1#K4b>9Tbsj#I&!Dgk?U{A19P`I$1~SdwcTpZq1j>#2-rk-C-HzhSJ)S zj5B7yJKy~CCkn`EBvfHn&aIKNa);{ZI$Fz#W~d&OyyZ}7o#@bz!9=yCvYu7xXGvL! z=}4L9U<0rzC6=zivgj*4t00MB6Apkmi&DV!U4JY|@wQA3v(qsy?7O%f<6j5ensf|L zta`{*RrN4DL{`0LbKN=JPUsNvG|YIGJk7v2a$@6_RJ7))@Rvjc$|FIlo5#O2JFRXM zZ|yaUzfRUM+Y<}}p6T-3{_r?L6$?e0*y`CCg;A>>nD=%9#eVUA3%T6pXnbp1s!;V* zM9D?3tloK7!FRZkH1ca0z_8Tl7-LLiQiZis2)e|@CXZbsx|6)#8-p&X$XO&D!r_rj zVar0nQjtYT#iS&T2K@7y?=)w4HbAJgiZ(a+qKA5Ep}tNvdK}-`b(~x^2_*9RP}hII zBUk#v_Jdu@7CNb`Q>+>bNq~tOIo~wBPP-tt%Z@0WXA*#L{R7jl>+}Rsn3z!UfCVq6 zh>Tj{qtEDJk3ZqT`z*v zyW`)((fGq4&QYseT*v^VGa0nNp4rMW)9K!y#NL1!QNf zPwX!Sc0Bzukff_l_kH#%T`)P6G_2U@`1=Cbf)SQCis~)FKUr@!Q6suSMGkAuXoeML z38W_$Tt|L+d)HD~Vj%mxsOr|CbTJS5P>!qs@)*Xy1SZxM0 zms$#RlM^zs>Ma=6y}G>wJanp0#81!MTh69NC(l(IY`)yB^_6BU38j?e7pffdfiOs- zs+XH#KUY-n+c)MK)SJH~Bqc=|aO!n5u1tT*w3 z1l$?kE{??85{LE98ahTPT-lzM4NNj2re1Qor!pE!}h zM+FADU#qYVrn~8N{F8VbxsRCmCqiIDwpivk{gIiCU8c`=`V;OL%q5>-EDGJ)6|@Ud zR3&uNLNL=t3W{O&cWaVNSozwe`50#j{iLu{+B0;y@8bFS;5;RsUXC%_BW&>%ebVO9 zvfJ}pSgRk2mP=oOS*ZMd1*)VD!{hnC*Si9NS5@+a(#_rH7Irf$bTmgxQ^7DFR=I4V8rIhDhfPLyl+Hw4du&Ro+mWAgH`a=+-&e##sUvJUp> zm;}ZNQ-O(!Wn)9Q#Hb>GlCxeI5mU<;dhD&7?K>82rEG99aSPhOt-1na?Vax8$&&KZ zU4nhue=EdLw!#H7k$3t2YWD`KiPgsT>{>r#V-VI55G^Zz_1L*|OYoz?wS^ad%V>JM zoBwYN?6fpHF7tKVOpo|_q*ldH_cW9$e?M7}pt7N0M2v#95ToPH<2Jjcm8yxo;4>6U zB!uA0fW#852$hY`60NXD_Y4hZ8)NdU*>xzNY@np@h>?GLMwIeVx-9mT!y+S(`LsKI zp_6|MysDOd;~@Be>NQRUQZn*Y^(~L`d55d!M0WN^8sbGn^^eoDo#)mKvSE`TKK0aj z&p*Ky63Ff2Q=s01Ouo>C74xQBHT;cM)Q|QfFa8kol$hH+c5+H`qD>v}HbcToacHRp zF;szmoguetP{E0SjT2}n<%X*8ONBx2osqR4utT~pOkH$bL&4Kdzl{=wm~^Mo?U#9j zErh(r>8!2CZmLLzIsIx9S`>2=g;#Ippk*?Bdb2xkLVb3?m1}d1mQo>*XwgY)lH{Fu zPt50l%w0c?Q~N6VA{l~Bs!cW6d}JkB({-i~dt+$t&uE+vG8`*moGfFZ0SpJ-B;7VK zil<7Bh*RRtety;=(%X^~ig1Lcc(}aCLHOM+8KfFGIB$++NR2;br}VVNagRUPGDvGc}0BpGTs zQO9K}QnhQHzXN*^4QtliS0OW;-xq9S$;3!u{y5ov%-kD&fGi+!EF3{zUPUItP+je_ z-6$xSR92W8uYiMTzM*M8-tCAW#MN%B^G`bqC>rJx}Dz< z_#E{TGFxl|Znq~3d%U4(I$Ur2CN3wsId+_Sn}@pS$qLQv%iKg2D^#f=O0j!ypotT~%lw#I& zOAX%;-V)y#ij;|er&}q~` zq;FWY-hwld&OT!gKY007R_^9>p2$(Zd2V99)Pg3L{5bYk_f&dPW9i;#)(15;j!weV zla!_u4>6^IsrySj&cH3Jg?cLa?jeeC?O5G+Mcnmk^4j+zkF8#m)Td0zeBQ=vz_rh3kQ=fT%} zlHQ`uv&VO^1H;+GM3ilCeS+$}7}&Qrvf*-7RC#cCaP~CcTyTnECUQ35F!SpAa_;zG zP|x+jL1EemqXu;1cl<4oTXMK(nFujWS!tlsSi5_Noe2f^kGtsw_uqkaEa*SdBX!Cr zOzF*oI*Ws;&UWLbU}7?eo^hZU_+ii9(%R}k6(}1=N~__RtTVEt#*M2eWL5ff6gO`y zlN24i%cA{JSePt5hdeiHWFz7Bbg57-f%1`u;#6kfM*_Ih*KB?~H+hs+t@G zBo_B)ee^gFhdEk4veVy zd^z%(qpxCRM`yALTwA;MgNgCv3kf4y<{#@Av)jw<NV$&r-ppSGPb5oM&-t~ zQHiM24UnrNg9(GbTlR{L>o_{1MH0_`g^E(j;b`o@(l}eOK2ctEG)m!8>3%pJ*CLZe zYBs#*>x-|9z*X28AI!4M+QP%f2PSOS-}Ynhul&G%f`qZ(xK61@3d}a9AVE-I2VKHk z_9z8|jL-D-`QhUF8iLZ8=qX7zXH}vQV?B1+TbyuoYp9l{<#Duva!terYd^!mA8|ti ziDsnd1&=Tof|i4>vaS~6w8+$i%B~70EK7hK8Zl*bd!zelqq&lR-m0E5tClp|%mA12i#}M1x7n?gvpmDovxu43y zu)SnoBO!15X%2XoZ5drKY*I4w#n|HbuSTtxdtaK4@z4G^(nayo@33=DA_S4pLj6<4 z4ffeT?s=8zeaJ1H$C?%nyjmiEV<_Q5boqDn&me#Nd{s_T_;m~wLeEwLxd0)*k=5xd1dB5x1G zL3G5@u6#j{8~$!JQX13K8@q>kxKi=G*&s~VDVEx@tWcy$uVDN5w@J4;RwRQ@3l`LH znGp3Vl)2t}WNq%yHdi2&HUT>b-kz_QbChyuW^(_jpK90P2#e%r{VDjft1OPp;smHJ zfK3+a4Vh!suFaT8_mnE_v{p|L_Bw(I|H{ALik4zr)f-Df8?659*<$Nsg+4(CTBa;G z?Rt^XDMXMlXh=ciMaZqp{4VOXDZA1gq5*(c7HEW{Z&J==N>T{-h$Nv$dJ?r80oM?J zNj%ec&7Un2X+d6Tq24VOmdokwEm5aQ-TpNi&uPw2vkpS&mq9dBBdN_so62o zS|vNRH)chs`TR&@wQm8KM{dPQ&x9lX_WWnOLMkK#!ox1FqA*R-p6os%FWKr_iM}E~ zp*&ASoEXo{;Y^hqldU|RWDxuM@(5_btk|wIv@9@~uUTjg633*BpM!?QilwTuE!&sS zjv&k%x?_++u2h+t^jWgpH9ls$zfXONAF#SpdM7~0;U&U&o&i;&?7XI4@w+y<;Pg45 z4iE%Mq-17?u1G=>xtHd)lfs3dyC+salfYoHVh$ZY!QXGlwD7lchaR!61E(j3j>((dz8j2o zydvf{5C?Rg+Q)-hv^d=PNpAoJKgW^eFlm~n73fLlY7;JtWw#gQDEs_sJ0l{g{INR` zv83zDQ364tkb-ZTFg*u-6N3hulo(Wtt6ep07cWSZMM5I4h2m@IFTyHF2&e;Qs^?dO zWgMs3HP@7RfeEtj=NbmhQTN?kU#V5Ba<54IO zzgs7laOid{;8bZ|z4XegrZb`RGkxkB#&VGUuFCfegVMYe1lvx72n^$75170_tRsLM zom%5+@mdGrsY}uL$K9?3(TMMkOYdaUEPy%ELJ?N|k+jpGwp<@-m|t5YF=S6*G7rtC z3tw`PAt5)aIWG_+pbaW3|Mlb5H5zsRD^B~ZbVW1`%A#KDBWjS~o(SMCBzSEM0V;V! zO;g3?|3OlV`xH^BN#=86$5hwb*L!|sDeESZx~ZS?s>(9f)(qU!+*-RS7=IpfPO8YC zZ1$*`m{1gk6mCsqJYEkF%+m65g?lWo{^qmj99^BSe0A-WmXxHa0#zY?zK{^wN<2%B z@1%UHvl~$)U!$rv|2sE{Vn-ayiUl!s<0+inshBVxL}1^o%0r(@!%?43BEwn4Zysww zI4mTz^xiGz#9lbWO$e1?lIJ9pM64u>iEAs|JG1+%o`K&&ki9{IZ~A7XwG5L`elpw3 z$g3&_%Mgk+?QEu~iT6xfvD|RYv`VmnKzQ^jc^{0kdfWC7Y&>4B1@BO4100~$kQ6>k(o@xhVo6KRP>AHHb3X)AW*D6ZK zHVI@@kUaR@Ur%&lQv{UkxQEZ`T#;t=^P8OzXZm+QLmD;Kk$*~?P?-ee1M3y^JZdt+ z8nQ~1M;tphuy4+NxF|bbVC5LUL%WXo&(T$!vK#jnSM1mfrkq*j!^qHh$Xra@F z>da^3eMt%MHYGacblK|wx`at37CS%{q^y>~*p}$9OTzp%2DvN68y*uAQe;NeyD^ki z8agPIctP5zle4ZkRrh~gBqA2E$`vgoQ(xDPV%kKdGv zfNgUzOoD5_Yc>l^@})j%`_W^^LC)mG&C16hh6z(< z3yo6BY$qrAME~JnNzN%6w7;4-!1z09?s!$QEUaY?_xb@-0`(+IUe2}U)x$TlLEjz1 zKeJbfg%~( zcyB-5o-8Lg3V){&6{YY=7wPQ(!Af~-C|24bM9Lo@kR~JcG?&#}tTdoH$T2vOLlrcA z9E^UlPDB-O!?id5f)d33fQNFlu9R$2fwsdxc<%bjwtsvFk*VUbLzE-$xa>dn?Tt_` z)5Rb3RdY(#v4Cy!hy`3shwLeKz&X4R-^a-7@C+Atk!2h zKJHb9e`FKJJ?}2U^ycnI$GZWro7;aCwkCT2Evu23V&dbOncjT0+Lrt~I;U>lU_O|? zWgt=X4U_B=l{O?cwuX~Vc=g+_mP{X`Y1qH2OS^2z%D6fQd@2ygEcz5e^AQ*oLS~6f z|H z;I3Ew;%gP#I8t8_qrG;7v6jM7q}xlz&nJV5;}-Js+Nj9A`hV90zQci*L&OI#bU2u*d@2pKR2f4I(OH_s5 zMY8f3yiRj)o}{ypi{|PoR#+va)-<%VXoOg_m?w%~Km_u2ds_?Yo>Q8KCJe0T$jGzd z($?09h<9MPQ}#CEH8HU^;UfEk0O)`*HHst{#XCZ$9Ga&{k=xkT3k9 zisGT679&8BONL_Wn3(7(455cAKDqH12NwH!Zq(DL%wADH z?sF*IWnW16Q8Pu4c=p>=XY)~If1Ur^oZ~X5=aFB_S({l4v?)oq{nq)#V28X$#W+J- zf(B$~_#o#Sq-2~fyc+M%gkb~Z{`+I8(eBv7Z~#?@@@G`6w3e2mXo=*Vka~Vv!~-}0 z>EN!jn3&0ox(FE79s?%zKtYyu;+|7c9-cmQ67Y+Tc{D~0LS@eg)rnCQ~Y zm=sf&A=*(tSAzp0zkghwK~!?VwwsNfbsrgi7OVjbl*px)xaH15-gcGv-F!ezhFMsQ z^R8jzCK`mkq|m@at55uRjGe-IYSDQ~c@e!~(onxxkl0@NgoHxvZ5kjCI3*2k)dm$=$HKweL({s;bs32MfOW)9) zqFAh!<1d?hUr7oy-C3U!8*Q+3uLp9AoyFH9k*(oR8YD|yK^*av@!`sk4rB6i>7O;7 zkBnBwI3{|?zL9(vad{JfM?fmF*8LPB1qVeX)8keJSY?z!$$)Twt8S4F+ryJLtdgZ# zug`xKD>BG6FKSU2-L`}C4;C6>wNYv@Efv9&LW$dAV3z`h(cvHlr6#*K>d{BFM) zv40>)7pVW-EEkd9Na@2IAlUUY{7$QVzS99{V0!S<9EKx8uGPbA~u?P&LQ;{5az53tWfA1fX86e65SvczBiZBUAhIpkYHFrK^dlV!4~I zPOa}5eKgmS?|NweLAjE}QB&?p_${xk$2{fcA7sp~tjzlA9E68%xyDu#~6WyzKew3l1eg z>5-q%hXaeChg~8gv6dzN(xki6y!pHOHR8Z+47wVnOoXl!lBX%}^OoYmD;UYq{%Z;h> zBP&#B3Q2L^Fm3Vq`2~&Pd`ufbwhR)TvGk6Tux@g%Z%L^O}vz(ykFrR{=&&3ngt*cc}1luFUBx(j%72*4{_YzWUY=LiVV8V|k&=XJqd!D1u6!_G#!j zWOO=wZ(F6kn$t|-0BqM{Pvmb-tI>&xq(}OhF$V{2QL$Ckk%EkLy_KjVICQ;?M`@~} zB0JV!%0PyUw6xcsZqJs(0F@bL!+t3jgWCyPLnvw%b2)0i1B*cm<#flxF(o#OJFSKW zyHisOqGEM!u2*@9PTPck_@DiswR)Be*0pTss#=X5J@;#j28WxDDC^veeNu;0N98W2 zl{k(k1_lPU{|0{`=C(u@@;nO*0u)Q7-gZg@hNyy`3$OAd_9;lV*h*p(*JwZYtRj^f zPV-pi;Q(ZZ%C3}V%Hmug1;LM^Llb8$!ygx6&goc03{s(WYN0ziBSqi!c4var5`6T~487*^(lHu-cV7jRM6;R9q)o#%rr&ZCaZ{3aRAiLiLJo#X6)!gkURZE%xCVG{2PNbuw#W>j}fX)(=OpV``OYgeSt6 z?8LXjmsp~=pt~lUQP*5fNZVxJe8O2>GJ&_;HQPd)u-J}(`X=isyy3zp!+qqX!?%!& zlX3!)%=ts!@t43VM=D6s210eNKIOym9}*>Uj`tPfU`jCZC3>1RLVfHa=rs*%{oMcs z*8IL9La2X2mGt#R!1l!$5!%I`u=7@vpVL5EGe$Q>!V18J6~HDJFQm=lxO^rldcw}e zqj{GD!Xb_R@%Np#!RV1t*3X6pbR*X!`X5$I4z1}+Zhy?0%PU`)ep^%Y+!~Sn_E?nc zcs@e&ejZ}Hn1j`8jWgzNg(isg8aQ{w9Y?H2G3NjJfq+Ix!@yAXQM7fsl|@BSh_G6} zNh6K+15ECZ6U#6PQDSe!e|fy=E~>S1VrnIGoME9D8V;VTQucgNof7><#Gx0mN$Vuh zHB$Sf>-8n6Fg`9G6e<--j%PdUHRLtC_o!L~`j=K4`1XPoFe4SBSksoUf`f&Q2^-}N zfv3av>^1XuaU`KW`2i6eM67>puJ<;N!%d0+EONEwTlua@FEuJxnGVuv;m7)2GL0CR z6oMs0Kh-}|?Hs*OUes;ZY;SNxdjZu8F05&&8T$^lfZ@R z`IdOU6*4ZQ#stJeB5DbPelF$*gpIv9eu23%QBlxlypT&>)6$bl_si?cY>pMit<0qk zx#+hCcXOl(x;=wy)oF<*#NR$HnRswT#I=y9i9o}Q^K?-Wbv|q_k<~~`Uw?(aNBZl~ z0BkrZL`555_WK9e6l&o>v<~Gky_+N`SGGWO6S+1W=b<&pRf5Cp8#+%sPjVk*7-__8 zv-`*(CXO=!eh{Idp+$`^2FdvHzC4{tF)-!4nG`MR9SKAz@K)u`Q&*ZC9 z0@=Z^o4;MR5p(G$Th4$7JL=pi&THyC(_3N)gsC8KW2;2KOiAvweOE2H-Bhf{>y zLHa-P^UnxAohuj!@~ioxlEM&cKmPvx640?jH1FA|YDq_9Hy5@zAcyrKHBUn$<8iz8 z^W#gs;hT|9y|yKrh|5H0OMwbBGc=KY`@dsniAhZ&gX_OJmR7gJB|?Wk#cW1)lS7*& z;Qt9xyha~W4Pt$F$#V>W-`d)mswOv7F$o~>1GbdfL1P2GFC8fyl^7-%jx=DTt zut@%~n#_pE8T`z=H=0u&sR;!aQHf-W6S()u#wH{z@@x7y@j;aRu;Mz3 zcgu+Aj^fQxlc3UJ$GW^{=bLmJHO88cG3Soo-?OZ^rA7+8m9}CQTRV3Igy?-9kgp~5 zV?{`LNh!_Qpn5N5>{NG=RHQldx4NEyBx%W9EGxyk=FnA$j_2kb@EybC%{8 zzJW1#6hbSYJ6+F4+6VCblURJPqulw8;5RRwp0}bG(wYYt=!*5z=9+uw$DzJC$^UE)Og7H3gO~y%@Wp5 zBqjd}aWrT)z3$4sJhHDf>q`*dii1s_kZ?4BMYsB8vIocE3ITq~zVe+1IH`dJW{xR| zbQ?)Us8>TW2l#wE?64XkWX>1dqh^g8?_o}8zY29p;{`}yyRpOG;8d|tLJlyNNjcseD5Fq zdc^V;;vWi@egJ4pWypC}tq6{&C-Ak~%?@+53J(6UqWLt4)@b)stQu-6{~UB2A9OkD zjyF|ppc6|fiU;MWBz%B%N)R5i4O@j&Z4$o@>>xKdHI=o#&dX zG9lBS`hBgqff!kqlj~UDDh}lpvo#d@ypGX> zQTl_wml87*LmC=9;1Ll(v3gSZF+iGD(%u?j1b#z)bj>v%B96`rTuR>yYLTZKp8J~= zA6>8mg4n~2COWae*Yy{|nw$IZj_& z!lP1{PIfR;&JQ-~Abw##j)6hgSC{oQg@1H}oV!3ZX7|L$0{4^Vim#>JPEM{sPv4_tb%}W(d|} znei1VjN7m=|5uJGAS%Pe`+X}Piu~?hx^8Ab1u6;(qz4ZVcgyAGfLfHRZKdq@Tx<%Gkf(yRJhID!Fg*66BJf1RbmVeGFTQG1H*~JqKw(->N>kQp*Ig2KdSe+HxhAA&;0hAR}~#_lb6R1 z2@M6HYI}+UoCbT}G`nxZEsv5_i}GxYT^Lr`_&K7_py(CeOO=Cl{0hB z{3gyB*hYrePrp@?DQHBC(Yw}E4!jdnuK>(~fsWYK49RbOO29VhDT3%t9edp$c~9(c zo&lTjfl3LnGOh1U9pscTEbDQbqUS4JXXj>q3m_FgQB$nfzMhp|U<}?LK=e zS`}Dn+^aGVUgFkq9CWUSb*I=t)4&EDz6jSILG*kY0&hguVM=OxUkc^dr4|gP#>ony zugS9zxM*e!GPk7ycBFZzxjT=EEKlm=?@%E zh(#mT1du_}ide8dZTZ)nV*LqaXWuBQ8^Mj~m;AC9Vl=#o=X-OJR%%EgrMjqw9F$6` zlX-H8l(%LhVyrrS;BXShsxnEX20xcy2UZo!we10C$s%6 zf^_`)-F3C>bexy9K1I3Z97oH@!zBDlI>@Q^aBY=(TJs#+y3=Qf4};jr*qM=BDy&>h z-8wg^-?FT)01mq7fuN`J`%v_lF|`5+`IgLB=Qa8c>sA_Ajrzxz+Pz;ZL#*JfCl9R) z9?J)WpDTl&!%v}nNo*sKsKMK9h=^B@!B&lmiz=lRNmP@gKFr-%k7`H<{nRgD*X>aL z1oBKQpV{cQl*G~Bn}qhSRj9!5B9Kk?wrjAh1_K0`Ujjuq%`I}5wmaUMb@!yDw^wFX zH@1WkBm#)c-}P~yL`}x6<@?IrFV!nk>W*HY&zVT^;v%ImO2#9gN0+^Z&w{~#6`*w0 zUViWu+Ix$%o#Hei=dM{~WRh*(kaIU@+mnuE=ebG`osGA<2g-mCZEC~ z8ch4#ap&H*@Pir?N@=F=YVmYFV^KLIRyqznLevC?+>%TDe&f^NNv$NcAVei~R!7 z39}6S0vhdCw#6wz%-+DZ>B#z3)C)ex85r8%M}6D*6%y;|Vo8$kbx2;V`dL@{#*k&B zGr@Met0)(n8vPDdH}8uJ^h@*g^Ox8v)>e`k8!r7&Q)N2%I!#?-$&GfOm=D(vn1`@5 zt6OIe2VwLo)aH8aljT!cBILv2yNrho6rqs1x+jz65VDPxNy$lxtW=5RQsr+i7PDc- z4@}bvaGaXj!532*tlvIWsj<&jF1l(epN?=U|Mdiep0Pi7O_r0>o3>NsZvQ<{M#KpR0#vfv)>6(OLa8Yr)V%(}nJ6#dEDtJ8CQ@VT#D4gnorPm{L=nS2@5fB;}d^V3J5q$B4sszB?-V z$(r61vvr&PbaPa(q0ryr{==bJgCz^ekifNfI2K!C=ossFl>P_ElIMIve?Ec$dut7g#538~@WLcyFGd ztDG<-NbCD(k!YYVxq=$;iNre9xc5XzVQ@OV?)m1J_>vTVLQ+sm6)Ve!fgsQU0uuak zaY^}K5HWVUsioy<1nR%rP(~LDkE0V=V&YEZzNv=nHRrma9Lznsb%giRoQW zv;DmTM;!RJiEx$F^;ts^$&8CJWwYOLbXs&MKPD9AlLM0NEcmTbo0p$&A3=l0!m&RZ z4^R?Ii$TO)=z($f?O5`i&Ae2>TLO#=Xofdn09t@eIxirQf#T?nOBa5)i9&LI-h~i5ZTMzPD%73rH}>V%&18YsROm zINT-dRI*#kAV3L9*we`KI{$fw2_0BTj z_h8EEx`uv7G-cpusr}1b?Xp5Fl5!94UKw^aGmw#>cU^A}K|wA0Dwwla4*C?OLr#%=AJU=`*d4 zNad|Z^lOCtYF9a&ImqcM7;c14Px@4?aeX93Kuigk>%yt&ys)EJlR@B!P`2oqx7~`33O=9t7iqjLd z0@X92qMnkkF|CxIZ_bmhQ=bA&ZhQLcin_nRmr@DHRz&ij83YK-J#P|_M4G<6g^P+$ zL-!UET5vp1vBk|Y8&21(GtAk?p}xI^z;aq0=j)M1TcFfQf*zcvLuui;YsR^>`OAw< z5<70C_)!i9$@X5qzMElUW4}Cm#(ZDBOCA6Acj$?vf&zxi?m}>qQDCKZyl&u11Q|6Y zf*y_S#B&s%I*YKtz$vBm03i$bz)m1BbbVcNw|K~*ZNQe~q-61PxWw*;I0uvr?^(0n z4dYt=Z8)7{s91na?Pc8ae z)twm7srw9_Y+FhbE;xWR$C(Jcm1E$QH z5XM90;TA^Iq?VS`L*!(T0QDq_&?H%uKP|e%*<0fo|J+l^))je6itl8DLv*aQe_bzX z@|2t>R9hW=zS0mVpO_ShvT(%@OK%KJ8hATXXsD~>WazLf0Fty*f`4d<@CjA+Ykr;9@O6K z3~0$yDCPZ)S`^gtyBwRMf_kuialAqeL65}wvy8P$kz}h;y6l}b$$iKh!Ept%&zD|!PXu8|N{OMve}V7ZHJZx!zOhC|$@sFU_Y+WoBd#KX zf(@imvh_BMH>6Yo>JqUQO=GVG&-f-Rp|*!3TX)O%qEfPToyD}e!x~VY--$6W*JOfZ zDQWqD!GyAn^cWtbbuQY1{LcP4-_1gEc=$`oVs-0p){|Nlzc;RLV&VG3P9k%cI?>VT zy$A3%@N^e>`Wav-QVqw!tC8>}L`a*{LmTD%#_H>mIf&>=w7gmuf^owrzpJv7xQ3Wx zyilhv3U68emMBpqL{OeoLJz6y6^{FUVG9x_1rHg)f0@T_YV-zMRpWxG-=K}3aK$}R>Ft);?b)1WYBS5Y6J zIx~_YhM9n68M=m`k*vn8U$581ISt|S=fcxS>tA>38{Yox=2!RzV~NtiZ^5F3q=>44 zUU7^paXm8c>qNdYp?XqZ&{Z~MtIwmAQn!(iB04VSOAFG4!Cd;@jH4a%`Oke(h)VN5 zLcGzSgoFd{*h*}4LL7=^fSRJ9WennH_oYpfiP-#|t0D$H?pG;aCgK*>)#aFaNWzZEF< z=<);Jmg+Yu#YGZVP{>E6kREhgTs6Fn2wu+tJhQ~NQq*YG@L<+YTc3DYzFS{#63K>$ zezeF=FWByh&T+q|o27amA02~XX@TfaT^(&U?3b8`m7Z?8_UpvCQaa%#p&`F|;s!f4 zW>L-57Fz7h22l(+cT17$shV|q-#0u>djIt4_KDl(RN2dJp?hu>6%~Vo)%ntVs;9-< z0sqyTrM$a0c{y42)Uy#VRS60O$C3h#s4Bs6rywF({(z($)){YeQkVTjAJR5`|E`;l zQ3-K~!~C0W0-cr;ou)M%HZ_#YLU^r$g4_<5LFxMRTpD|I8+~}&=_>a=KG&X}%?srh z9bKYG-?DQovljfVD~*pX>N5us>NBkgzkM2i&8YqDJupS3#JWB4zj!?{YF7PJZGS%y z$#!JZ=VAt1@AVN&tR`C_wC%gYuUOpfYcC+45ZKO!=J8^>r3P)kxVkz91FobZ-Ij1t zbhOV}E>&(`UdP;AZs4o0a(aiy=cA%0)iu=-x415I(-aiw0=3Fz8j&^A}iHX-GPbiqaKy8kLg1LTqb_zA|i1 zLv#J85>3S9KEls2Q6-%~x)Vi9FVrn*R8(S|pGZGb8GGAj@ts1$S8_X*?G-c28~jC6 z(ut5Tk#U+qr8;3a0c6WoUO_%SS+A$N=R|k9KokBPg%&a(SgV%7-kFf%<@X5xmdhS}UjT;1nyuD|RPl!+ClRcMB*gBw4G;eRAiI5Nw zbS^qIF1uAgagU3FVE_}DPm-zU_kuY0djAF?>10ceh!1z;GWv0xq*$JTQV1}KFeD1* zD*yGJMx73F5}n8 z8&cOEHydCcAqB++{^JzmVi91bmvaRjDJiN}UE_Pq9a#lw36Qb%H}l2UJ{{zT@7y}9 zYI_{qBCtHK-ha-h5L7?kNRc}{<1*S^0Ix<=8I7EU=oQYDlc04V8u!q9S9j{0K8~K6 zFv6{_vF3K*eu|7t*Hg7lK}(Ai7(wK6`)5dRwhCa`fswxcz>EuWW@bDWZ6+%oo}jS8 zU{ogaKr~B43@C|MruP#H3(2Q6FZmf34-eVMm%892auW55DqKMCAh&*Y(!;W>w*fGG zkAB9a<1tHo@bE&Axh5*25POX3cyz3hq)mD0;zklgK-OK>WSwkZlVW8h#QtRf&nMbUrctX0?cB+jfKqgp4AcEMLbs zsJy@fKlcFJimQBN*q$sY*+{9LwPJC_AKl6Bw|gkN$Z-pt0`|%RYv-M@nb{6mQo%Ko zpRyX=M9zSHe1V{uMh4|0^?9)nABR2{KPTW!jEXAsVCtoDl$dqg13F{)R#Y%Ena^RI z?Qn>-1e`^u#fB^F{aPI>*99sNKDQg_Q_&C!fBZ;vd|dqksP6RE(PLt=m?FW9 zguK9WYJEZOOc{-z;X80D=gdTSe8L}U2eMh_Vzd?yiz%GcSonWymE z$I2NZVft0Dp!AS7rDGf`)nYOehoos$S!bq9z+xg)Oa#KFqB;iKIY76yeOXHIfp^)Y zZtiU3&oEw@uFS_7)ou#D5>V2UQzB*T_tmB!obBGcb%W!U1*f^=!r zUkmu$!|!Ww2czA&2~1-n_pwX{D?iSiKR98&a%ay(M6K*`T>F7326If#`M zB0E)b)H~h(WdjHY_w*Y*F0`N^g)jioQ)Hi{zBON?aFijCr)l|`e)`qdUE5a`8`L*6 zB&=!2QcmiC-l|iLeAA%Cu zxe6ADDED;Fl(>6NMVm{;L5&VLRe}n?vmY7#FHQ%otr=5M7i^NnjhcfZ-PkVb-RXqi z&hno=%sDvP&y5(m?zZ08cU2nJU!+$7!DdCJ3NPY?x!&{?B<%=d@B6DzUM)3T{!n#1 z<2M=bO%x)Ohd+{NM-KYlzR;p2l147t7Ql3FM)LbJ%+*N+O$K1J8W#5R7)GvX437wO zw+1#C&*`=Yc2f1mY_xviw5quh%VY4FzL@it!K+ZUPH?~r*I2tII{nj6p_FNIa#n?l zN_vHg>VhF05~7GBa5xl4v|P+??=?^$KHBEv`1k}slm0DAjtJq#=4qt~1x46;0GGlM z@#Xb;hWi5nXvyXzSwVO9a226;ZR{^NT607F{5kR$U0)d<7oc6iN>6mxbxWt{rOw*z zm{H#nlynsu2K{V_NS^D0uM#`%%*^;m$LyDek38Ju-}tIZPYRfgmn0S^r|;Uwmy7(A z-5MGqE+*{}{m^#CnVnf=Ij0Z#NkcLD!x3jDk2q6wz4r|U1n=~Y+)8wG1oCqWCSxz; z+CzcOWI%m1%0b&h>&f%SXZ)iS0ZkFnnGmZwqTOf3*=6vq(|~RJNj!Xs#g_n*1Nn8T zmpP&C%FZxSqM-R^2ytBX?A}bRb&1#T!V5t>WDDt$zUkf2r-|r4S}qm?@9%E-mZ%?Z z?hyI6@Ri`Hy1NJLFT|x7`AO;t4&b6Tx3o-{CNRlg1Dl15GR@*b-!8u*#tIMRbNNzN z!KD%lB)1um4LK-IJ!mO6qE7nzAnpi4TwQmMG985SN4NpPpwJ^AA!RA}AwI9r$8%2M z@=N@g?I$XssCb#|N#*XJnrh`~Wsj|N4D#l6-V7V*d3|_x20cu%g@$v5jU3=%8aGh3 ziK_pOP1iKxNq@X^G1nA6uv~{6%QHbKoOsJXvq5NBGCS+`V$3=+N)-(;}7RC^eKZWTsx8p1Y*!CCh;|r6GUZY z-ZzuE(l0k;DX@X*-6N3zIDs}CBlxLo;1OrjzbYJal(cUxa9Br?sTv>ydLq#MjX}$R z)~tmnv8@W_dWzMoWPj!?$0Jzg|M@832L$_LN*>>lA{zzzsMh{E)VTks_(wVh6?OuS z$3Iu#L_AXBucO$GB!$zu&vq;~_+&jkHByA?3f{m);D#TaROYJ(sWzf zNbbPkCp{`n5Stv z($L=cD%#@_zr}-FxleS~sggUUp@dk5Sp#UGEUFgi#@JKZt;%Y{#Z1Sn>J3ytwoQPW zl*(fL0edQL8=(%!GSXn-;2}gLMDB|hYW&jPF&NYW>bL`hPUULd5MiA}(f4(v8lNLi=@X#N_^vJEgkblphKbrIpOO^fqHY1NG(G&fpY9G1!|CIr7I#OOO zY+a9WoZ}i-s;2#ArSS_CklFKY_?t8u?Ua#o;WY;6hU3Er^;6_Mo@xg|%k& zG1fP`{hNcyrpnE}3)a>KM<{YHaTvMgqAj_)z|=s)fn4g<)#(y4v(a#%Uz$f@_MU(% z{`XGN)Cz^+M=@OG=_SM-B;|)g*T2CWuW;Cp?KFDD(o(u5vv0tU(p!TUW42x7$H;g)E(w*~o0WOF$=^UkJ}Q z#2&*>1v@Lvx7#evRZ~+*dc(tz9DIhj-*_`H_5u}^KA&wJHM=;#M1qxpeaK_OTPb7@ zjhAm`NC}V=Rtb z-xOA%k<7F5b*_yrsj3B|k{Tr!8mopa5Aa(xu8Z{F-&VfW=No>!0|a!~XntQIcf$Ju zOYJoSyQXOrp_N%>nlQjmgJk92)UM1413B-qz|~GjCp-s<>z>f=ENo`coEr{KQOQ86 za|-+-6?))S_EeAv!jH9$cD_jxrnSbqArfX$xM+U=T)p??4cYa%|8>jQ{znF)Je2qV zXq!>*9(wA*s~onW_Q8SDv*YcBSdrX^ZSR-G+gkkR5tQhux!7!nO@f*$ za(*7T3jqs}>6z_Op%txu%lvv{v-?N>$LVX|v$xzM`!YWE1eR|I90qt*#5Lg`6N?{D z_1FKnQdd@a6Am2~SZ`8rlBAMYUogSf+`J&Vy=+x^NC7@sl7U?Z;D@1=*((D+KNQ1c zSSMQ!8&91;Ub*{Hbmqj*G2L$`Z5joe@XnCt5}A6;#emHu(#jrPU*%xuG5YvSRIp_8 z_S5(y9k2{8EIiW4uGiAP>71kM5;HQAL#V-erd1o~P54({ec9hG?3}t34Uy$c=el`8 zjpzqBuV}U}<_p+N2`FYjYDMoVRp`l;@MMGM@tEIH5tqpH#EERFtGVnSAm27mcv2g| zwAnw;Ezy15N6lkTn+Eafs8Ag=>r)-1#r5GMjXse_Z535}F6)Gz;46Y7^9FcG{n~dC zf9dFEIWaMezrGX8%8VOeu6X5uonf-FTk6T=g_Ey?IG=NteRKa}tu8A)D<3NMG^cBT zOjO=Tuo^|1e+1YiezJDI-R88tUA6ZKH8)#w&W<(R0xDQhSW#E$Rr>pV_}82~{+%Jk zp}3Kr6_gj{A~~6|MG8>t1?JQt?PZu+M4hYy$WCYBCo1QH+LZ9ImQ$OsSpvq{&-v&F znVtK_TN^WBW%IGoG4ZUUBVGc)1%J|ymO!wEF-Kr!76qsZO;Zp}JlUId3jcr{*{xg= zZc25%{)7UjEnvvE$UKYud+nREN^Kl{z}e=k93XQD+(V-PKWcNO zU|*GNg0GzL82i8WT_@k5Ahl^j#*mRv3o#Rat*gJY^$pMrr>0FQEDIyTG9?(++D%12 z=q?todrIFGhucfdOC3{Ng)~QPo#DXivQ{$II3J=qWJ$YL|F&W6d}Sj{)}S zv1508HTV3KJy|rD8VJLD*vfB4v+;Dm8IA;O@4KBNnR$h?`fMrFKGpm*09uV_yp>jW{dq^Acsu$kzgzeDe}tL!cw;1RU=ky`+12 z8==NGAF-9NRM8|_yrdsMZ5m8;9!CUaeB1KsHL2&Vug7hYPZJY!OH+drg2tz#4N25A z@SzPF!=?=rm>5MKk$x_RTaei|eR+hVwfQs@0z27Ja?Uf; z!b(gE>4IY02NBlQG)wcwp3-cUZyNxn4ENELRSsnKLH{5*AmU-)$}gZYTJmU`#GP#Z zgNXrNjfl`(gtSDz&bEGwH2`#*ZF~vNPFJ zI(<@Gu`m8?Wa2n7V=1|Ok5J^%*l*7NaVhBI(9&W^R62Z~4he|iD2`?1b`^8PMLwQz z+xdv+)ZSPo$Yah@@=j9`%eYfL0zF5+M~G0YI1hLf+|3^3iz?)HXv3!pHtB}q4XyEQu)dq{1eVNtk ze_<^(#DBwDYmZp#!vIZgz8SYZRvOh^?m*q&WiklvxG=dchC8$@^X3q zH?f(`%QPgU$HEvw$rbFG@F@DYTrO#F%smycIwt_>m_Xlyn80clZn0caXGK?1(L7na8lzXBk z{wpBS1HI$e3$<#y%5=zcV+z1ghb(&C>6l1-7q9Y`mOKd)N+Ix!sW*j?f0G@|el*^U ze?Y&q8Kr)lhf+u(JvX60!st~w8+_iq$9HB#FQQclbvBE|=mq}L{MGRn{J88GdP}8{ zO$Zbs{FlG5&ItizU=!#KIM{x0ZO|L>Pzh~dY-sU0S#T8g)%hFUt=sklZXfi1{sa5f zlpn$3sG9*+LTN~6A)PqfJ4O7mN7)D}to|c!LoZo1?+1I`sTn(aXc$M@zn{4i zKkzKkg`c+5bGJ(CH#bjS1@upYaoqK-9fry(a?JicR3T9X5?0V**l)g6W7m%`$E_t! zi)b$o&Rn z!Q{Q{EG#d2dwYix#i*`?j{)~%)V>^Iub_J-c)sgOkXZWqV3Qog@Zw~tMD@^dIFa>^ zegng#$FGi@_cs!Q!!-M+ot>m_#Yl^9_qg&hNL|D4u>- z;jroq9)3#>c&8Zutm2rprve2aC#QE~V`FxghlVbcxj@&}mR#>%q!?12B(6f(6mb@%&|b`;xNY0!z1-6%SA`g!ki+fKCYSz z;0*8@{o@;=Y@kL$em0QW+uH+?M#KC}3(0P0SuU*xOBe>AZYB4Re*!7}(MLbIWZZF6 zlVV#^l2<=1*_S6vkf?OS`}L(Rux<9kuS4EsJIM2d3+>J4yuHTje%|jN#Pbl!B8HXM zTai?&PU59U9dU2(k-&vZY;g50n++%rck?Y%rz1kk;lu|c$e0X$-Byd&#)-F$w-GUX7ZzJA;kcvKg^C%B7Z|%g!OUO^f zY-#rSpXinH>?X_uaNy$gg0Hf36IHYH zh|7DBD}nh69t;3zfrNPs2=~m{{R;-$cZFo1^JrDT_vPvS0a&}WtK#Kp)H2fcXn*S-zw$VM??O^N!N4g<{Z{-YoL=T$clA3a-8)-8mga_%H5 zE@pdP6m&#&C;cBzfce7RMMWovQ`#dF^-2Cm(|XjSkva@mSp{>TnWy<Cfo13&*i zp%r@e(4WsrCbuEtVvj2r$U9+;7X5ME`xyRKJsN+hp2r)e@%}N{1D&0n26b};Nfatn zq*7i{fH?z>f+0$MdqG-&U`4yTRz&=+F92c+3kwr9icLnK%{n6FX6NL<_V)H5fsYzq z0f;97SW^qw`VbWp)5()x_$&H-DcY>euf9VjPNekzF@?sJOT-0Z9a9Fswzvoq7J*tucyuTFckH77ywEu}ehKz}s z+3GSd$mIN7LQ3j6#M3h^5L*qC0O-0RC--H|AE?ky!-EGdH~X(I2XdXL0QC!%a{^9Y zWN*_63X%YZ#2n}b;WgMS3_J?-`Y{BS#QazENBY!zsk{sH`xE zWj&d>kNVAeADY;PmdRTdz)-On{6nvQ?o9s|HDwr|GxA{68e-yf%dSYaL0@bRCcW%e4G5TMW>rEj2MI zhxVO1odC|Vxh%Iiq{@kgrd!4i^D|=4#rb`RJV?IsHq$}fvp;&zwWaMkqDnTkBDlC# zCULq*hL2^{^xG#7HdWOYaJfPdrpu6-<#6i#k}I7yo^^Tm~ZI@E_kS}2xum*K4vyrrWV**?{aHp<#ZBh z!*^X$4APGXWsWnNTkX{2TB-8CF>q|vPqJQ`cD2&f*O{4e+x^_cx_;rHAU(Gzk@2xj zzm_x2BWt=c@bEe&t&$g0z!Qnp`cxq4J`!sID%Wl#I!E4qg`teRuBPxRwP0kbmfao0 z1BLybE;(|3-K6Ern@~Fq8W+EILWemYq?g9`ibo^GQmB(@gSB%HT)Z^X0DUok+G$nF zx4qx@egnzOs!J5ixVM#2!@AUX}c2*yw-kO z$(8@YXx>41{l%bd)oK_jb^_&E+$DicoV#-3(PghSulgsG!eYO=7Lsut6Djdpq0$F~ zzB2&@J4XhrwiYlKVtrpADzmC{cZFgMtu=Qegx|fEarJJ9LzChV)^lEpa{HEYn z01p;;!(#l(VgB}a#T&i&-pX{he&0Sxjsv=NWIsc$--EN85Z$kSNrKkYY-trk9NOFJ z5W#&EC!%}7dwIuwhM`)73Vl!XbghKXh+QP@jxR}nFj`k1-As7cji+e$DK+Gts8XUO za|_>jl!P3YX=JM2>FjDAbK2k+(qsBFZPzl!vaTI_uv*p$xi64IBSlCb@F6Dy&h_@) z!W+sMwT!l_eI@BX$1bny4O4l~VJY^)QyEG8E>wC;D))4Bh5c#Q7qVZ8c@9-ht%%@v z7Bg5qZ=LHetB)?v15f;*H)#AE<^3d-sBtoI!sUk?c zKF{c-h2$b+ABKfLQ6=aAX8LK#rSCr)iaK2%u`HG9{(LXMBh9VBfN&b~I}uWR@KDp; z{?D~ZNvyGaObJ2$r(n#cS1nENBw4EGZlJ@JWIwMOU)5$5d{l=?nb`}cu}&qoQW4i;@PsXy2?hvjw1s>3H@%@d z)m6bg-(P&*k?e6aK0wP_?i=o1p zjFPbI(yJMSnQupC&5k^_jH#BB{BPosydTJZFn5AWU??Swd3)iq)w2SrhE6jB*XZX;{%yu{B3Qh1$*8%}A zsd#1=hv-CGUGW>CQwqWBTgls76|T#IyAZBLM>Q`Hwb=^VE*rQjbP3|4p_b>q$pMB< zitDci*EU>Uv`kkPE{8Bey?i|dTaM0SKDn;pI`1;BvKZaNJ01Os+)|X6bfcwiqiH!k znErIWUU#^^>wmdgmvOY-wFd)gv`Jo{qfDjRSgsf_L=*)g%75k6%Yf_Ee&|l9N1I8j zD5`$j=0I`$QF1Yk|Mb^4#iN~&jnOV4^pd620Q4cF%wpN1u>kW?OruBf*_nRk6XDml z9MsoTqqevgprL*n#9Yu)cyb`1Sj3;#>M@ykk=bz4vl^i<6cFM0>_Fjb2y@#5f;XcF zs_2gC@=_6{{mr~;IloR@oX-J5SgF8AC@S~T&%@-F5;Hw@LSThWXE9h zc_pj|Q;qHSiif{C9Y}bUH=A-9AmA}HhC62tuLlSb(>YH-UgZ3jjp=k#;ZBDyC`Ihs zALjL@U$Q!;^DGj|`av_5skO+JbixX1!dJL-H~Zh&u>{|kM*g-`(~<=#`dtMSK?1V5zNtey+i;*G-O;L<+1p-Fxmjm>BuA^ialcZ#dV(+)J-122KQp&GPb*?K zSlF-j^gE%D7x+g*WN~AZFc`KiMdr22ySZ}+J2+aXaPXsvM8@UZOt!>ZCKlMpM`_wu1^$E7K)hG+MSwDT5!Sx7DCP?Hd;GN`HrqGMNXW1 z_{r;Z@wuF1C?UOoj0fIpS-904nlDisDCTQ+Hd-Ah}#V$j@<_9 zpOs81azm5x*zk4?E(UecRIa28{bK0tJy|m+n4rQ1>6WK`pAZl7X5UhVx zf1e%;37`*TXQWkVH()7z7#1-VehX$kFZw||Sy^>*wiP0>r*w9>6fl19K{Kg{cAs!j zNmtUV3*5$Yq09jbB+Tj?%B}6dTzmv_u|l7x(l3?lIV4* zNu$O~dwy_(5SzCf`tXC5pLIV~^EU=`j&e7ImrO1%qp{73bUxjWJiEQVLdBoTJ(S;C zx$kHkbSwT~PtN=5!OaIRTi2S*ar_!6#6$lP$Fa2R$GV_Y6W6Ao9IWZ|0MCG4k8B%h zS$@K;?4#Pak7wDH%1QVUqlmX9kAunL_dIn7^MFd?!eV&ed0Zj2N&dZNs_6? z0K0{M95n`Yn^P^=VD7HsA8fC*4{MffR|qm{iB5W>?9Mm-yef^lgr_PB?7BJMP7FL< z<2+jyq~jRrdWW$)yXvZoC6@>r3!PZ44cA!-%w2X9Ec=I<}zD@@3aaX;)7Y+ zm#j6E5I-QG{5~fbRd4HFkz~1SIEbFGdrlH}hp8VL@PUxUb62WaSMP0*_<5J;_PkM( zlo>$Ri+V3OwU1`%3UT{n>i5NL1$jKbIGy$#LXzEDYOp7s;$5_0MGgv02H(%Eqz5*3 z2pN(~=(*$3R0y5OD5l9a74zHv6xylJHg-1J^h7GJNDKUaBKUT;>CL+3z}0BTYd>2N z(%`D}XLBKHQ8%yUwKUe8N4^Lqe$$Bbw=JGIJV+5`-ApzKvRp3B32(xVuIfLWQ0q$T zs#Z{muu2I_fqH!&?6$$@5ndz=-xs_&>SCuneD7Aa`@a1#4QszX9CPn+f0VC!hj zo+d9GE|`B#BUpP8dbvBGHAJ@Z-z2tv=0MOyA(Uts)hF9DfB~93QW0o6XxKK_R-ozWewR$rM)Cn}LIzzdyqW zHoALZ=NoLbOV6m58*oOEI!sjCdXm(q9gWWl2G;Yt-oTow(=CO=#dFrhs}B(&B4)jg zczlA|EOm;s8@sgK`S`qLN{h`V{DPiFFMJ&?jE=Z$e8B~jl?c}*ju$o*T{ftL3#-@Y zprSNs9_1MHqMIp$<3gCLZ6fX0$k!gtF>zy0H1LYi1Y9h^6Mgd$w&_hIq6+rWAyLA% zhDSSaO6(`hat-T%3vkF{nksEqYSbhs_DMu+TpC`(ddzb!6Fx%_lgUI z#Vww9lWP6B$jIFhf@aGh!hBA}0obtb_GT3EljhZA-*J&+6;V<1vzyhcK)w#MX>3Tt zAYKbi7&MO-xhD>o5sXs1H}RAfs8EJlf*l+cEr-}X7_8hPq}_Q>uV^igYey z0Z{uPD?OJO5mvi}GzvQ+xLnGN4Wo9}Gdgotv-MK~&&gO-P5)%9SX6ys9A67XY8nIt z4M3RwLYq7_Y_bFWH1|Id`NQ%Yp`^B!a<6?~OSNY!=7kk4;>V5sTA4CLk8V0Ll4E*R z#DBOYS6a*YEc;elj-buat#OsN|j#q&07a4QeeiCIgyN8l+m$@|vnfR?oFB?vTB zRe#y(*YVJ8rVlM{8}l2D}qUB6gvU=lycFOo$Pn3$Zj3@dILXQtIb z3Ap*T|2e`>!l+Lxb8==YiLZDh?eHT`wl=Bz%TTYc;nB|auR}peN;MCtmq!)rNu0GW zeK8+`rfnLY2}pRlxc49Od8nwIz2aU&JgucNfK337yq@t5*0^A#|F8+y>UFG|?ME{s zDPhViymHbu_!~>K_7r^q@L0D;L#XVV>SyC$-ImBC){n4qv~dTaZE5Ai`^=R}P{NmbDu$oR!)WeB z91^R-Vi#IM?-K8_I|*j0UReR>!1a7p6;3x4skW&5cS$piR?B^G?P0J%}w7_UY1p#L)K1 zwA;|Nk;HSdf}p2`Y^0%P_=nv_C|@0{SxMd5;?T4mlI;_-imk8e0m6c;xd%yxI7W>IUfo{KeQfsd z6fnNM(#pyzD13&rvcB4*p}8v2Nc ztY1Ip`pKl`#T{yWwpHvzTuIyvWeRQ0 zIq({C^gFg5d>X!)*B3oRZ1clu%T7*h$tUutJ#O!CpAX~W9UP~eJ2q;FlefcxTz~vH zHh~YzSyYOLZ+r4j%z|%Ht#WxRD+IT=Y|qANajaccIb8k{lT^nWoH+?*msZ_OKwfIv zI`kcFMFtl#R?;<46j!{T1nm#1G&WWbicDUH3cVW-Q z(uzwO;(ErhKGAI7kqPo?_o=tEd&A~^i-xKK@?Sscy`< z|3TA@3YDA@2{v>Cg{>3j7J#I2`-4+R!X@N2&?9U^ZE6m<&se{acknKM11C1oPv{8* z#w*@Qh&Pq1;wW!Yl-52a&XKV`3~(X{$4t2Q@#eB^p9=Rjm@Kv5Z1jNREK>Z|++Ouf zMyfLR-g5@VNw=qg4nlqXuDsn4k8TMTI7olzvgzwGq)U<6)#EEt`om?cffKuGvsG}D zZC6-M-wl3h`FH*GxyRuq>hw;!iWq`ILF$`agZfA-4X5{~(mBfhmp5B#abqZqgyR=K zKd$>*F`v7%?gUVyv}B|PDh5tV*mR6cRnHyX(NpD+H@XPr<)frI`aF*ss&H%Qao&B? z%agic{B_A5m5Fn=yP0RK%!rbvr4GZ~1_Dw+wY zbzSbos&`yL530To)}Uo#9-|yCV`tD%kTqcOMQ?}c@}>DycAgtV!l&(UiomWi6)moA zl3Il@8*a>uHvRqK5U2T`)s&O4r^dCp^W^P#efY)FawK!)9{Ed;M|cwYGxyh)r)7!z zL_k~L2uyT+YjoIv!+j+=k-)g@IhqX4n9ZfqyFT5(hK&0N#ouM3HaRJFOVoL0X=Ut- z(7sp6{iYo1o`N_Bt-JWDzv@0zA!x$Ek)3sfd#7`=S)x3bEYpe>uR9zs{Km2SCDadO zSmUPJ+iAC_RXTho)_+c0uBYs5-1RlDc=Xd&@Y6)@^LN{0B*NUjgM_jQV~xKnyrV45 zeM0FO=%_!AUV^JQ>p06v*Bjp+V0x>4%=khYxGLrrcWIK| z&ZTYAgZp#^Ou6ZV(y-WlL#I6^a3S4xQ27U=c^WmygenefY4^ z3T-&i@{)+H(GUfAuWm-Qzhl`^&J$@g#iXbMxkINd{jxjD3`dX+B$;8plvpRN^qP1B zr$KTcpdRz+xJ=R-H}J8(a6#-tCqV=q-sw)56PRy<;AJ*@A=bp zqCie#>!C<6hxj;72zSwD^>MJi(XYDR!-m~}8g^te`zfHoQ7hr$8Z$v5c&EfQbNO5s z?{ueJCOh1b^~~ucVgD$v+B=6v{8@XjK0~Me&<4|YirlUn;nKSr^Niac%(%;SDoted ztNFqr8-c0}w0-{8sP`9b@jdUpLF3{2-S_k&@JMw-{HPOlRpZMJyv2z`U_^Ny*wq7f zbM^KUR=X!#0R{T_u0RZP{KNQONcthvM$X&tX#{Q?h=!>nCl(|+(?*NvP8{ygGXu$I(ZJ&JUC2qQa<$BY)`n2B0v#Aq_kk#W?5hIP# za=RdrlF566oNn>`!nrA?A9X72uR0xaL+ml;L>J}`KX%-Hu716gvuP;ZHAb7>tw<{i z!qF>-FRacLK#!p5LFYDO9@3c584UL_;~oH(WvTux#f<3J{xme2^5WXy}v$mxy^GO7UedKP5^=^iTJm_H)wDxE2~~{hoTSOj>4it|6eZdUxCuD-nFIsNUv_ zv_tMVlpf9w9QKlH`(KQ`WnWZr-}WnlAkrYxor03mC7n_-gtUZ8*APRubT<60{z-MO7`mgniLUP0MaOGy~c3t zuWWCuJOigT3hS%AXsaC9NMjolzd?gs_kVVcYpugiFeZ(#=JX(q$dpfpE9)41ed6P5 zMoOK5xZiJN|9xl89=^TN>EEr@KGYpD6S{g!!ek6H>&KIP#PjF3Xf2#AQS=5c&Blxw zr4~F!$EbWd^a>rtE7n%cthR{B->wX$^!PAwI#tYdJwXsXt)K0SGm|1jZ{F{3p_Xeu zb4bedS5mZe;&MbFyN62pcJ)@M>i7uxq@xxkgSRFB^mCE!?#$n&^<4(5HU+?*l=g}Cm7ca1#+WG1_rj@~=UCc_0TsAGy-?iegb zpP!Tdg-;5dc2RY6bbB*xvA-@d6>v!g*-CihH|z?h+%A($H}%;{>Q+_zZz_QPQr4&F zQwX#PH-D8DaG*76?|6&-ZTRFo!Z(|7)+2eCSm0@>Sir)bRI#+q(znybu13dE)2WsV zy+sdkurDK9uCBLSowG39RaMhOP4s2}GeGwB@JVM$(|63TG2$2X>((DhT+g%W8ZTlvAS#`ZY2aq6Qx7BuDG|uvkFi;S7&ql>^uUM9}!+t;BNv$w)mt^}h$d%&ULE_nT z8s>h?YvrQP=GG!e=vne@`{UYPH;%*b)Qtg3EWQ>e_MeW9PKtP37N763+>(v=yPTC) zR)da{{wa@m!^KhWDefR65NWE%O98>fV@nkgWt^#v6$yupm@s?O+j!%XF;j_m9j=t( zH-{Kk2~s3O`l0O^Ti5$%6=sJB^LMSbXw4#c?4VWkdwaz5S^=8Ai7U!%!%VQ4IMmyD z(3FJD_~B^s%kN@U4rB()4T5+?WQxk3(~I(8$eoW52lC1uFEYftTjk=$?L?l<^DWd( zH8)exf&fgAT#N3^SfTg{`IUIW$~Zar~w$y>77X!O?(H@su#2>*!7Y&ti<6v@z2%Jsye* z)y$9^#LDcVBbw3OX9ApQaRm#>h=9YpnpJ|3G)Wr;8x&6&T_ zGuv3U_8e}m-bv5CY9T%LZnXziuG84mHXn9%zuAZB8yWlAr-rJg^%s4}IMr`R_U|p` z6j`Wx!Um#?IFdbMx%|9w&*Z+5XenGq3HZaZ##F}uZPo?DSzlTLh$(cWEJlaFBcf~6 z+r^H{SUp$}Zy0YYYA9-RpsCAClK8-DaBqY{tEzte0y$#_3EZv%Bi?Vf4{fWQKJ)yK z_UWHe;s2+jr9W&Grt}vr=zs0pglEtG>krCOY&ZG;sbShyq4)80W^6W`#r>t<(7!aC z*)qK3+PZA)-9O6&cw%H+kbv4*M#iG|pZIy9y~1(DZ}jW*r@{sQgsS1Dj>Wg_I-70& z?%o+^d|CK`noNS6SJS;56;K&94-~6SJ)ck@~LJT+_km%01nm z9v*%izCq8j##TyVF5&G{&Gg%zx|{{JxNcW+&qo&{vMt5Ky0Y%p);7Pyb6=Wt-=I6(KIqVPi|2<;zR$^pR^X)amFK$@<7aW3 zqca;nbF{vBQRH9oNpor0Hi)0oFK4|&RrWQET9Kd$1+c%Sy9@$rH+9AO29z@U+!w!d zxYI>kwOUgzbUOryHywCGzJCWk3W0_mV0d$42ljaFq>x{R?v@k!*E?qxx_AG2+z*E4 zb{;g{NB7uaqh@N;YpDzavW(<>{6be+wJ+}*EZL(ifKA|Fuq5o`XQ}rQ1gT<2)b9is zO_0iyj-5B7FhyzdiU}DR5G8Qc>Dkx;$)lZK-fz#pUag)@5>5i`5!>3CMxFdZd`mZx zY!v8)s`6O`q|Ry2W-o}6say{yfdT*VYlDShA&*ZBqdP+TuiwnNiLSpPXJ0pCF{x|4 zwg_-;-EY$^SgBjv;hc2XPD)w*Ssz_hYb%V`<&AYkeDJg7!1i3@D9UKx%W&Y20LClf zY!PLyPwi)6#?xoIDl~{|=+?tZpLRbAAbErS@Ac>oQa)6)YN?Z29N%|)(vIQDMmKzRxw%6@tDn_X&cR{CrfaQI|XTxe2 zq*F#R)o%N$m!070=tU}rL@up;^VpYZfU~OHPu}^#J=+Awvst{#xmg?r7Qwy= z*Q>c08Yr1RMsnd0!Dh}|yQHJmPjbJ}bN38S#jJo0ekLLcP&5N#{Qo&+KZH|qY2TRXQ!NP;2a}ku;QH=vamh}vX z9XIL!GYeg(R2H#7K70*)S9Wh@G2TL2{PsoG<|2uF$#(l!NSas7{B7O)ob@Nb?PmYK z=WYB64rSQfYvKqf1he!Y>-cAUusgVnp>65xlHN-gSlK4Gh3-x)aQ&MTKeT0B|L0eI zOh&nV(^n*YJRWX;(k0z}fz;2=^lzxZmO4ZhUPB_pHzJ?a@8^#$VSU(SdAae6Zin)~ zy`ve()Ntu#0z?j4WocJ9pMSq%kZX{MgH1}IuZGw6(l-}=bH#$T!;GrN+Ng1tAR|J37kgX zr_HbKyk=B?Osx~_FRaf>e<1;`@N1^a=R5>K=}rvqg}mPAJv_>_PW|E*KfnHiqV|xx z9KIAtbvr;*(^u9-VlN)%DZldFNDPwe97;iDrWW>nrxkeg{_{Ut04_pqbv@MQ5f0cG zVqR#D2s)3hlFv6%wlibFF7fj<`R5+c7qb(Uo5fcr`ww`3i`YGLm17RS=5Nd9hyTo( z`li@hO`P#$Sa!$Ao$_mEFX<*F%_k@4=xvT9oJZAWbSh4oD3bQYa2aPbh>VtBTQL6ROaleTemdbjC&JQ zOBp0`L=&1~9}Csg()ho`Z<7gGSqEO)#5VnV4;B3`k)1y`*MBcIHB|QWUgbR!b?hF98!q^*vY4~(S27>MEzKZWi)rsfxhQG+MyOGwZb&D zl6yns$?b$B6rC7ps(5j7dFd)HB9lM zxD-mC(;NZkRLKiAa-s!VVg(+XOF%YcuR! z5wg&V8+>1t)&a*w%z+o~(w2D&%`OKN+(+z@_8wjirM7FNwH{kz^d0irE1vgPPa1hUW2l82DQtg%LIHTkW|mYA{EWVdF`J4gjm77Cde%-Jsy$k3t$*@d zI7;6?b}f5#K|Uhtilgg;X_Y4rM+d^pymYKWE8DH=hc|Vb9EXh8fm8>}Chy|x0iTW4 z9jo?O)0%r%e97e%1c$<@C8?_NnySr6KlF8q$Q6h#sj!k3uB0306lx&{>f~A99~>JC z1^&*6K`d{00XLKeF&8WNRVFQ~g~%;C((REbwf!05p3T+0LhV|hDaf|Q=R9k}>bw zG@tEPQjK01*S1P;vJbt)#om$5uF8*9qhq7lw}gkJu4IS$`=XWaa9VPmSskt?F&q~%oGDlC z@`Ps^-#%ww-s5t3V%I!H(?qe->6c;Dr}7}CX1dxLGd=xF==$EAT;To7(@LwMgxfRw zcEQrcdL(?#1HpGo)}w9yHPtD zgzi;Hib+4+ilL6YD-!P5j`nXfb9*;07$a1L{&k|0X!h|L)KlNv=TCyWXWH2ht7u8s z1mmMWj3~Q*(#rGyqdd>*^)T?q$A-S6ne20w8dy8xmi4`qZ3UVi1v>{UIae^XHoTrx z;*9JZmJkN!UD3?;enw+!6@rPyoZ}qkTlfUq-I?EL$B^Xmhw&=t!f?#j@wwQ_^;4pG z^348BnHtaIOYZ}GmmU3!8xn3eAYEp(0g%_f{7+@W&W;6zLLm(FEdR=ngZrU4PAF6m z=S=&-UrcX&$73%-_dG*M`N2AQhv}=85H41S`wJcZdO$E3UqAiD`2LBp?TO@(2s*S@ zKy$TnhU?&uYbB*o(b;rOg-=8o#Y+Dof!$V~<}T!Lir_0vVo=f%?8w81P5U~gD{pru zk=r|i>pFPG!Gen7kD5+)_Bo5@J)pyLrfio75r%%w6yed5=I4J8r*;)6hKF19&7JI+ z#n3bJzajym7n;Eb;Srqevp2XP48X|&|9dQ!7cU4Hlh|;`;9Y?0NaJFIyy$9DO~Q02 z5jJ~zOKXiOny-b zeaq5cbqXDSo{&QFM!daJ*X#Jtg8ll;ijTCHS1U5oU>yftI*c}nz4M<4A4CCsZI`}+ z6Izsl*x&f>u)ffk-bnjouZW6Q-XHM|gh3PU<7~V8d@Joc%vfmw^}dnQFLB5@tB+pW zr^7cptMNFzc} z%RdZd{GK=L0^pBF^}3FC1L$U1mjKH&2qzhM$jSlgmJVOFSU(r*j~p{ z^#1;vS?B%l60RzS>oYsVx8g{5;X~h56zmQQK|2~Si;?d-&Ku))N)fk18OtJ23XNu8 zBR{4#@p4e9m(VmV@i6f}v*Q?-jrEl>;zNLX`dCBMku`P?&@#0ufTeWv2Sm+#meWteLdY4 z%OALw2g@}WBCa;_QeL7(YT5qay_vn!0_`84n1+aF>>XblEf0A1#&CLJC*dWNX@Nk= zJOGN0ErEBCH%KgA*ElPP`m796WcPH1XsYR759Gc(}5 zPTk`lSS6e~oogm9dUZ~8%;Q1)j=AJ-l1@8Nt0&9m32aCah(u37MlODh{T*k>Eaf&TnUs)#19)dun27)m6TwUT?|r}0 zY5VPF&a?(G&QC54Hs);*9Uc@@<%U=(S4S}bf;`W(JLLG}#POb#I-RT?wj`hB~ zJd)4a`P!Ckp?m{al@H6CDHsW&4o%vBFT`?UeR)nMM3T0G8W8&b*sypDH zo?5s!D0<7->UDO(cuig~BjIr?XpI4b1hXJ*Kmp!H68g7AyiX zvM?Ysnod;IAVq*<@6V5vnB8TAQr|n`Um1M;INc$pt9~fAEr!xhcSitT2OM}_<;4Ss zn=J$NcD;8SD%a8O zePM6Xo%^?nP!H}kXDJ(Bmes@blC^zZE7*O@}<`55BXz1vIp>`Ro zu%YL>2aFcMU6X#1BP#A>k;hoalm4aV;PyBA(T5+H*e}ymAIESfW{zqN+F0AMuxc{^cV|UYsMP&bV}AJWNCAA=-%IhD_uqS zcj9(jr~xS2{L_ztUmuTP)wF7DqhmFDP>`e~o$xBtqa0zQ)3e%Iub2Rnqz{HVT9{0- zA>wi&KFaTvw<_j+O=$kWovcqL3M)VHUWmVT26(-z`AA&N=!eri&f6k|b-5_AR5qaI zErG+|SfB%zC}yA(1#HS}VZi+)&9b21&P6AG~? zF*}{E+FGSWo52ok=*3pMu>bdlNgOb=uW8U*!Hg-YS7&Czwze1f$hC*?hPA z^YVu6eufQnf{3l4qI7%|GTt>y_ z4HC#zGyha`?yIvK#aMbw_8~eazF{u!YPeOyiOMb3)FT>nx1nnf;=jJWhCBVlTGrGq z8H1E=GbgZ!ii({t>Fy?N!iHX8+IhT(e1zeZ@<^6*3hxvQ5mIevii+YKYxaA1c$A)f zGC8^M@BQrGAYAdQyj!#v79~Jk@4&)zx4!KJtc*?V0|LdHw5Hu7lSnfz4}KQ zqB*OSt(;j56QXW{{}eUA@HG zVhHbD#ljB#7v!BLJU^A6>Ka97P#H1TunA~+Byvi^MVG9Ht$zLK=(nud8yj@waz{`b z@RL2wA7Uqaadt5i;jl44aM=gI$l$d5uVq1mN04kWOiavFQ7{{js0Z?1M(xU_<4XH= zoY*8WvW$>umy~a@Zvmt65E2rgi_ACJ$K5olJ4|p@}K2T}7v)SWjw-QY}+!3fjMMF&(E)3q=2~ko}!ESarEDQaj-n*7128RRu zTJa`H^+tIOIXQHo@Ox?xqE>KFd@9NFt*LAzJSc$uukVh)x6zH%F1epbJ>Ueu>pYa) zH1|0!W)lDN%L55N;PIL7(*}WacTLB$ym3w?7y9<)f$i^?zuaf7ztfx05k1U>Ht^87bQrQF=$k(k-Fq7ehuuQ|6x5G+?&- zh(j8oiSmS*Jn5oS7b~dfH+C2DTl6Z5N`4J<*l8OEPqh*6xZKGvjwLa*(ps^(N}Lt! z-v&ZHLhgrd7k>^7x)o3_|)LgTL#U?flDk*yl%yD|`1p0-{1rdzD^-KyP@5V$d@9ZD#%a zR%wG~!lVGiiof4f?IH(VuMu7~mx(@i(cH$whM{mKV6P-h z-<-M}6}JN2%+@x1%za0wyx@LFXET!0EWx9=+Br5pJdzZs~Wa z4(Al?BZh{C27vJvUVrzI>fe!uS0|DI>#(#;U|ppi4rk=&QjO7C&h|zM&DQ+Typr#} zy&%BHf5K^xg_y30c=AYFQ251UHOK$Enu1@2?-MKe8GkW(=7OZ~4u;(R|B7J3y6`+;R0z`QIWT3}?Nge>;nIR?F9k&O~T_0cE z{E{LUzxB+bXzKs(O+3mmv*gu8=CF|Ewz?OL6>C5CapnteYg;6w#8vCytJwZnQTebb z9o=l3rvQ*zfZ_Y&hJx&mN@|-*Wl;g_lpafCyw2pKMd%1CQ|X~19$O2Wd^L}=$w}Pc zs0B4!pwY$j*LYJu9s@NgL+ay1Jv^r9LjL^ujpD(LZEEzi_hNWyWV>u)so+&MV77AN z6e#;}*xC66XP_tBS;`5^PBJc`_uZ)(rPdpR9`@3_Q-TcP*SZQ}IoO5VJHEk%nA zMD>4a~tYSw^MHSqX zJt7OREwkjQ8f@=eBO5RH_vhNM=Cs-GpL&6W@B+FR0JyT!pI=3(3BMH z*JC=H=`SUri1fpID!5IoM**V=lbB7Elb_^P!KqgNHsT-ImwX>#4is_C_i;IiBYaF% zZ^Sm#*_J#eN@oc6)-&) z#g|kp#JF8@`!_$D(F-j{(}Ov;$AFMa()rr9=U~L28Fq$219DBFG4&*HSio>51-q|q zc;MaDs>wdV#k^lSA46PRTKOnb5||-Ujx&p9Bc=?_*hP;?t{AJ-mP<}VxUbAd?Dsv#qeo*36jIc32XA+tv5|<3cm5}hQ8`Bq4lUDGf$&-=>=O}NL#~XbDu-T&! z#3Q3ZA@zn`L%UhI(MADNX9+oVZoB1_)%2^rfshg+;^1a*J^>j)*l{ISP*70gua13C zZ-{`#w^V^4If%-jm3u|o3A2$Gpt&Deh8V#(44c;?JhF)mzHH93jcw}gDlXI;>hI4t zDP#CqO_k-APx?cyp&VkO7Vh*tKybbJBlR^HW8WdoBI8qixGyxAJt+u2ud z^%>-o0LFz~Xa}3Vm-m}Wiv@73$`DUP1t-8$2eP`$#^}y;48@OMY?Q3jx$J7Xm#hHc zS3vY4{!4s6bnsrzw;Ib?UoLcjr2)jsj#v;AgD{-8MDZ1|G4g1(F;f8 z5!xpuR&Z-mZjT}JH%Lo@`iBPDtN8e3Icn;g=UAn%(4L#?YuL>DrP~jcZexJ4rwvs; zno>&2DV-WhTgvKD(&xVj3kf9v!U?`EHs(SxthO%?2I{};g*O{Qcn347ttKU@w=2al z1qbgnsf9ZeOUJ&{St4>0KJam=5bkK=V0~kqDqVT+TfCGUDB3o6j@Cd}Fr>4cvXc^e zA4gL*3e)*Kq#7t|F-(pWpC!tU%o^lJ!@!_tWz>2s`a}>1nHU6U<@tIwMPFxgP^6@# z2};N*4;!CqO+RDT_=lB9jGBttNEbQ@-aY}sO*;EOj|?{h~NI zsJ7>Nj8Lmw9rj4&x$)I)-Jr0UKgV-DS`HNzTPrIr9~h>j1c29HVWW;Wn>srpt2{4( zua5UC{hjoZFc_aqh|p z^0`d!j3k;`owFZ93m|nQtY#`%qr3&fC5_+OJdg~k*Uld&mr;iA^U z^igs^s1G?RI<{1I2#)E=l{B1k>Mjwft_&A3}U#mvh!?E$R1L-(Eb&o^lH*)SzWQ`Q09|m>%74$N>H>Y|-nC)tuVJg6&3AK6$G$?OD0DM$ zc`6%ni$w}Dqkoh4%Io3Yg_9lgJElpVnuAaAjl6ErZQ=WD-}dJ+!amG8m! znkXAK77CB+bxpK4{TE|ne@72)JhSHv6c1e_9UL7UXWy1gBB{0sVT;ugDoh#seYAIz zGgT!Qi1BkuXNo`7M8iT#A>)txms8J1r6f0Lbi=T{^5z#=oQF9&D+?q<`J=_{(n(^c z-inx*B|62pmZPb;q4mmh;3hVP!o~O%cA~7KxfYUaUu4s5s@bSiL@&m}?UgL=wwK;? zVJrUsa8HPPbEe4Rl?JXPykd@Y%HBIU5!TzT6j6+?cy~~?Tpe^jgA;I`oqR`V5eTh- zwtxOCV4Hpg8pnNsJ=Kkikdw{zNMPP^*T68C&u2l$A@!p+Y*`PylIVmQwty^JnGsX4 zQo=?nn-ZnQ0ykKs{YpI!CgzEt(+1b&;R;qw%ribK`L2!i2*2O>Q)R?q>R=x|rm=+; zJRhqwP!w)jV0T{hkB<%1PwyWz8te=UzL?WaT7kAx2CWO-^30y+_>2rs15VJZgm(&z z*Gyt%=D2S#b?2t`MC**1vwnSyqRYRH_PU_hY*LY8cbhD9%+(iSix^T;ZJO}YsGv%y z*oGaAPW2B^GM$EOSfm+mWPf;E{d;*(`a#%~=^uMKs@m~cq0h~MPMP6Z3Wt3`XgRj& z?ovyPcppe7US7xuGHp4xFX%w4oy57^oDy%escLxFhRwV&OO;|TRDo7P?Y%>=C~|2B zdbIjsAKBAK{WCXJy0UcA5_Z~Y`AbeS8iyOMQUzZxZhfT7(-LB_2v0P&tRVyE@FVo{~s}Q~@_&+bvXl@Z{GO}IBCaSeIzj&4a-f)U0>lgrPrS19vPUAtkL%6Itw82)`KTNoo42Y#n1yoCUXiKUqFp^Ihw- z7U`#wO--(1jH+9SFMbdNNLOJvuC9td2rKhFK^oS)(*N@i^~<-SUt*8{bN?Tz?_p80 z$wLyy@45tNpVO_yJufHoPmIJihpoiLCcnt=s<6r#pb?u=eR%b)VF0ciU$bG<2p!;8 z6B}c{UpTmRQ>mOf}0rldnqIfsuA?_YnJrtt7^ot;kp(XsFmk2~(> z#{}=hx#h2s_tCdY@>#@KfOLid{Ps_VLI39bVHpE)#o55#_wUbTWdBHoBt8?GgIMUO zfuk-P-1jKRmW{-@K6$MjY=?SLZJhQ6lhpQtrOSK8+?In`Xgsu5Qe*cT`S?(GzkP$a zHx>^*f2<^b?o=LLjA}S~l<4%{rFG}?i2EaI>*>DpSA!1dZ532us2VY*cR%F@Kl)T- z8CAaC6!2Btk}Uo(v&1ki>5VnSw1!#(*;$?fbZZod7wosPAMuBPc=N zl#H}_DarR|o&Eb2pVYe(6;b!Gwi2>exEXBNPFfxbM;5=^+JW_1 z;jsKonODQT+|u5ko2x`8ps@wmTk3FZgD!el=FU(E#HB5{&*5|Yh=B7GBs`wMu%CBg z;=+-qV>DB3Yqgx)jO(_%<@k4g%xc>=nw^`kzymcps7J;d7j{3uoJ6J*tU_IAUHtAm zjE#bfu$Zu7@tvHuYBbc7Va#quEYw_A8Y-qEHV-V8LV$n`^C2obGPc}_n)6TbgTFS* z>lUlZ7jg)iFZNLJY6MK)buILh%&|Q(>hs3_7-e$H{DHV19K=*m`rciYFMl4}IXg>A zDgH7rAK@WoKncXN$HiwBut1YmlSTvzb-rkNfG~hG!S?}Qp-|=}_t?&);$ntbX7;l| zM`4K}V|6~(DZD9P9S;uYxPdQXS`0ErI{ib{tAy^(&l-WRA^?BN9;x_Erl) z>nkoc*?+iN3uEakJ>B`vVQl`jX>dW=`_w%v`ri+>o~dcE`r_4~wxBzsJ``Ruam2WV-zXo>B=C^VdN%~0woOv$?pDM>(*^{fmz*K?h;=y zJ~w}t?qW8b!{WnF&-I?!5%+f&Gp6w7<^ni}jh6wnUn|)1E;{Sl^8s>yz5e1qnRQiL zocAZ&#sjsMbgG86fCw#*^ONRlHtLtowuJ|r?HY5Hm8^qFK8&-xo})Geu%Y_>_@v_v zD#be#Q@+?@wpG}mqVN;*hRvP9u3xWe?A)(~LsiT-b-{Q)w^>wW9MofRU&zYI(KBD- zBj;LWAKg;ch&6~{8mA3T z>Ou;Sabj%!FlUdQo08kBy-;Cr9u)@2@xLWvVpKay4uZYoo}MVHA~;1cYw$b6c2 zSCYOPYVMPrFCT-zgbCQqmRy~zG~4LvCaNB4jy;|#_rP8>mSt|N@8zG$i<=*okT9$E ze9y4^2`uJe1@@iEzcN6#p-@Iq!CG{nwRL&FT|{Buvy^|NdDB^NmR?Fb-}ns+Y1Dxt ziGm!6&-Fx5I>M_tw zb}J!#9gnb6t!UgxMNZa1d!uDbDq7s<*g?Jf`KX43s}rS1Ff;RL!7#bO-&hYB8~q|a z>05{oOr@v0S5vM_zjN8Lg7zMO+~M9PIvhQnG?^r#Gr2iMVR2H5e1zHxVr_XM&~>(w z;{1hc`BgBxU(J2Xrfwaijss6h%4!Fm)G$~Q`Y<=`I{dbQmQhf~SFZ7?@~NJlqMDr- zn$k_~vhwi2l=FLwVtrFhi?l>jC!1Dyzae;f8nUxAIB-%uc=i4E0 zU;ElP`EOyKK~iooR>x0<7>ST{zMn1?%->p-@UR0zQE(^(0)P$Y-)SGUG0|%bG&-iY zR417ICh>M_@l9INOP??QbjL9YCYhyRW*@|2rWLwRB$4B(Q5BqC@|1o^o=WksiyQ!o z!y7B7eIW?$Gcs+)GY$oHT2@w>EAcoe&F(G?pomWRqrm)b;34%R3k+gf1@byBE>yJ| zkA0zzS=8vSExPCg#p$1Lx(za_MOf z24Lhi!i1jyZszGn_jf5bE zu(8p{d)~H8cW~!__^NVqjpZ{u@>AEv(9X4P0IXcikG$H_e~yL4?j+_W)^UWyINog? zbE$yu?A$gN+g4^j1H^Vu1FTmg#>A%u-YPL{$2BLT)wv$dMOoW5Q-@w1&XjHp;>h-+ zZ9-E;J$Rbk_KegY*-L-+4{Kqsc-{T5a{xk|OJGRVE+op%WbxSRtQxpUyC5_=>;=&6 z?;gkH-!cd2ZKW1k72hVs7ArusWd4NzEH`X%FnKiUDGir{D5W1J)!HA~+00f_SA~8) zS1)SwyrLC)O<<(i71zEA$LE15#r6(UEWe_;zQ#cGfh(xviXNh=9=m7>8o42OMIJ7Riczw%l^a;=gY+5A^^68xZ!i*? z#6RxRlu(0vH3HO}2)D1?A;O1tLteV4B(c!w2_ns>pH9KmgDXjJj+-ktG2tyt(e_zc z+1Rz}81YL%%U{TXpX~`dHjakHdw*aNYG1~B!;gGA7w4Q^-YYdOlaz0_T$|(X^?!H3x@;iiVm!|RD^)8?F$AgTlU&VgY&b*WbyFx_bZpOx zrBkU`j>V@n$Ln?SUE-nb~Yi|Xz>`QJ&&45>I%-RaNzLCU>Nf7xw!T$QSBD1^d$7~Rd){P4J--*s-Q`ay>MToNUm{yb_ zgD5t0>KmV9JY4?@U)lg5Z!wO6$V{oQZ)}EJE#{3kr*t;YkGX*Xl^G(1VS|sr=yMhn zC@P|SFg73qu?oLMGG3&j>#HUUt5w#V9EC9;1ln&p8Y*wHKL|U#cWE|AV%0O!pRRSm zlOTiAO20Pu!AWD)K8?0Pf#3^Nsd&)jr||C~r;2mfI}E zjUAl~W5=4xQLCGvANlep{0kUhj|vp4Ku^-u@{z^##!Y#;?%ukMi%jQCsvEH8M@6=gi5F;aV@z3T7YhtQJXDWR9Heqn2XbG((fE{kaw9rZZjE#{ zjj1hCvkN@u7}e6Jgs2LtcXm*i80%@Bs|Hu(pK3sS+tFSXXvCFXdJ1mEJN40?XF;gG z56zs_D&~j5sdaAuP1y~EuB}wuZ^>G(kni3YBdGuh#CIU+cGuT87ZqyvRNE=~Zx`E) zhnn0d!?%akE?wOb%d&?(fh^|W%uN`b(E!+Jxe8vrTUzl7VWyhm1#CZ8n>hv;K;Gr= zT1=3lqL(|U*DLxJW^)b7Cx0V2J1xihd#PH?0BL0^-dV0fO7L8dA4J4w|&SZFuUF={f?*`RT_9#Q42KZ;X zndAfAA#q+*G8haJEM98W^QAPT@dNo^AbuEIJ%nMdWEyI7B($&+by)mR>Ux<@Y9y8~(;K;ua-DEK@su5Q(= zerK(eXS|7$ooG$TVLCkxMsX#@NuD!Nj|TI(TUT3Zsq}e(JPS^_AMI66UhQ~92F9Q{ zgO&kZ!pRfE1L|TkL0eLXeac4Do*R!-%x=3DK5S&47Fw<> zN{D_QEM;*rg)1|&(?Dgm{(99Cr z16dPtSY2B&wEV+1<~H*psNCSFX<+-+n`=Tx%rH`JFEn(#_G%N~uBMGMgvRLRyc%(g zh1|`=j6qVe@69Gj+s`V41PjszrH$r>#5r@#3kHf1+_qO>#MF4Uklwso8x0|4r19ge zw17{}5ZV%|PL`J47L^F(Wp_D6QE1|1TB)5y2pIuiSkbTBd3J%~ zkD8N}kDVws4@bt{-nbyJmr(if)&PU7{pcWaLg7vlp`hi#%?d(~?m1F8Kiz@fE5{-_ z{k?S``i~H#oyO!H$mlgC#79VW%3$R2@pE&_A_TE6&jfF5Rzm)EM102bME9V4R{!t< z)~gGTh?wldX}T`vG(SDI&rSkr>b;JwzPQr9NJ(s@kg z`Q}`2Wxk-*y)^Fb_HLDe|7*$i&F*qrD6&O;S64SPH6{ix{heK8@~`w<(An0`RJ(T^ z5NA!`u~_#7&*$Mn=d}O;WQP5eWj;!wjV;j8OPct6B57h0i+ie2sST0i`?~mK9Gt%f zJDorV{y!{z1zTHP({5WRE(MA^6pB-dySq!U;Lzgk!CG8{OL6z$1S{@P+}+*X;pBP0 z^9yqA?6qdk+%||YsR?@H^T`H^YK=0lan3_m_11I7M^IZn9-1rtm)oZ^5A(?{168`n zGP1sL&qV?6Ji%fr!AOUI?=#MS(&R4d(9%Yrym;&mFZyQtS!L#l*J9TR6p7wVLtB?C za0q(8Ef5My^Euz_&jjCO3NFB7ONreYaPz-8kG5hi2mbmE?p|;-Mc;F8nce@C1R*_=zuW-e=9Fq zxF%m!&xUD{f9X@9P*SLakqS~pOEH)UfLR*rvBR|M)0%0sUaB+531y zT;(()2Rv!3P+HoyDF0L~uAHtoBVk{8=jza=EW8zwr#{X6_FJ~ZV3YA0Y&17Bvx(pS z90c)A{zOjwTQn4&k%e6%bpgg@;q|I?=TAjx%>RfV*pv3m^_d+e@LN=L%1U%ur{g$(7L*ES`=4`r>WD>%(>KA zQz>eBr1)3Gd?WGey10EBJT?ARZGKI;Dmd?Y-7UIi{^RFnL9)T+7h}nv^GO%@`BSSfK=I(@>Pj&TfqwF^~QCWMm41Hl<7tII*c&OcwLo&^NrB|($G$T$( zLeeL#IWO|TIR<$E)l2-Leiz-~uN&oeHFES5wd`X80f}me^zz9p-AneZmQMJ%q?S%w zc5Z3Qp-3K?y;Rhx1e1X!N>~8m?7`f_L#xj=Yb>Jupug>#KJpr^&D)&Mdn4QzYwoM{ zUuKh-fvUP{R;cF13f8U=`J(ipR*l#6*vUI+qWw`OQ_9XoGa)^;Q6=Mh;FpHwE_xS9ws-A%r97pQ6SZ=x8sS~*Z%o)f#H6JB z%5S9e%!E?-$&(4aI`9fIu?e8zPy}heD9G#v{ z^5kA#Q38Fx=b)cx@ch*&rs&XnS-A5+Ln0Cq!H9J-@Rei_0gTb=NOGnb!hvviDd&gvh0y~X^)3uuZtVu@Ma2M_N!{&is} z8@hgW-rE>D)3cNMUl+w|jaOP)woQb-EWS;uJ(B#L~$C@%Pt zUw~F+*5}DVetlTdB&V&r>pzPRdTYmCNHJe7D7|Sg)NA0e_+B({5@rbuNyx1yZJf$v zzbL=eyo+Ua4Hp=>5>$$o27if(@d6dvm01k6kxlKj+B73c`tM7LiT&mI`DftM*W;3+ ztiG1BHtRdGctHap#u|Ms>q~q*JiALGYM_%G3d&`}!n;8o^9-6h84kj^z)A$p!(y6( zd@4#IjPCD~Sj5Cf4{+`QuR?29qH9`{?67hht+bKpvuIi0ENQm)l7aO!M20@iCz!56aY)b!7m?_l-w$Skua;rxEj+`0qQVbj1GoI zP;*k_bW2C}G&(K_3bg6gCBPf8(jAOm}m8px-W+r>vY>LWs{0wTPUipqh*t5ZeIKMhn*>>^f$=O4GQ>Gyi_olKbxevuOZUv7ePR)pQCnGfNTh> zGZooJb)l~=p$5fmI@lt1*<}Lc_~}#Fq5b?e>Q$%WDYyw{p3C{^~wpo=hru%oz61(fijXZJedUt@N{R7GFEU?}k41~nEAjybBuK0^hB*_JCx*^79Vdm8uwu*MzgU0PcR z){P%%)3p5lh2`Tl||OWsJV)Q>P6l+*UCuKbG2DsbhVkPOgfQZdl< zFNcW}3d||^_^<$*$}aE-raQqm^D${5H)&kPQ@fCP0}X#EPhV+CYhVCfU`V5iP9bsy zE_A*(6;P(Cr)TpTM)2OcU>$y9w2)^;1I&W;W?FpESLy48i1&7c8%e~DF>;bojP=$m z_iPE#9U2+w9cE?jf}tCq2+6|Mvqchyn@qt%2vO*+9?us+bjxipaie9H_Dz`HV>#Z# z>~GsB&payXZ$E}UN8m4GN9@_N)-d#@N2OK$tgqOeqjx<29I*h`12dktYnW_aUdoyM zK*uq(zQ)j3BYBB^<1UtdBG;5Vfg6=s+%xnBudTQu9$J;s&{@{gHmG8YAWBK;?tkjoxwz;8C@Fx}$k zK$6=o@CuK|q;2%1E14^+20@#+%g~)LU(5!4X_UdUCx0A|{kzy#Sdj?A5P81b zYk=KM&D^Tu9t5Ub>aB_8$-vNJmHaP#!+?ww!9AOQsl3AooN_~QG!CPk&GkoBj#E+^ znh9Mi`_R(p`F)wAxj!+VLZ%ZWzV!8_8RnMx99{M|hns<Y=KtH;=P|TK_~tEZ)`1ic{z%J{U=kOxjD6-w}3te zwV>ci;oNOt-+QIMVuFW$DG7^+Bq5eJhl!1+(sQz(D8Xv-h~eS#=lSrXB_)PPDMLf?{3G(jpUlp|G@^n{Y-Bl$@_8tOX>5PD% zb5iusZlo!)8B#BR?ch&$@Sm&O zWoN&J;61auZyo!>B+~oJ(E{O1cI_h!avU;qtEjQD6Pp^AwPK18kiRFo2*w`}Ql{sj z{VMywpq!*IX9tZ0fIz&OLDeY?8A1Bf`VY zEka|00XU1@a$Y(gQ;&~aG^gM^Zw88q0^T+Dj_y;0X?h+oBt7awAR{KIy6bUg(+uJn zP6XK4c5YO#4SE-0i=ho_qxJJhW=?9kdTZf3!^xCVBoeOwevS#@w%;!>C(%*Bx~H&s zGtj-VUst8y3vuj?5?Xu*FhL%SNTFsUZEB5p`m&7TJjyNG`4(rP#6<*XvW{^tCZId=bpaF$hvWWN$6DX!$sf zi!HLZvtx3#D^Lb5g+N5>?&ZUfFJu(u#H>uHHcd^ji1C>(M5{)IkkBJUBoqX&8U6a8 z3KThpF4^}7;TuH$7+&k8HsLN*_!ExG(uma5ly*MYPY&b+>DiF5yzFvI&g#?A)7Q&g zM+@uz9sxorRJ}9ZE`nS{i6DQ6NRlW)@xRR`s;BEd=ZBFO!W|r1yY|PQ0|YOQr|?er z736N-15&Ij+wlOL0Ee(C4gIY?`L}z-%~r-v7iq@iQ?LE#q(-ZXO6eGx^S@WDB7Mah zv!5Djnsp;paOHX_K0@7dt2>$d8D^YBinuLHa{IUzdzRC`DQbsp6$;9^BnWCG^s`avDiINL^s0|k9uX;UB?jQN>(yKo3UiB$=Q>zu3bGgt$Obona~{ufzNk#cNbhvPJ$#i z|9#ex)ZcJhkiaV|D`nO$uDs%?nlXp$(uV=Kt>EYDM zy@?1LqL~MNo8NjP0=|TAwKjYe!5@hm^C*?jqckp*=0n%RsY|Wmt5N=QzOSSb+!%GZ)i9n|N>J4G8Ir*ll;QjO-`I*(iFD<<`Zs~0Dl+Cm_?@>rW+ zXpNQ|9aG##bq0n=pyG2OM6f#&LM=Y0@7IbhvH#f~y}}h#b{MG{CoS2(G1&j+kJTN5 zsq=>z4UMK1rqD`mZ$z(T0lhC8<$F&vV+Wh&->A4~F=R`SvA6qlT9LVQf+T^Q z09V(KLuEq~las;cRYpy;A1A1`Qp@EM6txiSY$l=a6Y(S}^_ox~mY|`SGgj)VQ+2t3 zvXVIL9e%Zcnlqf7dHEVe(!Hkt<{b&ja4B*6Ot(W-WmJHZ=r@+v_6{~`?ro!5xn!rL zu#X6bj@K5maPOX>@9!`iS_*3z-lP49VRdxG)Ja5$7Z#WgFA*rME5;?^jf*95r4Q!c zfR7|&CyLrMotc>qN1X<%Ok_3Q^Vn#&uY$zUhha8unR++UhGz{%-1eR*VI3n}C`_;U z2Owqnez2ry6#9oq_W?3R+kpRhlNFU*eD|CQja;Y;p@oya@HGyj(Gp-1;9YrJG1I0# zcnPcx#>62dG9H=3shcalmtJw)#I14vMn5BM_cp^??YM=&hBvZrf!X1#ENnW(Y6C9@ z_Zhi4Bcu+cSTjrjcBkg6elp{+`hL7y@gm>Na?i||Lu>|)vOew&-wzv-HLR+CT)M)- zB8tRoQm$Lpf2{1Ch8S&bvTnNo5*F|m>@B|FXWzn^0YfB89+gqE!lL+n1_S> zn#|PROJ~O{%JSqAdb47N&DyE})!>>*6O9Di9H|c!ij4G65Yr;%{p6_V`g943q7V9->unBQ32McGDe(p1c%r|PIktfmndipcVU||uW zNLb)Xz00NfhKY_L4S`>ein`Hkoq$cgu}5+7agNJjU*|vPL7{k`lRuk|q`Ka(ci&aJ z&1m5bw0!ViM?vemU{{V=U$SkNx3saSc};l)s;R}X*&XvJqM2`2mNC0pqU+ z&RD84L$>TktJwOb=xYoP#V{cE=@KD7`9)Jf1aW_ zE;c>XD%LB*r04{#uAqZ*3WM{3I71L4L{U)TnT1pYXG~^W*#}GUymYJ*cjJ)S^2S}& z5i2e(t_sGlY;Tu0bh0a|9B2sYS4ln1Sj|j5-W;7T@t<`}%i?_7%2)4kN0G?67mdxe z*3~cmZp$9M4Z2yY1L?JXx4p_~S1xO`GnAevi&&uk$#bX;%)r>Aa0Ew}xA}sH5CFmyt$@M!ipGOGr7V zS_z4Zt*Me5Oey0O62}n;h^0)NoSFFvWa{F$c=)!|YeXW`nsZ(HhF{w)5jIUZ^8O+e z(9kp<>}DCiK0g%ph%-CvGoyXG#bnrP1Iy4- zuD4EebLCc*E1-#{9E52amsEV|>6I73soC;fiPtkbBY=&Z-?s+RE01}X=5oN1v?jyD z!>Y7nyIoykpK)<>8yh7^-cFJRCIgd{TF3jcbZ?6Gxh=^#T>UvV@~0k-i=)wioaJAi z%;pZ;zFOcPSG`KrGeQ~f94SSn#|ph8eCrxq*F&kn9kNB4;GshV)L$khIA1=6iL`hp zYU-3^Om)UJFEVH(C1*F+{KM9&cjYuOEgher1!pT^lCz-*w?6#*7bjvt2g6C*i`&xq zo;XlhOmg#VXlPE_aq=%oKKtiI^P%{8pZlK8^cWJBf>asC9`A&ok*sGcq+g+s;!+mr z*!!!3nxF_}Y;02M8nilYPdw9!OWh+@#<2@}oPJ7LDy8jKNi)MDvydEi5ih+V#GX6W zWOD~!<0eJkp`m{r^Nyw}x_Y7Q!cW|RQc}at&C8iLMVa5_y~}=9Q^u?tlqrezp3NDx zjqqo*eB_)-#B(Y7@^!HFJDYD8yh0`ia)f~=VcI1$6v9w_umn5zy>;T4^$hs7NRUns z_c|h}_?}2c5Tb3LlJ9*U`G((F<)?+LNLbhNuCo0lsb`kaVZY9DV4>o+Lu$ zi{BjOP};XiLOcfRTka2t58kS~I3!)Jd>t;I0hx62(&S^``ti?mSbR{y~H_GwNBAdt>r)8^$&WD9X z_34;r{zhf}EAqziq-XCfNfj+hTX2j1wej^Sf^{=C$WK(nAv+NR%E`5{_;!Z4nKCv{ z^G@qJ1!W%=d1PaERB6{vx|iWqK@$CR~)XleM zf%ZkY$M1h|ZK{bu&-@tu1l2<%?X4 z6!BH&I%%))Rh_!c7pG#8m3{Jb^BWkr@w;}6cMe;15$+ow?P98=byd!k+?ep0-tzqx z@q^8sb8xP~y-+eKN0T8yJ?b(XMM7FSM?bUU;p4}7&8m@7Y3Qzfwbj|Ur{^LT-FxZXkz zDmGqpN9<57z}#Z3cD%uFY-#wqBMPHz$iO0Pb>Py<7g0AvqLnC%X+TJQ7|1;B8&spuLHDaOXStmxaDKSCKktDtU`DfFvE0eb~u z8$+&wLTKMJ351A=k-4|^ZUqKcIXUiAhERGf6L{b3>$$22~!eXZ>$4}lU#vIpi!V=&WwZ|#y`e<3C%ZOdn z`r!eTl5j}wx_iMt%7cE0OCeBPbD6#ow*5NK^&Prw`bxEM)N=dBN85O z^!T9)JFke9?yEkbv;EGt<%nal!``+H#;wt^+{-5Zejk|9xpA9#O~lS1^TuoPP&@JG z-W2U3bjvZ2W5G;tTH#diZhDHp@`|?N*kp`NUqD31}{v(SNMTPqjvELqa zu#@a~Qa|^nEk8UOm%(XnaWz-W2DM&~b&sX#-UOlDcT{BtH4&Jo_@m$19drMN6I-Bi zMCU6IgKOJH!cmnHijSL9qMG@JEpdbvO5w9QT#tT=ClMS1)5eprVcQ&CpN^l?xj;3j z8NL(H(;wVh&46k@5*d1EUSre`VY+YuzmYM3%mPTAgduzf3EEZXjUKkBB(X2yb>oJS?ZOyV9y`COH>_NExF{QW;1U6c&29u6@a;THI0bBGqS zE`4XOmCz(+cAKRy3nBL{rg4~oOx*H~jMzPuk4~%jFw@VLmZD9;2z9r8xxVJ`hQk=z zqB>mhS|C2Qb+v&*YU3mq(qqdtS?cp8+N$iBUWgxseF0aM_dVmFK1FM2eNVOr;Ev~z#L^rFW!I;;w$j3nH;Lz~WK z4yNy^ht*;(L#V-L4PuC;l;+1Je?--H`jbL}%y7fWEcLt%hvNtCr5la6eZ*$M?+(Ho z3I1??fbknPgu7ej|9V>QmgPS#z;WOO9;4ityp0PQ7gQ3ZXlZEyjj{*HPR?@j0-Nci z6kX9TQ7=_Bb$|Yjhx&7lB6YZz9{FW0nKBF_GD(EvaU9rfC zu2ycI_>7?Fa==^9{a6Z4S_Sx((pFAaPhs5@7ZcP1_HCp+%+O%krMDsF-z!<+qTvt$EYvmAouT1lS!>(s0W#|GW9O*4&oc zPjBqt13oS%fR4Gc<2DZ4W^hcpVE&zZ9!Aoh z#t_`J=;`YTT|FpW$waxqcxY0D_06to)a|NTp-~BYulceI_k&TW$3%SuUct@PIA4E% z-;E%9_l`$hRzg(6YX_iZ{KJlA5y)S4xP^J1G0fV1JhiKxE9+H#ULvhVB|F2Nqy}fd6??9lc+pKMiq3Kae7u#FWTmN|Xj%Rh8$YxoY zK!omZ8$#(G{~UR|MoAj|^|@@tibVG1Nc}+D${t4}bCSrq)A#c znwm!QRmR$nr!BMEBTWrSWUF`M0dy=(U7maC)ZyG1LD>KH1+I>d9Ydp{>YrjWbE+xK zw2JW&!K*wsx>&%qaic*-+u{nJ5~$5exa|`3kGiI=?$0o$ld(K%Z$5&}aZ05`aJq5| zM{g2VB~Jg4&H@0(gb!CDAcM>C(&Yk;Q8rmi|3T^`mA2&i&Q*>guN_bnygxS6ld$-Q zI+_n}Oz3eq$d=MzvAmF z@=fktF%$pgv+H*VrFWvh9cn~GY>Ovqrr)XM=Uw~mfol%QtT|B>czOG)X2$0 zbkf9e|G7_EUq8_JjvMe&QnDcTb$`kc1o;c1npo8C7pG`&aB$@`=fbOF2x^$bd7v)3 z>KbM&cU~l5syibOq-0}+bP|!2kOU3>^fjsCy z&bL%P_3z=QcA2MIODcsjY*&Cu#$l10T(qUw(Rnv!x9gmNwXBih^_iOism~ifTGlXeU%g7Q0dnt0wxsrk50q5@tav!mfO9JL zIzZde+ru&#^DQcG4WuC$r|GE23{6@nRV|J)qLzsqs&P+V!cJT{utcGJ09i00E#MyZ z{+2w6tlZknh88u9w(&`&MwXVBVi%Q9msu{&xsx=dr>Y4DWbJME)HF`naZBpE`c1UM4{(|}H0_GfV`w%hq1Z%qO+*X;=zg-Cp1I>IK;G?h9Z{z5XR6oAx$RnMXpJ2mr2 zoWbICk2I$VQs_alIc>AiMVE#U)D{`@(ja(!GpdYMw?X$bh{Lp-PN)kPA^HIY+IBX4Mual1{i%j=8uhKqvygGrYi-Y4 zSt*jXgjP8-k{O0JOZvYAZgbSRd*F?Pg2O*aLq=0m-QA#Gg~v_)BY&*ztm?vzAbJN0 z6cS~cV!aDi<(0La(FmT9@=}SGWy63!{C@6;6>XOcXANY}u#X&gi+yKjXQOUPx#1TT zB^wr7wWEcF3s9Ie0|OR>f!z4^`sA*s{EUCt`zr3^C5cEX^m2>?Q;q88S?IgSW{os^ zNU2)VqlBYWL$coip>|Tn6KB$1*WbaHk)*a@pjWG7xhcUvwCN174ke_(0N z;?kMh)~&hMSqxxjZ3J` zVp#0gENKE#(zg%h%La-|PCW$qp*N`U z@N(GkI#zlySAnPu<{VgZp_e>~vl9n|ln|4p6Wo)(c_2?9hk3mC4XbJaqN0;nrKQA3 z;?&~)u~^iU)ECTvj1SkfHC#S-byG9HCnmzPx-5e0X=y{iQmDM5B9@OVyP<;LR_F%x-!}|IRt32eYB09o?Oscj+f6@hiuLRcjDSXA zZM|lVBp3eC&*>Ghl>*ZZ0+yerfC|4Cdp7KGeyGq)@>|7ox{kYOEUm2fA@^tf@_Vta z-b2VA`@hl>(HUVHtqCy*fPn(iHqwHy1VY+MJ7k4RENZ3HrIlZeDRdWTbwqPh)Rf~= zyIQ2*h_Y6JAcCe4X6y$IGetP@$us3q$ z`_zf)ns>${Te!CBi1>OxF3$CC?@NJheWkSdJAeNGhi=SD7}cgXkUvfF3*=}tzSnYf zxF6WoFj8yU?~Wh8&$;NX;)T7tyCY|0U|se&BVVqntrHe?G`sXGP&2ksg1vsvs1|-k zcL58ZJbHZn+tC;fAu0V_V6NPUCtY`H>|UYg$63S84e8g=sI)6KCv94=z_Wt_jQG)% zc6KXnk#0c6sOi1#2^;5N{f zdOcPJB&{%wwxr8uEe?PcJ4>REqrIrE4&2%oltej6 zxs6iGpkrAE7o4>PC0fs#OczhgUU}{`bGk);Yhn>QEnCckqtgYDeG!VIwVH9a!hUe) z=G4KKFKc;y?7^9Z)1B}I;Ns#!V-$^5v7kHn*)q?ixm1e5i9ps;o(W<7745P~V#zhT zHp}-I!p|~K&z&goYEPk8?|dG}mDjs54H_2a%VzDwBmp={e;*CMM5wn_V(NUQFGy5F zwQCzp0Hh5c)*%7g0H+HRs|fmZd7}PECy!aQ;HB`p*O2`DFK7BPhS~qu_y*wjUwCZ< z;X9nT@-Yh_?PaoK!lF659?^M`K1s{P%ZG)@TDWlkxhlw-Fp{$U!}m+^6y(_G9}~le z)@c#~%YgBXB<3DHtyGu6;6JpmS%U(ORl*f3P1M4#@uY>aJa2XW!=e6J&IQ*J5)vk% z0luyg4Gvuh3ptR3v2{$ z>kiy!jSQbZw;D7UDZcxIj#On9bIWt|sdH8nj$xe?wuc*K`oKq&MgG%4AuUdHZ3KuO5?(Q{&PL2>-~+rUjVQ3?f~lzRl9uPN=q#cWR`)RzS?Z2bf)ZL zyxXtFY-!8ymA#M@RD4$&5TAYK_x99KP*@0Zmp!`>;F+s@BcYLOqVg7{D=Q8Fk`2Lp zu!Js&zkGbEQa^-!vmaRa)v)TjbUI~RMpj~pRCOsn(p)M#F=3N|SOMFy-MotKM7c6%qHZ%3-j3T2}g_yPno!({-O+kTiJP(li zJA3cLpPx}u4K|~-mYb!O(_0FT6}w}??jfzJEjN?H5h`kWkw}hito@`D}S?-v0<9GeLA1@pFUf zUx9FYio{%U?+k+Sdpw$y)8MaZit&AfZi}hUUlAE?3OY7x)a04qUEH**vKoFwaOy=z zM2lC=r{sKqofm4@1pI98_e5L{Y?w70$K99Jw!GBvLa{pg3K4Kx<>unZ9b7RBu&@jJ zoo43D&AIfs32)&)fV7~pM8{xAP^rmHug1UgD9T#+UJ)B71Ht2?hPFs0%1Fn%*Q8Z zt|0r@roHg;`~_D;##DnZPl3{5F54A(;;U&Ce-KVSvFz2eT0AtrN@GtMT`BhJ4fxO? z#khmK6g8SSh#A*ftY)mnLXao}Kao!_OiVPqwccgUN4Ysh2J2u3(K>Dhn`21J$ndDe z$1%^7GrsT7!+gV1W_hlKxD*PRS~~uQjN%hw1&K6ira3(>@-drheU*zx$S{eXX8V%g z!1H@h5??>iukA0=o9B!0{fp1t#_|8rY_qvL4&JUs96tyPlU)FBP*X&j&q9 z4}gnK5eunb4;DdYLLf7-)A?eY-$w12a6P~<0*Y@svDw?GeEpIWLRND|EsmO4l#)X5 zywT>>n~W69NH>o6W8!s?@5Sz4PZ-bZcKkP819mB}n1U0ESZ?kFy{~kdQlj;lL~9En z=jPP(^h|DUDFSZdOmS%vq;#kj+wcbs10#c3`=aJj8Kgu~-Fw!TFE%ZrrsMdhnLe$eNBjp7v8CMq=n0nS zj{kbgk@;|>?y(+?N$u)EYv6g4)p%TEzjfMj8tb*EBbjIivN!bl*-Q(JdTuj%Aa+Ga z`Qyvf}7ruoLiw5N2SX=3wTrtjmyENkZ@(|}{WnNdX=E(@ou0@ z6Kp0enFoP9%aluG}Y z5q3D_B#)Pnl#~t4dCz%sGvCZRcNJtNvvH5bm*_Y#%(?+JTmh%qwIQ#yJv-@nPxN}6 zXSQ5Kn`icxv9flxNv+E*t*F4O*OUaD=u4E7an5k= zqoIh+7OOf~lG9&g)D4|ojR|+dkgEXo9`112P-)3O15U2YL@Uftp2)+Fo-iCNQo^v3 zcv|Dt^xE_1sj>CGG&?CX6A}Z^&X520HZ-2KJu$nvGvIMPDJG(q?-nF>30 z+e-tjVsiQ+AUPV%S3g+f2hU!5?MZFEeN}lLOrVt$n zpPJQG9b(s!{a<5s$j^T~SpB5#<$J>+@1~<(J1-2U+1%qwh~O=;4nz9JVLlOevy_p}Cu;O7$f$-=@q3E4bgX)_f~L zT`ATtk3&B4P!5E(u>tLnlT*W%`iU-mo5IFpbErY$wXK@sx*5jvlCY2>Y){4_@CHje z=nQH+??NsA;C{N?!~=xln2&ZiP8FFB)mf%|^TZC0BKUnBX&efUl1#W4KDksJfiTb7 zJo6{(gNG~rb``#o!n`4=JDjq|F^_^@NBEFoti8v{ntV(uFUd?IE-W?ztEC`_niM-a z%al=*&yDE|4?+A_g}Sv*gv0^$#IvR&r2r2Y#i9z(4~V6frlRFrd?j_!&1&P~;M#nU zjQHm1%~c4yx|>@##|DUu|BWVOS#_vvy3kB>BUyM%${3C zIrBSz?;jAb-IggwuYakgg)lyW@}h?8ZR=7INyv||m*JjC&zS*j6aD|=D>+zje4^3t zZI|1^(ys3_GSY{Do<3N#`*gzR|ESn`ISwnY?;+^5ad)_95D~l8pTvaE&%>;$0ANV! zkX%#Yh=0g#5*{$;pl+dP{>61lBAJUu(CqNDp3hEVC?2Bqf0U@9M&>kTE-QOujseND zMMgO-%4>tkM7;@%w*cXh=Lj|f*WFaX;=Jj}Pi<$QKv=%E=*b6Sq2(-2Wd0g)Q*~ zC=0%DB(eD5Z(s|M_}*{KvgcG(U^wrlhdGbkl*jFTd9ReoiCQ!wgsV=gEOy&i&2v(> zpiKpynEk6KAt^cR$`@6Mv((GW|B$aEqaah|bHd2E)q~@BNMD3j5||*i@_$IZFkig5 zt+Z+^i?!iTfI8quuR}{34J}INwP|#<ejR$Li+YE3^J%ZTDzpZO_=1t+%N+6_brZ^lVu2 zt03ZshmR>-vd`7WHZ5bFa%;OO=*AlB2{AD$b?z>?5dE|`SKTDTC;6L?(iUHpQhp&X z;7qJm5Uv?LWWKW=on2hEan_8^5+aQje5A{KT{66g#y9MEgH7yz-6;Y;i3x{>5Ddcoa%UN zcOp9879?kvSHCCsyF|_vqyFZ4iDhvhaG%B-4;Rum)%-wPnNIsU+7|y!ZQ6b!Kd}Oy z;A>j$UPwWqZW@`gvbk~`{dcO#sXd*Ftn`d1p0n^OQK~JFylvuo9oKp9gW#z9k3#eH zB4LAHMVTO?T3EccCx9l?W0SS<`EvN;xOQylE>OH8;bi@;1SV#euA9hGGtjgf>2t;X ztLd3^ScjXCov>dlC&zDh40(D zyQ$P1-aQU5`BF|(6Ct%!WEBdqch2$Bb&}-C>6X_#Pv5R+m?bSB|L)YHzZ777%(r*7 zqGe0Ln+RJE^LOhp7AMU8Me@3=zJ#mXn z7a%0}yP(&s)DO6wZR=MKJZ5Y|rtbGsOTSAEJUm_6zoC}QG_6~UPmchHL;VvWz=nmc z@8M^yB+Y(v+sDZFH)HnQ)PBBb(n`V@y*mXS+Z!P&G4TP}w=fwzzNfI~c~}0lP>r~V zex{!oGuX;rN=L7$9 zQ$4IH_4M}Z1dK8-L3P&x;+1&`VcQ$H8msw9h}nO1!8;CofEg&Pc22F5IE?G*-EmS= zQ~zq=h#?V%_w@ErvNPkNN5S&E`NZ^CIJg@q%(oC~aly5>#FDqp$KJ}a^oV;c z)0k67V4-7&=Zm|$tye_yjse*P3n%W}wnCzyr9TN&Expv)xQRPlpmMMGR0@mmP+^7w z5)DiZ?Rp~lAi$e)+P}ka89w|p*n2+!$6GvJPmP^-A~OE-jU?ekxpBQQX<)#@!wYvF zy}dr2&*(1T*@G$gVg!YN=o#qXI`_II5m{AZN))JlgZ1sU2bLHTFV94Z#Na;?@Gmf} z^#BPDDWVKdo$-s*^xWKnCt})dK3lvE;o#uF5J8w_Jna%nWgr??E)~@`(7i+8KpBr% zXhgI$%mY1JK7kAN+9T$b1>USoAEi1TU)&#=Dz9fg`9^0kY5$UEY3gy_5JiKD2lMOe z7udN0t1S}YhgM}O>OkAQ$8J&k%scDv26u&ox!!l04wkSU#S1FFASKuf=-ez1@&Ud>Cz^OGG$0j+scuREpYtiuO!L z3;|I|%uNEs_aVT4-HkiE9XBi{=9A6*`k&@Q4UHRwJ>8p@F7fVfrjN)lsYvpFJ~p@K z|M(c69>+~rP=-7g?vPtr+gHR^@WUMMW2TZF+quE`lz}k;aC-}W%hv}f{PsdWvxE?ZpEocswsBA|h7F&WHGiHU*E2VR z9tey61-7)pF$%r*cm>CF{>C|o9vZ3}YH$93m0fu})Lq+mTLzgd5y?6TQ6brPl1OAJ zli$Nfc#Y%Z)+GQljjPv1E;GS;oF5OJl}9VvP5Up67m^&-1?T=l#6?7$3{; zoa>xx`F^kKEWf>gaM}n7~m4P42?`yA53>pQFv3k-e-J6}?DV4|1=-WMFJDcu}{S z+##@#baHsjdh}(+x0eKA>m-HD2Bi#1A? zt7SXE{Ty84FC;HN)e|xq=5MU1?ZM*5=6cQO3vAMTBOG%=n*oOQZqIDcdH7HkEGS6- zN%AkUt{B4^TnT{u$as`zQ&x4-$hH3hW=$JpS|=l8cZ$Z^CC-A!UbLyXvhP&Z>PS7D z5nVshO-_#9-k)Ww}=RJbs^8zN3RlDPcUglnBrvVAK*zoaSch^6fPZnYacFQk* zGd1(-Z{-@R2|7+S_~UpA8A6d9hS9>j1{})1)2X+!ZC`m;!Te9tcLFzZ2FRudlYDlB zq0vIgJ-XY*XIB|tcjb>+bjWbl0=1a%jt_hrd(rdQ=u%a=pyWfvPRUMiBGuu&KoAi* zU2!GDwd|ESfL$F|Q zQLP32xh5tyXZ=K2v37{_U>oSFDk!XhCdt~3ohgzk;2l>hlV`}`#Th=o`;>nYlw?sRN+z(4#ha`~j`}aRtXfv>$IN?xI&P|RHMP1Um zdB-Bc*A4r+qT+ZvAM)H2Yu=6#yFO>I{#?!-z7_2#Tfh_UZ*STeXR^3c`o6fS^2rAm zxQCYS-ILZQ%xOn_w77cydMJvo-K+xXAU&^RLS`nsB%D=HOsw7ZzAQ;VOh%U(3Yx+l zgF|3y&2KnhHh+pX_x8sN30@`3en|%}7JWp}_PY8?P6=_kP-YD<(77nt6yw!!M@dR0 zQJZ01;Z^d6x7puN6Y?zY!{3k9NV6CF<``!^c*+t)gZ zKi)qNQ`1QY3$S47zR9}#7+tlnJiD-1UR8y-Z~ESBd&`nI|96+LFhNGA0R;3@n={nA2hZl7}`t zD2%q8FETFXiDUsGB?T(Yu*^LMy*QH7E%?2$iK3oal9cw2>}l)ESHgwPD_R0Hn$Q9 z-&H8*ZV@=@$6N4E(--jv3>Jn$Wa_&KtcMW+1|+&pA)o>px_?t7?E7PidHp@hN;5#d ze` z^C#}X z%Z>oe9MJoMw!HTEVdRcdY(r&L1VpIbOs>AN>Y4H{bu2--!ch&cI=Wcr9?+|c?bxU5 zS|E8(xU>`d5SMA#vU&-@dy%q3El8<-xcY&P9}bb3cyY6Mdx{C@0QXov>}Y1V-vA{= zX2~q)(LcDJr@V>w+;A11KTL}`qvV#5KRzcJkG5ygdZvt%U%_G2@39qs!L*7|-OvkG z=ZvixK-yNK?~SDWV#*}SdX;33cXw0;w?9#}E}K`ux11vs7P|;OWJ>KBB#aG+2mHa= zgXDV{6l_eM$FA;71Fbz1D(7ZC(qD&txc*sfQY>TjLS}`f_sN!~;r=OnXq&^tzM@xm zvqqB$fz6P_Se(Up?f}u7`!x!WJW;)(m35zWgAc3UH^XORhr~8p{`H71b=T-nk!X@d z7Qxu-T-cB~J&y=%ujAiKbMbjH$fwEN9jv8hH-qIuyWkgv&RF{+^G( zqxrbFoTmUU$1+~>Gt{ws8D`DHHl?JiRmJo2krAOYU>jUruuHmtMdn%fTQ(F@S>3 z-w)dJRvZwjg(FYtqkIu8Sb`h-82@_o@K26tnJN1`w`>!^J9%~A4LWoHjSo5USI`12 zXaNdoi__xV_^6fdWcao1)VcP=5;s$H7~|ghq|v|)(dh2WM ziAd+U=!oeSjI6IyAWMuVhI~Mp1w^vm6C#={K;2o@NHfh-b8QXTQGi!$;JCJ80iy)xAWRc?6)42&yc$Lns94Ksx{T6R4a z5jD2!fj?pfxc(fy&R*Ak)pIn%E<8G!mM~S{Zx=Aj1cxZa_c{9`IL4r*^k5w8A?dSB zzG1xLI}x^9cTC@Xz|h>3+uop|VPik}JaSE%z^%O_1!(x-utJAUHO=}b6EC`L43(E4 z4ODxFp3G_A=NupU1o(9caBfx>vb!wD3WS?s?~c?Sd=Dj|<8)d4TOxZQ^!wx28+qRO z8~NTKN{6_}c4%sWI5Gp=70N;O0Cd1faLE(1^zuz|DwC_9yt;fF5(MJ7&aBRyPGML} z2mlf?6$m9^G-J>?mfhU$Y>)WRyoRmUc8R9SAve3|7`a`{nQ!tl!pLBZ>s&`e17OfF z<0&6jt3?VW_eBNk8{F5COJzC{qPcKmA%S(S0l;bq#X}B(@ViW8XCOz5z!D2Z4h|I( zd&rZGs{nKO&X8%rzaKtRHXt#EB5t!VcH#)m!XxN%E9;xkdL50~{|lle0D~&8@XdagH%ytL zs7_R8Hh`^`1~5f5$H5eVUmgdZgu6HAnA_Jk5vy4-iAYlqW16E%DFK>A1b!Pu0{KoqOO`kp8kXS$BOWQtY5Sbij zfV?_6w^5K0n7>k-Lp`zYaVehvYX8FIoAZyP1nv&}Dj zU|_fH(3oWEMo>QfP(W%8e>Sb!89`mabcxf+f+W`{==5=(#fSqASq7YdVN|2fc}t_5 zo2IuZA^CQ+WSM7^DFG$(kmy~(WN47!v2{}l;Dn_@g8y^jD~8) z^oo3~bHgAS4$~TLkd00s(`FcTJBk2XoIrH}n(UWGtEdo!!Kk_n6`3w%fM%bF#SZaf z2YQ?Uf_v(h=`Z2QhZi5~7q(iKo@2+VPHZz`waauZxes8KM43FLIaGP`E z-qh%MEm;=htx5wxP#UQel%+sK->dDXJ};FK-rwW5p=SA0txUzo$*FyNG=H|ocZ!`k zHDF^BE4LuJ9`~)z` zU`F5tDmmR=c>Q!OQk1@e+EPMjiN2p-+FtGMvF{f~bQ}zlREGz?ATga*`19(u`L)A0 zi#IQ8oJ{w2CES~_RjEo>zq!X}bd{vGRac92Tbl?gpH z>N6}|sM1U~Lcj=ZXH}gS`M8DP`Zs#z(xMy4V)j;?E+idi=iulF z_9U-P3Ol*v)@CcqWP?DS-Mw~=V&9DLXN#9W;M2Gand)~E`{;$b#o%jfRLGnAa}fJn zqiI_<^z9*DDXnIyUM%!IKTd+E?hO{_y{q3u;ZjLrLLkUqYIH`2I;LeTSX(((g-8a(Dgm0oaLMM)k78xyPl|8ptj`Aq# z(z@VmkDn56yx^bw)hqy=zVUe?LS_J}E5-9J=F1e5qb@VYmoHx`ITXU67Y9xFZrI$& zJmpW}5hQkCjH+N%L?YuD)Z6}*&p`TXE|nxRmAv9KUbmA8B^kz_W4cuH5v@sAw>ye@ z?_IvaXO1m3(m;E9yXXtYkykzbn&i<~6MYjfgwtT3_xln2g=(H2lxO8mqY}T-2hw(* zeXv@)`lq>O*+;bXq1@dM$f%gQf)k66j+^y@A|Z|Jhp^0|qH}{oLpcQnkpmYy;&txY z-nH}9^lfSYc>|dq4&m+uv4p*x`#Y7xm$T`$ehqOMN@mM|>=dYf6Is51B|O-hn3^kG z`$oHY%9Fat&D)@?Ic+16$+c}p7+kcSk~um$IyD)T`?354xC;)0403F@hO_PQZCWAH zSGSUvFc0>G;$%i-vV0M3PKy)kC_T>F$r1}6%wp_xh)yht^ZZ?0fYfwDNq$hXfj5jL zjBNfrr}+m-$mxxUKXE4p+*N3psXTrV5N|R<)#xNjZ?{ik{bLNFo z0uG3Zea=w2y)I|*;9MRXr~RMbEtf=f)H>5x?B2WW3ntm%mpAv@r^Om{^GEll-lOq5 z1zr2Evs!X`19J2W^YZf4;g80TsSr^X$|co%CkaR?Hv&FG#=~Lp z_ruS+d6DT&)PafEb6mzZYB_d5BVDiS<@-g)td@Q7@dVrVoHRTRp(>3Jx{01b)} zG0TJDK$AU+eoZ2Rz7K?bc^+py9<0EUj{`)%wDu3&*RqO=ip@Xm9?S0H9g2{_KSPy$ zM3vU-Rzr*(ihF|uPVG1IrJ0^LnHU|pUiHi05!a(sJUr94kM={Kb1Ehr5aacSrwK)* z{-`s~fTQAm<30wYa`F5f;}v$vx0HUF%Pha}C-&gRG~oC=S%eu(d@0#1q4h0OAYMg^ z>mkCNcX`VyOPAi~&g0Aj?B&#wtmUmwW7nnRnuuiNySl#``ebifgdBQUKt+O|PG6rE zep5op_`?A)lRp_2U$0Hc-2ms}b>~f{B=*8VLrfa3K1Tl}F(7$4_W#Uh|Mlp9rou)I zB5$edO2T((lJ&GnJS*bsV&ZWi1s^n-Gu};7HC2dg=(DiG0m|34pq-2ULLogl-USyEzt#L#SK~=-W?Wg9=>mMkU1pX9_e|v_a0!rzjMPN-Y9qhKIxgN5t+aRYL z7Z;QA)h}!WV7;`4PK)t}0RojnZpOo@b zDU$NQi=)aNPDENsX*4zVjVbG#497c`cU<9M15US~lNiMdLE%T+Z_e$g#CL;_VTt3f z^z?+RES8sDPphjHQ*)AF5vje(&xFA1z;kkPrewq8@gRxFVFt2bfGkj7zk8H?K)lRM dYHaP020n6y<>O~HPAc%FuWNL Date: Wed, 13 Dec 2023 19:49:47 +0100 Subject: [PATCH 44/47] Fix for F --- Commands.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Commands.cs b/Commands.cs index 9a8d55d0..d1b3f033 100644 --- a/Commands.cs +++ b/Commands.cs @@ -210,7 +210,7 @@ namespace WeaponPaints int.TryParse(weaponDefIndexObj.ToString(), out var weaponDefIndex) && int.TryParse(selectedPaintID, out var paintID)) { - p!.Print(Localizer["f", selectedSkin]); + p!.Print(Localizer["wp_skin_menu_select", selectedSkin]); if (!gPlayerWeaponsInfo[playerIndex].ContainsKey(weaponDefIndex)) { From 7e12b89a9e3a17cd9dc25646c44e676b19ef1146 Mon Sep 17 00:00:00 2001 From: Nereziel Date: Wed, 13 Dec 2023 20:02:01 +0100 Subject: [PATCH 45/47] fix logout --- website/index.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/website/index.php b/website/index.php index 54c4794e..5339448e 100644 --- a/website/index.php +++ b/website/index.php @@ -59,7 +59,7 @@ if (isset($_SESSION['steamid'])) { loginbutton("rectangle"); echo ""; } else { - echo ""; + echo "

Your current weapon skin loadout Logout

"; echo "
"; ?> @@ -260,4 +260,4 @@ if (isset($_SESSION['steamid'])) {
- \ No newline at end of file + From 52962518fe3d594ba5ba0334bf6e2828b3cd5d72 Mon Sep 17 00:00:00 2001 From: LynchMus Date: Thu, 14 Dec 2023 12:13:31 +0800 Subject: [PATCH 46/47] Create zh-cn.json --- lang/zh-cn.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 lang/zh-cn.json diff --git a/lang/zh-cn.json b/lang/zh-cn.json new file mode 100644 index 00000000..2d983585 --- /dev/null +++ b/lang/zh-cn.json @@ -0,0 +1,14 @@ +{ + "wp_prefix": "{lightblue}[武器皮肤] {default}", + "wp_info_website": "在线访问 {lime}{0}{default} 更改你的武器皮肤", + "wp_info_refresh": "输入 {lime}!wp{default} 进行在线皮肤同步", + "wp_info_knife": "输入 {lime}!knife{default} 打开刀菜单", + "wp_command_cooldown": "{lightred}皮肤同步刷新冷却中", + "wp_command_refresh_done": "{lime}刷新武器皮肤中", + "wp_knife_menu_select": "你选择了 {lime}{0}{default} 作为你的刀", + "wp_knife_menu_kill": "如需完全应用皮肤到刀上, 你需要输入 {lime}!kill{default} 自杀来进行刷新", + "wp_knife_menu_title": "刀菜单", + "wp_skin_menu_weapon_title": "武器菜单", + "wp_skin_menu_skin_title": "选择 {lime}{0}{default} 的皮肤", + "wp_skin_menu_select": "你选择了 {lime}{0}{default} 作为你的皮肤" +} From 3a1adf8d4ad2a5226f9e09cdb5d830c97b444a9f Mon Sep 17 00:00:00 2001 From: panikajo Date: Sun, 17 Dec 2023 13:05:50 +0200 Subject: [PATCH 47/47] add Ukraine Language added Ukrainian translation --- lang/ua.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 lang/ua.json diff --git a/lang/ua.json b/lang/ua.json new file mode 100644 index 00000000..7561f5ee --- /dev/null +++ b/lang/ua.json @@ -0,0 +1,14 @@ +{ + "wp_prefix": "{lightblue}[WeaponPaints] {default}", + "wp_info_website": "Відвідайте веб-сайт {lime}{0},{default} щоб вибрати скин", + "wp_info_refresh": "Напишіть у чат {lime}!wp{default} для синхронізації вибраних скинів", + "wp_info_knife": "Напишіть у чат {lime}!knife,{default} щоб вибрати ніж", + "wp_command_cooldown": "{lightred}Ви не можете вибрати зброю зараз", + "wp_command_refresh_done": "{lime}Оновлення скинів для зброї", + "wp_knife_menu_select": "Ви вибрали скин {lime}{0}{default} для ножа", + "wp_knife_menu_kill": "Щоб правильно застосувати скин для ножа, напишіть у чат {lime}!kill{default}", + "wp_knife_menu_title": "Меню ножів", + "wp_skin_menu_weapon_title": "Меню зброї", + "wp_skin_menu_skin_title": "Виберіть скин для {lime}{0}{default}", + "wp_skin_menu_select": "Ви вибрали скин {lime}{0}{default} для зброї" +} \ No newline at end of file