mirror of
https://github.com/Nereziel/cs2-WeaponPaints.git
synced 2026-02-18 10:43:22 +00:00
Merge pull request #310 from stefanx111/stickers-and-charms
feat: Stickers and Charms
This commit is contained in:
@@ -89,6 +89,8 @@ namespace WeaponPaints
|
||||
g_playersMusic.TryRemove(player.Slot, out _);
|
||||
}
|
||||
|
||||
temporaryPlayerWeaponWear.TryRemove(player.Slot, out _);
|
||||
|
||||
commandsCooldown.Remove(player.Slot);
|
||||
|
||||
return HookResult.Continue;
|
||||
|
||||
@@ -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` (
|
||||
|
||||
106
WeaponAction.cs
106
WeaponAction.cs
@@ -5,6 +5,8 @@ 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
|
||||
@@ -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<int, float>());
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,5 +5,28 @@
|
||||
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<StickerInfo> Stickers { get; set; } = new List<StickerInfo>();
|
||||
}
|
||||
|
||||
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; }
|
||||
}
|
||||
}
|
||||
@@ -165,6 +165,8 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
|
||||
private ulong _nextItemId = MinimumCustomItemId;
|
||||
public static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||
|
||||
private ConcurrentDictionary<int, ConcurrentDictionary<int, float>> temporaryPlayerWeaponWear = new ConcurrentDictionary<int, ConcurrentDictionary<int, float>>();
|
||||
|
||||
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";
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.233" />
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.281" />
|
||||
<PackageReference Include="Dapper" Version="2.1.35" />
|
||||
<PackageReference Include="MySqlConnector" Version="2.3.7" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using MySqlConnector;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
|
||||
namespace WeaponPaints
|
||||
{
|
||||
internal class WeaponSynchronization
|
||||
@@ -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<string, object>)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,8 +239,6 @@ namespace WeaponPaints
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal async Task SyncKnifeToDatabase(PlayerInfo player, string knife)
|
||||
{
|
||||
if (!_config.Additional.KnifeEnabled || string.IsNullOrEmpty(player.SteamId) || string.IsNullOrEmpty(knife)) return;
|
||||
|
||||
Reference in New Issue
Block a user