From c55a40ecb1861f0c98b6bd7cf52e5acd2796de88 Mon Sep 17 00:00:00 2001 From: StefanX <60297289+stefanx111@users.noreply.github.com> Date: Fri, 18 Oct 2024 19:48:42 +0300 Subject: [PATCH] stickers, charms, nametag --- Events.cs | 2 + Utility.cs | 9 +++- WeaponAction.cs | 114 +++++++++++++++++++++++++++++++++++++-- WeaponInfo.cs | 34 +++++++++--- WeaponPaints.cs | 2 + WeaponPaints.csproj | 2 +- WeaponSynchronization.cs | 80 +++++++++++++++++++++++++-- 7 files changed, 225 insertions(+), 18 deletions(-) diff --git a/Events.cs b/Events.cs index 9c0e8ca6..f5caf97f 100644 --- a/Events.cs +++ b/Events.cs @@ -89,6 +89,8 @@ namespace WeaponPaints g_playersMusic.TryRemove(player.Slot, out _); } + temporaryPlayerWeaponWear.TryRemove(player.Slot, out _); + commandsCooldown.Remove(player.Slot); return HookResult.Continue; diff --git a/Utility.cs b/Utility.cs index cda54412..21e50b5e 100644 --- a/Utility.cs +++ b/Utility.cs @@ -31,7 +31,14 @@ namespace WeaponPaints `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 + `weapon_seed` int(16) NOT NULL DEFAULT 0, + `weapon_nametag` VARCHAR(128) DEFAULT NULL, + `weapon_sticker_0` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation', + `weapon_sticker_1` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation', + `weapon_sticker_2` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation', + `weapon_sticker_3` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation', + `weapon_sticker_4` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation', + `weapon_keychain`VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0' COMMENT 'id;x;y;z;seed' ) ENGINE=InnoDB """, @"CREATE TABLE IF NOT EXISTS `wp_player_knife` ( diff --git a/WeaponAction.cs b/WeaponAction.cs index f34133d7..20e35937 100644 --- a/WeaponAction.cs +++ b/WeaponAction.cs @@ -5,13 +5,15 @@ using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Timers; using CounterStrikeSharp.API.Modules.Utils; using Microsoft.Extensions.Logging; +using System.Collections.Concurrent; +using System.Linq.Expressions; using System.Runtime.InteropServices; namespace WeaponPaints { public partial class WeaponPaints { - private void GivePlayerWeaponSkin(CCSPlayerController player, CBasePlayerWeapon weapon) + private void GivePlayerWeaponSkin(CCSPlayerController player, CBasePlayerWeapon weapon) { if (!Config.Additional.SkinEnabled) return; if (!gPlayerWeaponsInfo.TryGetValue(player.Slot, out _)) return; @@ -52,12 +54,12 @@ namespace WeaponPaints weapon.FallbackWear = 0.01f; weapon.AttributeManager.Item.NetworkedDynamicAttributes.Attributes.RemoveAll(); - CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture prefab", GetRandomPaint(weaponDefIndex)); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture prefab", GetRandomPaint(weaponDefIndex)); CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture seed", 0); CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture wear", 0.01f); weapon.AttributeManager.Item.AttributeList.Attributes.RemoveAll(); - CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "set item texture prefab", GetRandomPaint(weaponDefIndex)); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "set item texture prefab", GetRandomPaint(weaponDefIndex)); CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "set item texture seed", 0); CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "set item texture wear", 0.01f); @@ -75,9 +77,11 @@ namespace WeaponPaints var weaponInfo = value; //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.AttributeManager.Item.CustomName = weaponInfo.Nametag; weapon.FallbackPaintKit = weaponInfo.Paint; weapon.FallbackSeed = weaponInfo.Seed; weapon.FallbackWear = weaponInfo.Wear; @@ -89,9 +93,104 @@ namespace WeaponPaints return; if (isKnife) return; + + if (weaponInfo.Stickers.Count > 0) SetStickers(player, weapon); + if (weaponInfo.KeyChain != null) SetKeychain(player, weapon); + UpdatePlayerWeaponMeshGroupMask(player, weapon, !newPaints.Contains(fallbackPaintKit)); } - + + // silly method to update sticker when call RefreshWeapons() + private void IncrementWearForWeaponWithStickers(CCSPlayerController player, CBasePlayerWeapon weapon) + { + int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex; + if (gPlayerWeaponsInfo.TryGetValue(player.Slot, out var playerWeapons) && + playerWeapons.TryGetValue(weaponDefIndex, out var weaponInfo) && + weaponInfo.Stickers.Count > 0) + { + + float wearIncrement = 0.001f; + float currentWear = weaponInfo.Wear; + + var playerWear = temporaryPlayerWeaponWear.GetOrAdd(player.Slot, _ => new ConcurrentDictionary()); + + float incrementedWear = playerWear.AddOrUpdate( + weaponDefIndex, + currentWear + wearIncrement, + (_, oldWear) => Math.Min(oldWear + wearIncrement, 1.0f) + ); + + weapon.FallbackWear = incrementedWear; + } + } + + public void SetStickers(CCSPlayerController? player, CBasePlayerWeapon weapon) + { + if (player == null || !player.IsValid) return; + + int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex; + + if (!gPlayerWeaponsInfo.TryGetValue(player.Slot, out var playerWeapons) || + playerWeapons == null || + !playerWeapons.TryGetValue(weaponDefIndex, out var weaponInfo) || + weaponInfo == null) + { + return; + } + + foreach (var sticker in weaponInfo.Stickers) + { + int stickerSlot = weaponInfo.Stickers.IndexOf(sticker); + + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, + $"sticker slot {stickerSlot} id", ViewAsFloat(sticker.Id)); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, + $"sticker slot {stickerSlot} schema", sticker.Schema); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, + $"sticker slot {stickerSlot} offset x", sticker.OffsetX); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, + $"sticker slot {stickerSlot} offset y", sticker.OffsetY); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, + $"sticker slot {stickerSlot} wear", sticker.Wear); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, + $"sticker slot {stickerSlot} scale", sticker.Scale); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, + $"sticker slot {stickerSlot} rotation", sticker.Rotation); + } + + if (temporaryPlayerWeaponWear != null && + temporaryPlayerWeaponWear.TryGetValue(player.Slot, out var playerWear) && + playerWear.TryGetValue(weaponDefIndex, out float storedWear)) + { + weapon.FallbackWear = storedWear; + } + } + + public void SetKeychain(CCSPlayerController? player, CBasePlayerWeapon weapon) + { + if (player == null || !player.IsValid) return; + + int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex; + + if (gPlayerWeaponsInfo.TryGetValue(player.Slot, out var playerWeaponsInfo) && + playerWeaponsInfo.TryGetValue(weaponDefIndex, out var value) && + value.KeyChain != null) + { + var keyChain = value.KeyChain; + + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, + "keychain slot 0 id", ViewAsFloat(keyChain.Id)); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, + "keychain slot 0 offset x", keyChain.OffsetX); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, + "keychain slot 0 offset y", keyChain.OffsetY); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, + "keychain slot 0 offset z", keyChain.OffsetZ); + CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, + "keychain slot 0 seed", keyChain.Seed); + } + } + private static void GiveKnifeToPlayer(CCSPlayerController? player) { if (!_config.Additional.KnifeEnabled || player == null || !player.IsValid) return; @@ -248,6 +347,8 @@ namespace WeaponPaints { newWeapon.Clip1 = ammo.Item1; newWeapon.ReserveAmmo[0] = ammo.Item2; + + IncrementWearForWeaponWithStickers(player, newWeapon); } catch (Exception ex) { @@ -447,5 +548,10 @@ namespace WeaponPaints return values; } + + public float ViewAsFloat(uint value) + { + return BitConverter.Int32BitsToSingle((int)value); + } } } \ No newline at end of file diff --git a/WeaponInfo.cs b/WeaponInfo.cs index c1a97d58..40b77876 100644 --- a/WeaponInfo.cs +++ b/WeaponInfo.cs @@ -1,9 +1,29 @@ -namespace WeaponPaints +public class WeaponInfo { - public class WeaponInfo - { - public int Paint { get; set; } - public int Seed { get; set; } = 0; - public float Wear { get; set; } = 0f; - } + public int Paint { get; set; } + public int Seed { get; set; } = 0; + public float Wear { get; set; } = 0f; + public string Nametag { get; set; } = ""; + public KeyChainInfo? KeyChain { get; set; } + public List Stickers { get; set; } = new List(); +} + +public class StickerInfo +{ + public uint Id { get; set; } + public uint Schema { get; set; } + public float OffsetX { get; set; } + public float OffsetY { get; set; } + public float Wear { get; set; } + public float Scale { get; set; } + public float Rotation { get; set; } +} + +public class KeyChainInfo +{ + public uint Id { get; set; } + public float OffsetX { get; set; } + public float OffsetY { get; set; } + public float OffsetZ { get; set; } + public uint Seed { get; set; } } \ No newline at end of file diff --git a/WeaponPaints.cs b/WeaponPaints.cs index d644a411..a8fdd7e8 100644 --- a/WeaponPaints.cs +++ b/WeaponPaints.cs @@ -165,6 +165,8 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig> temporaryPlayerWeaponWear = new ConcurrentDictionary>(); + public WeaponPaintsConfig Config { get; set; } = new(); public override string ModuleAuthor => "Nereziel & daffyy"; public override string ModuleDescription => "Skin, gloves, agents and knife selector, standalone and web-based"; diff --git a/WeaponPaints.csproj b/WeaponPaints.csproj index 6bd8b370..eedfafc6 100644 --- a/WeaponPaints.csproj +++ b/WeaponPaints.csproj @@ -9,7 +9,7 @@ - + diff --git a/WeaponSynchronization.cs b/WeaponSynchronization.cs index c8c04aec..836dc19e 100644 --- a/WeaponSynchronization.cs +++ b/WeaponSynchronization.cs @@ -2,6 +2,7 @@ using MySqlConnector; using System.Collections.Concurrent; + namespace WeaponPaints { internal class WeaponSynchronization @@ -20,7 +21,7 @@ namespace WeaponPaints try { await using var connection = await _database.GetConnectionAsync(); - + if (_config.Additional.KnifeEnabled) GetKnifeFromDatabase(player, connection); if (_config.Additional.GloveEnabled) @@ -127,14 +128,85 @@ namespace WeaponPaints int weaponPaintId = row?.weapon_paint_id ?? 0; float weaponWear = row?.weapon_wear ?? 0f; int weaponSeed = row?.weapon_seed ?? 0; + string weaponNameTag = row?.weapon_nametag ?? ""; + string[]? keyChainParts = row?.weapon_keychain?.ToString().Split(';'); + + KeyChainInfo keyChainInfo = new KeyChainInfo(); + + if (keyChainParts!.Length == 5 && + uint.TryParse(keyChainParts[0], out uint keyChainId) && + float.TryParse(keyChainParts[1], out float keyChainOffsetX) && + float.TryParse(keyChainParts[2], out float keyChainOffsetY) && + float.TryParse(keyChainParts[3], out float keyChainOffsetZ) && + uint.TryParse(keyChainParts[4], out uint keyChainSeed)) + { + // Successfully parsed the values + keyChainInfo.Id = keyChainId; + keyChainInfo.OffsetX = keyChainOffsetX; + keyChainInfo.OffsetY = keyChainOffsetY; + keyChainInfo.OffsetZ = keyChainOffsetZ; + keyChainInfo.Seed = keyChainSeed; + } + else + { + // Failed to parse the values, default to 0 + keyChainInfo.Id = 0; + keyChainInfo.OffsetX = 0f; + keyChainInfo.OffsetY = 0f; + keyChainInfo.OffsetZ = 0f; + keyChainInfo.Seed = 0; + } + + // Create the WeaponInfo object WeaponInfo weaponInfo = new WeaponInfo { Paint = weaponPaintId, Seed = weaponSeed, - Wear = weaponWear + Wear = weaponWear, + Nametag = weaponNameTag, + KeyChain = keyChainInfo }; + // Retrieve and parse sticker data (up to 5 slots) + for (int i = 0; i <= 4; i++) + { + // Access the sticker data dynamically using reflection + string stickerColumn = $"weapon_sticker_{i}"; + var stickerData = ((IDictionary)row!)[stickerColumn]; // Safely cast row to a dictionary + + if (stickerData != null && !string.IsNullOrEmpty(stickerData.ToString())) + { + var parts = stickerData.ToString()!.Split(';'); + + //"id;schema;x;y;wear;scale;rotation" + if (parts.Length == 7 && + uint.TryParse(parts[0], out uint stickerId) && + uint.TryParse(parts[1], out uint stickerSchema) && + float.TryParse(parts[2], out float stickerOffsetX) && + float.TryParse(parts[3], out float stickerOffsetY) && + float.TryParse(parts[4], out float stickerWear) && + float.TryParse(parts[5], out float stickerScale) && + float.TryParse(parts[6], out float stickerRotation)) + { + StickerInfo stickerInfo = new StickerInfo + { + Id = stickerId, + Schema = stickerSchema, + OffsetX = stickerOffsetX, + OffsetY = stickerOffsetY, + Wear = stickerWear, + Scale = stickerScale, + Rotation = stickerRotation + }; + + weaponInfo.Stickers.Add(stickerInfo); + + + } + } + } + weaponInfos[weaponDefIndex] = weaponInfo; } @@ -167,14 +239,12 @@ namespace WeaponPaints } } - - internal async Task SyncKnifeToDatabase(PlayerInfo player, string knife) { if (!_config.Additional.KnifeEnabled || string.IsNullOrEmpty(player.SteamId) || string.IsNullOrEmpty(knife)) return; const string query = "INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(@steamid, @newKnife) ON DUPLICATE KEY UPDATE `knife` = @newKnife"; - + try { await using var connection = await _database.GetConnectionAsync();