Compare commits

..

12 Commits

Author SHA1 Message Date
Dawid Bepierszcz
ee1259846d Merge pull request #187 from daffyyyy/main
2.1a
2024-03-04 23:52:42 +01:00
Dawid Bepierszcz
485dd1e9dd Merge branch 'Nereziel:main' into main 2024-03-04 23:51:32 +01:00
Dawid Bepierszcz
666de9e2d2 Merge branch 'main' of https://github.com/daffyyyy/cs2-WeaponPaints 2024-03-04 23:51:10 +01:00
Dawid Bepierszcz
77409269d0 2.1a
- FINALLY WINDOWS SUPPORT
- Fixed visual bug (no skin)
2024-03-04 23:51:08 +01:00
Dawid Bepierszcz
09d9de2a2a Merge pull request #186 from daffyyyy/main
2.0a
2024-03-04 21:50:42 +01:00
Dawid Bepierszcz
362084c503 Merge branch 'Nereziel:main' into main 2024-03-04 21:49:41 +01:00
Dawid Bepierszcz
8cf175d336 2.0a
- New knife method
- Minor commands changes
- Remove OnTick event
- Changed save place
- New skins saving method
- New gamedata file

**IMPORTANT UPDATE**
2024-03-04 21:47:54 +01:00
Nereziel
84adfa8085 Update FUNDING.yml 2024-03-01 15:16:54 +01:00
Nereziel
286d97d125 Create FUNDING.yml 2024-03-01 15:14:07 +01:00
Dawid Bepierszcz
4d3db5b34f Merge pull request #174 from daffyyyy/main
1.9c
2024-02-25 13:20:57 +01:00
Dawid Bepierszcz
85d4e11f26 Merge branch 'Nereziel:main' into main 2024-02-25 13:19:55 +01:00
Dawid Bepierszcz
34b086a140 1.9c
- Some changes
2024-02-25 12:55:44 +01:00
25 changed files with 1229 additions and 908 deletions

14
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: nereziel # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
custom: ['https://steamcommunity.com/tradeoffer/new/?partner=41515647&token=gW2W-nXE'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@@ -1,8 +1,6 @@
using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Commands; using CounterStrikeSharp.API.Modules.Commands;
using CounterStrikeSharp.API.Modules.Menu; using CounterStrikeSharp.API.Modules.Menu;
using Newtonsoft.Json.Linq;
using System.Text;
namespace WeaponPaints namespace WeaponPaints
{ {
@@ -14,14 +12,17 @@ namespace WeaponPaints
if (!Utility.IsPlayerValid(player)) return; if (!Utility.IsPlayerValid(player)) return;
if (player == null || !player.IsValid || player.UserId == null || player.IsBot) return; if (player == null || !player.IsValid || player.UserId == null || player.IsBot) return;
PlayerInfo playerInfo = new (
(int)player.Index, PlayerInfo playerInfo = new PlayerInfo
player.Slot, {
player.UserId, UserId = player.UserId,
player.SteamID.ToString(), Slot = player.Slot,
player.PlayerName, Index = (int)player.Index,
player.IpAddress?.Split(":")[0] SteamId = player?.SteamID.ToString(),
); Name = player?.PlayerName,
IpAddress = player?.IpAddress?.Split(":")[0]
};
try try
{ {
if (player != null && !commandsCooldown.TryGetValue(player.Slot, out DateTime cooldownEndTime) || if (player != null && !commandsCooldown.TryGetValue(player.Slot, out DateTime cooldownEndTime) ||
@@ -124,372 +125,331 @@ namespace WeaponPaints
} }
} }
private void SetupKnifeMenu() private void SetupKnifeMenu()
{ {
if (!Config.Additional.KnifeEnabled || !g_bCommandsAllowed) return; if (!Config.Additional.KnifeEnabled || !g_bCommandsAllowed) return;
var knivesOnly = GetKnivesOnly(); var knivesOnly = weaponList
var giveItemMenu = new ChatMenu(Localizer["wp_knife_menu_title"]); .Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet"))
.ToDictionary(pair => pair.Key, pair => pair.Value);
AddMenuOptions(giveItemMenu, knivesOnly); var giveItemMenu = new ChatMenu(Localizer["wp_knife_menu_title"]);
AddCommandToOpenKnifeMenu(giveItemMenu); var handleGive = (CCSPlayerController player, ChatMenuOption option) =>
} {
if (!Utility.IsPlayerValid(player)) return;
private Dictionary<string, string> GetKnivesOnly() var knifeName = option.Text;
{ var knifeKey = knivesOnly.FirstOrDefault(x => x.Value == knifeName).Key;
return weaponList if (!string.IsNullOrEmpty(knifeKey))
.Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")) {
.ToDictionary(pair => pair.Key, pair => pair.Value); if (!string.IsNullOrEmpty(Localizer["wp_knife_menu_select"]))
} {
player!.Print(Localizer["wp_knife_menu_select", knifeName]);
}
private void AddMenuOptions(ChatMenu giveItemMenu, Dictionary<string, string> knivesOnly) if (!string.IsNullOrEmpty(Localizer["wp_knife_menu_kill"]) && Config.Additional.CommandKillEnabled)
{ {
foreach (var knifePair in knivesOnly) player!.Print(Localizer["wp_knife_menu_kill"]);
{ }
giveItemMenu.AddMenuOption(knifePair.Value, (player, option) => HandleGiveOption(player, option, knivesOnly));
}
}
private void HandleGiveOption(CCSPlayerController player, ChatMenuOption option, Dictionary<string, string> knivesOnly) PlayerInfo playerInfo = new PlayerInfo
{ {
if (!Utility.IsPlayerValid(player)) return; UserId = player.UserId,
Slot = player.Slot,
Index = (int)player.Index,
SteamId = player.SteamID.ToString(),
Name = player.PlayerName,
IpAddress = player.IpAddress?.Split(":")[0]
};
var knifeName = option.Text; g_playersKnife[player.Slot] = knifeKey;
var knifeKey = knivesOnly.FirstOrDefault(x => x.Value == knifeName).Key;
if (!string.IsNullOrEmpty(knifeKey)) if (g_bCommandsAllowed && (LifeState_t)player.LifeState == LifeState_t.LIFE_ALIVE)
{ RefreshWeapons(player);
PrintMessages(player, knifeName);
SetPlayerKnife(player, knifeKey);
SyncKnifeToDatabase(player, knifeKey);
}
}
private void PrintMessages(CCSPlayerController player, string knifeName) if (weaponSync != null)
{ Task.Run(async () => await weaponSync.SyncKnifeToDatabase(playerInfo, knifeKey));
if (!string.IsNullOrEmpty(Localizer["wp_knife_menu_select"])) }
{ };
player!.Print(Localizer["wp_knife_menu_select", knifeName]); foreach (var knifePair in knivesOnly)
} {
giveItemMenu.AddMenuOption(knifePair.Value, handleGive);
}
AddCommand($"css_{Config.Additional.CommandKnife}", "Knife Menu", (player, info) =>
{
if (!Utility.IsPlayerValid(player) || !g_bCommandsAllowed) return;
if (!string.IsNullOrEmpty(Localizer["wp_knife_menu_kill"]) && Config.Additional.CommandKillEnabled) if (player == null || player.UserId == null) return;
{
player!.Print(Localizer["wp_knife_menu_kill"]);
}
}
private void SetPlayerKnife(CCSPlayerController player, string knifeKey) if (player != null && !commandsCooldown.TryGetValue(player.Slot, out DateTime cooldownEndTime) ||
{ player != null && DateTime.UtcNow >= (commandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
g_playersKnife[player.Slot] = knifeKey; {
commandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
giveItemMenu.PostSelectAction = PostSelectAction.Close;
MenuManager.OpenChatMenu(player, giveItemMenu);
return;
}
if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"]))
{
player!.Print(Localizer["wp_command_cooldown"]);
}
});
}
if (g_bCommandsAllowed && (LifeState_t)player.LifeState == LifeState_t.LIFE_ALIVE) private void SetupSkinsMenu()
RefreshWeapons(player); {
} var classNamesByWeapon = weaponList.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
var weaponSelectionMenu = new ChatMenu(Localizer["wp_skin_menu_weapon_title"]);
private void SyncKnifeToDatabase(CCSPlayerController player, string knifeKey) // Function to handle skin selection for a specific weapon
{ var handleWeaponSelection = (CCSPlayerController? player, ChatMenuOption option) =>
if (weaponSync != null) {
Task.Run(async () => await weaponSync.SyncKnifeToDatabase(GetPlayerInfo(player), knifeKey)); if (!Utility.IsPlayerValid(player)) return;
}
private PlayerInfo GetPlayerInfo(CCSPlayerController player) string selectedWeapon = option.Text;
{ if (classNamesByWeapon.TryGetValue(selectedWeapon, out string? selectedWeaponClassname))
return new PlayerInfo {
{ if (selectedWeaponClassname == null) return;
UserId = player.UserId, var skinsForSelectedWeapon = skinsList?.Where(skin =>
Slot = player.Slot, skin != null &&
Index = (int)player.Index, skin.TryGetValue("weapon_name", out var weaponName) &&
SteamId = player.SteamID.ToString(), weaponName?.ToString() == selectedWeaponClassname
Name = player.PlayerName, )?.ToList();
IpAddress = player.IpAddress?.Split(":")[0]
};
}
private void AddCommandToOpenKnifeMenu(ChatMenu giveItemMenu) var skinSubMenu = new ChatMenu(Localizer["wp_skin_menu_skin_title", selectedWeapon]);
{
AddCommand($"css_{Config.Additional.CommandKnife}", "Knife Menu", (player, info) =>
{
if (!Utility.IsPlayerValid(player) || !g_bCommandsAllowed) return;
if (player == null || player.UserId == null) return; // Function to handle skin selection for the chosen weapon
var handleSkinSelection = (CCSPlayerController p, ChatMenuOption opt) =>
{
if (!Utility.IsPlayerValid(p)) return;
if (IsCommandAllowed(player)) string steamId = p.SteamID.ToString();
{ var firstSkin = skinsList?.FirstOrDefault(skin =>
commandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds); {
giveItemMenu.PostSelectAction = PostSelectAction.Close; if (skin != null && skin.TryGetValue("weapon_name", out var weaponName))
MenuManager.OpenChatMenu(player, giveItemMenu); {
return; return weaponName?.ToString() == selectedWeaponClassname;
} }
if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"])) return false;
{ });
player!.Print(Localizer["wp_command_cooldown"]);
}
});
}
private bool IsCommandAllowed(CCSPlayerController player)
{
if (!commandsCooldown.TryGetValue(player.Slot, out DateTime cooldownEndTime))
{
return true;
}
return DateTime.UtcNow >= cooldownEndTime;
}
private void SetupSkinsMenu() string selectedSkin = opt.Text;
{ string selectedPaintID = selectedSkin.Substring(selectedSkin.LastIndexOf('(') + 1).Trim(')');
var classNamesByWeapon = weaponList.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
var weaponSelectionMenu = new ChatMenu(Localizer["wp_skin_menu_weapon_title"]);
// Add weapon options to the weapon selection menu if (firstSkin != null &&
foreach (var weaponClass in weaponList.Keys) firstSkin.TryGetValue("weapon_defindex", out var weaponDefIndexObj) &&
{ weaponDefIndexObj != null &&
string weaponName = weaponList[weaponClass]; int.TryParse(weaponDefIndexObj.ToString(), out var weaponDefIndex) &&
weaponSelectionMenu.AddMenuOption(weaponName, (player, option) => HandleWeaponSelection(player, option, classNamesByWeapon)); int.TryParse(selectedPaintID, out var paintID))
} {
if (Config.Additional.ShowSkinImage && skinsList != null)
{
var foundSkin = skinsList.FirstOrDefault(skin =>
((int?)skin?["weapon_defindex"] ?? 0) == weaponDefIndex &&
((int?)skin?["paint"] ?? 0) == paintID &&
skin?["image"] != null
);
string image = foundSkin?["image"]?.ToString() ?? "";
PlayerWeaponImage[p.Slot] = image;
AddTimer(2.0f, () => PlayerWeaponImage.Remove(p.Slot), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
// Command to open the weapon selection menu for players p.Print(Localizer["wp_skin_menu_select", selectedSkin]);
AddCommand($"css_{Config.Additional.CommandSkinSelection}", "Skins selection menu", (player, info) => OpenWeaponSelectionMenu(player, weaponSelectionMenu));
}
private void HandleWeaponSelection(CCSPlayerController? player, ChatMenuOption option, Dictionary<string, string> classNamesByWeapon) if (!gPlayerWeaponsInfo[p.Slot].ContainsKey(weaponDefIndex))
{ {
if (!Utility.IsPlayerValid(player)) return; gPlayerWeaponsInfo[p.Slot][weaponDefIndex] = new WeaponInfo();
}
string selectedWeapon = option.Text; gPlayerWeaponsInfo[p.Slot][weaponDefIndex].Paint = paintID;
if (classNamesByWeapon.TryGetValue(selectedWeapon, out string? selectedWeaponClassname)) gPlayerWeaponsInfo[p.Slot][weaponDefIndex].Wear = 0.01f;
{ gPlayerWeaponsInfo[p.Slot][weaponDefIndex].Seed = 0;
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(Localizer["wp_skin_menu_skin_title", selectedWeapon]); PlayerInfo playerInfo = new PlayerInfo
skinSubMenu.PostSelectAction = PostSelectAction.Close; {
UserId = p.UserId,
Slot = p.Slot,
Index = (int)p.Index,
SteamId = p.SteamID.ToString(),
Name = p.PlayerName,
IpAddress = p.IpAddress?.Split(":")[0]
};
// Add skin options to the submenu for the selected weapon if (g_bCommandsAllowed && (LifeState_t)p.LifeState == LifeState_t.LIFE_ALIVE && weaponSync != null)
if (skinsForSelectedWeapon != null) {
{ RefreshWeapons(player);
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)) try
{ {
var optionText = new StringBuilder(paintName).Append(" (").Append(paint).Append(")").ToString(); Task.Run(async () => await weaponSync.SyncWeaponPaintsToDatabase(playerInfo));
skinSubMenu.AddMenuOption(optionText, (p, opt) => HandleSkinSelection(p, opt, selectedWeaponClassname)); }
} catch (Exception ex)
} {
} Utility.Log($"Error syncing weapon paints: {ex.Message}");
} }
if (player != null && Utility.IsPlayerValid(player)) }
MenuManager.OpenChatMenu(player, skinSubMenu); }
} };
}
private void HandleSkinSelection(CCSPlayerController p, ChatMenuOption opt, string selectedWeaponClassname) // Add skin options to the submenu for the selected weapon
{ if (skinsForSelectedWeapon != null)
if (!Utility.IsPlayerValid(p)) return; {
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();
string steamId = p.SteamID.ToString(); if (!string.IsNullOrEmpty(paintName) && !string.IsNullOrEmpty(paint))
var firstSkin = skinsList?.Find(skin => {
{ skinSubMenu.AddMenuOption($"{paintName} ({paint})", handleSkinSelection);
if (skin != null && skin.TryGetValue("weapon_name", out var weaponName)) }
{ }
return weaponName?.ToString() == selectedWeaponClassname; }
} }
return false; if (player != null && Utility.IsPlayerValid(player))
}); MenuManager.OpenChatMenu(player, skinSubMenu);
}
};
string selectedSkin = opt.Text; // Add weapon options to the weapon selection menu
string selectedPaintID = selectedSkin.Split('(')[1].Trim(')').Trim(); 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;
if (firstSkin != null && if (player == null || player.UserId == null) return;
firstSkin.TryGetValue("weapon_defindex", out var weaponDefIndexObj) &&
weaponDefIndexObj != null &&
int.TryParse(weaponDefIndexObj.ToString(), out var weaponDefIndex) &&
int.TryParse(selectedPaintID, out var paintID))
{
HandleSkinImage(p, weaponDefIndex, paintID);
p.Print(Localizer["wp_skin_menu_select", selectedSkin]);
UpdatePlayerWeaponInfo(p, weaponDefIndex, paintID);
}
}
private void HandleSkinImage(CCSPlayerController p, int weaponDefIndex, int paintID) if (player != null && !commandsCooldown.TryGetValue(player.Slot, out DateTime cooldownEndTime) ||
{ player != null && DateTime.UtcNow >= (commandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
if (Config.Additional.ShowSkinImage && skinsList != null) {
{ commandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
var foundSkin = skinsList.FirstOrDefault(skin => MenuManager.OpenChatMenu(player, weaponSelectionMenu);
((int?)skin?["weapon_defindex"] ?? 0) == weaponDefIndex && return;
((int?)skin?["paint"] ?? 0) == paintID && }
skin?["image"] != null if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"]))
); {
string image = foundSkin?["image"]?.ToString() ?? ""; player!.Print(Localizer["wp_command_cooldown"]);
PlayerWeaponImage[p.Slot] = image; }
AddTimer(2.0f, () => PlayerWeaponImage.Remove(p.Slot), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE); });
} }
}
private void UpdatePlayerWeaponInfo(CCSPlayerController p, int weaponDefIndex, int paintID) private void SetupGlovesMenu()
{ {
if (!gPlayerWeaponsInfo[p.Slot].TryGetValue(weaponDefIndex, out WeaponInfo? value)) var glovesSelectionMenu = new ChatMenu(Localizer["wp_glove_menu_title"]);
{ glovesSelectionMenu.PostSelectAction = PostSelectAction.Close;
value = new WeaponInfo();
gPlayerWeaponsInfo[p.Slot][weaponDefIndex] = value;
}
value.Paint = paintID; var handleGloveSelection = (CCSPlayerController? player, ChatMenuOption option) =>
value.Wear = 0.00f; {
value.Seed = 0; if (!Utility.IsPlayerValid(player) || player is null) return;
if (g_bCommandsAllowed && (LifeState_t)p.LifeState == LifeState_t.LIFE_ALIVE) string selectedPaintName = option.Text;
{
RefreshWeapons(p);
}
}
private void OpenWeaponSelectionMenu(CCSPlayerController? player, ChatMenu weaponSelectionMenu) var selectedGlove = glovesList.FirstOrDefault(g => g.ContainsKey("paint_name") && g["paint_name"]?.ToString() == selectedPaintName);
{ if (selectedGlove != null)
if (!Utility.IsPlayerValid(player)) return; {
if (
selectedGlove != null &&
selectedGlove.ContainsKey("weapon_defindex") &&
selectedGlove.ContainsKey("paint") &&
int.TryParse(selectedGlove["weapon_defindex"]?.ToString(), out int weaponDefindex) &&
int.TryParse(selectedGlove["paint"]?.ToString(), out int paint)
)
{
if (Config.Additional.ShowSkinImage)
{
string image = selectedGlove["image"]?.ToString() ?? "";
PlayerWeaponImage[player.Slot] = image;
AddTimer(2.0f, () => PlayerWeaponImage.Remove(player.Slot), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
if (player == null || player.UserId == null) return; PlayerInfo playerInfo = new PlayerInfo
{
UserId = player.UserId,
Slot = player.Slot,
Index = (int)player.Index,
SteamId = player.SteamID.ToString(),
Name = player.PlayerName,
IpAddress = player.IpAddress?.Split(":")[0]
};
if (player != null && !commandsCooldown.TryGetValue(player.Slot, out DateTime cooldownEndTime) || if (paint != 0)
player != null && DateTime.UtcNow >= (commandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow)) {
{ g_playersGlove[player.Slot] = (ushort)weaponDefindex;
commandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
MenuManager.OpenChatMenu(player, weaponSelectionMenu);
return;
}
if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"]))
{
player!.Print(Localizer["wp_command_cooldown"]);
}
}
private void SetupGlovesMenu()
{
var glovesSelectionMenu = new ChatMenu(Localizer["wp_glove_menu_title"]);
glovesSelectionMenu.PostSelectAction = PostSelectAction.Close;
var handleGloveSelection = (CCSPlayerController? player, ChatMenuOption option) => if (!gPlayerWeaponsInfo[player.Slot].ContainsKey(weaponDefindex))
{ {
if (!Utility.IsPlayerValid(player) || player is null) return; WeaponInfo weaponInfo = new();
weaponInfo.Paint = paint;
gPlayerWeaponsInfo[player.Slot][weaponDefindex] = weaponInfo;
}
}
else
{
g_playersGlove.TryRemove(player.Slot, out _);
}
string selectedPaintName = option.Text; if (!string.IsNullOrEmpty(Localizer["wp_glove_menu_select"]))
var selectedGlove = glovesList.FirstOrDefault(g => g.ContainsKey("paint_name") && g["paint_name"]?.ToString() == selectedPaintName); {
if (selectedGlove != null) player!.Print(Localizer["wp_glove_menu_select", selectedPaintName]);
{ }
HandleSelectedGlove(player, selectedGlove, selectedPaintName);
}
};
AddGloveOptionsToMenu(glovesSelectionMenu, handleGloveSelection); if (weaponSync != null)
AddCommandToOpenGloveSelectionMenu(glovesSelectionMenu); {
} Task.Run(async () =>
{
void HandleSelectedGlove(CCSPlayerController? player, JObject selectedGlove, string selectedPaintName) await weaponSync.SyncGloveToDatabase(playerInfo, weaponDefindex);
{
if (selectedGlove != null &&
selectedGlove.ContainsKey("weapon_defindex") &&
selectedGlove.ContainsKey("paint") &&
int.TryParse(selectedGlove["weapon_defindex"]?.ToString(), out int weaponDefindex) &&
int.TryParse(selectedGlove["paint"]?.ToString(), out int paint))
{
if (Config.Additional.ShowSkinImage)
{
string image = selectedGlove["image"]?.ToString() ?? "";
PlayerWeaponImage[player.Slot] = image;
AddTimer(2.0f, () => PlayerWeaponImage.Remove(player.Slot), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
PlayerInfo playerInfo = new(
(int)player.Index,
player.Slot,
player.UserId,
player.SteamID.ToString(),
player.PlayerName,
player.IpAddress?.Split(":")[0]
);
if (paint != 0) if (!gPlayerWeaponsInfo[playerInfo.Slot].ContainsKey(weaponDefindex))
{ {
g_playersGlove[player.Slot] = (ushort)weaponDefindex; gPlayerWeaponsInfo[playerInfo.Slot][weaponDefindex] = new WeaponInfo();
}
if (!gPlayerWeaponsInfo[player.Slot].TryGetValue(weaponDefindex, out _)) gPlayerWeaponsInfo[playerInfo.Slot][weaponDefindex].Paint = paint;
{ gPlayerWeaponsInfo[playerInfo.Slot][weaponDefindex].Wear = 0.00f;
WeaponInfo weaponInfo = new(); gPlayerWeaponsInfo[playerInfo.Slot][weaponDefindex].Seed = 0;
weaponInfo.Paint = paint; });
gPlayerWeaponsInfo[player.Slot][weaponDefindex] = weaponInfo; }
} RefreshGloves(player);
} }
else };
{ };
g_playersGlove.TryRemove(player.Slot, out _);
}
if (!string.IsNullOrEmpty(Localizer["wp_glove_menu_select"])) // Add weapon options to the weapon selection menu
{ foreach (var gloveObject in glovesList)
player!.Print(Localizer["wp_glove_menu_select", selectedPaintName]); {
} string paintName = gloveObject["paint_name"]?.ToString() ?? "";
if (weaponSync != null) if (paintName.Length > 0)
{ glovesSelectionMenu.AddMenuOption(paintName, handleGloveSelection);
Task.Run(async () => }
{
await weaponSync.SyncGloveToDatabase(playerInfo, weaponDefindex);
if (!gPlayerWeaponsInfo[playerInfo.Slot].TryGetValue(weaponDefindex, out var weaponInfo)) // Command to open the weapon selection menu for players
{ AddCommand($"css_{Config.Additional.CommandGlove}", "Gloves selection menu", (player, info) =>
weaponInfo = new WeaponInfo(); {
gPlayerWeaponsInfo[playerInfo.Slot][weaponDefindex] = weaponInfo; if (!Utility.IsPlayerValid(player) || !g_bCommandsAllowed) return;
}
weaponInfo.Paint = paint; if (player == null || player.UserId == null) return;
weaponInfo.Wear = 0.00f;
weaponInfo.Seed = 0;
}); if (player != null && !commandsCooldown.TryGetValue(player.Slot, out DateTime cooldownEndTime) ||
} player != null && DateTime.UtcNow >= (commandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
RefreshGloves(player); {
} commandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
} MenuManager.OpenChatMenu(player, glovesSelectionMenu);
return;
private void AddGloveOptionsToMenu(ChatMenu glovesSelectionMenu, Action<CCSPlayerController?, ChatMenuOption> handleGloveSelection) }
{ if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"]))
foreach (var gloveObject in glovesList) {
{ player!.Print(Localizer["wp_command_cooldown"]);
string paintName = gloveObject["paint_name"]?.ToString() ?? ""; }
});
if (paintName.Length > 0) }
glovesSelectionMenu.AddMenuOption(paintName, handleGloveSelection);
}
}
private void AddCommandToOpenGloveSelectionMenu(ChatMenu glovesSelectionMenu)
{
AddCommand($"css_{Config.Additional.CommandGlove}", "Gloves selection menu", (player, info) =>
{
if (!Utility.IsPlayerValid(player) || !g_bCommandsAllowed) return;
if (player == null || player.UserId == null) return;
if (player != null && !commandsCooldown.TryGetValue(player.Slot, out DateTime cooldownEndTime) ||
player != null && DateTime.UtcNow >= (commandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
{
commandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
MenuManager.OpenChatMenu(player, glovesSelectionMenu);
return;
}
if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"]))
{
player!.Print(Localizer["wp_command_cooldown"]);
}
});
}
} }
} }

View File

@@ -5,9 +5,6 @@ namespace WeaponPaints
{ {
public class Additional public class Additional
{ {
[JsonPropertyName("SkinVisibilityFix")]
public bool SkinVisibilityFix { get; set; } = true;
[JsonPropertyName("KnifeEnabled")] [JsonPropertyName("KnifeEnabled")]
public bool KnifeEnabled { get; set; } = true; public bool KnifeEnabled { get; set; } = true;
@@ -56,7 +53,7 @@ namespace WeaponPaints
public class WeaponPaintsConfig : BasePluginConfig public class WeaponPaintsConfig : BasePluginConfig
{ {
public override int Version { get; set; } = 4; public override int Version { get; set; } = 5;
[JsonPropertyName("DatabaseHost")] [JsonPropertyName("DatabaseHost")]
public string DatabaseHost { get; set; } = ""; public string DatabaseHost { get; set; } = "";

View File

@@ -11,11 +11,18 @@ namespace WeaponPaints
_dbConnectionString = dbConnectionString; _dbConnectionString = dbConnectionString;
} }
public async Task<MySqlConnection> GetConnectionAsync() public async Task<MySqlConnection> GetConnectionAsync()
{ {
var connection = new MySqlConnection(_dbConnectionString); try
await connection.OpenAsync(); {
return connection; var connection = new MySqlConnection(_dbConnectionString);
} await connection.OpenAsync();
return connection;
}
catch (Exception)
{
throw;
}
}
} }
} }

335
Events.cs
View File

@@ -1,6 +1,7 @@
using CounterStrikeSharp.API; using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Attributes.Registration; using CounterStrikeSharp.API.Core.Attributes.Registration;
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
namespace WeaponPaints namespace WeaponPaints
{ {
@@ -14,33 +15,42 @@ namespace WeaponPaints
if (player is null || !player.IsValid || player.IsBot || player.IsHLTV || player.SteamID.ToString().Length != 17 || if (player is null || !player.IsValid || player.IsBot || player.IsHLTV || player.SteamID.ToString().Length != 17 ||
weaponSync == null || _database == null) return HookResult.Continue; weaponSync == null || _database == null) return HookResult.Continue;
PlayerInfo playerInfo = new( PlayerInfo playerInfo = new PlayerInfo
(int)player.Index,
player.Slot,
player.UserId,
player.SteamID.ToString(),
player.PlayerName,
player.IpAddress?.Split(":")[0]
);
try
{ {
UserId = player.UserId,
Slot = player.Slot,
Index = (int)player.Index,
SteamId = player.SteamID.ToString(),
Name = player.PlayerName,
IpAddress = player.IpAddress?.Split(":")[0]
};
try
{
List<Task> tasks = new List<Task>();
if (Config.Additional.SkinEnabled) if (Config.Additional.SkinEnabled)
{ {
Task.Run(() => weaponSync.GetWeaponPaintsFromDatabase(playerInfo)); tasks.Add(Task.Run(() => weaponSync.GetWeaponPaintsFromDatabase(playerInfo)));
} }
if (Config.Additional.KnifeEnabled) if (Config.Additional.KnifeEnabled)
{ {
Task.Run(() => weaponSync.GetKnifeFromDatabase(playerInfo)); tasks.Add(Task.Run(() => weaponSync.GetKnifeFromDatabase(playerInfo)));
} }
if (Config.Additional.GloveEnabled) if (Config.Additional.GloveEnabled)
{ {
Task.Run(() => weaponSync.GetGloveFromDatabase(playerInfo)); tasks.Add(Task.Run(() => weaponSync.GetGloveFromDatabase(playerInfo)));
} }
Task.WaitAll(tasks.ToArray());
} }
catch (Exception ex) catch (AggregateException ex)
{ {
Console.WriteLine($"An error occurred: {ex.Message}"); // Handle the exception
foreach (var innerException in ex.InnerExceptions)
{
Console.WriteLine($"An error occurred for player {player}: {innerException.Message}");
}
} }
return HookResult.Continue; return HookResult.Continue;
@@ -54,114 +64,124 @@ namespace WeaponPaints
if (player is null || !player.IsValid || player.IsBot || if (player is null || !player.IsValid || player.IsBot ||
player.IsHLTV || player.SteamID.ToString().Length != 17) return HookResult.Continue; player.IsHLTV || player.SteamID.ToString().Length != 17) return HookResult.Continue;
PlayerInfo playerInfo = new( PlayerInfo playerInfo = new PlayerInfo
(int)player.Index,
player.Slot,
player.UserId,
player.SteamID.ToString(),
player.PlayerName,
player.IpAddress?.Split(":")[0]
);
if (weaponSync != null)
{ {
// Run weapon sync tasks asynchronously UserId = player.UserId,
Task.Run(async () => Slot = player.Slot,
{ Index = (int)player.Index,
await weaponSync.SyncWeaponPaintsToDatabase(playerInfo); SteamId = player.SteamID.ToString(),
}); Name = player.PlayerName,
IpAddress = player.IpAddress?.Split(":")[0]
};
// Remove player data if (Config.Additional.SkinEnabled)
if (Config.Additional.SkinEnabled) {
{ gPlayerWeaponsInfo.TryRemove(player.Slot, out _);
gPlayerWeaponsInfo.TryRemove(player.Slot, out _); }
} if (Config.Additional.KnifeEnabled)
if (Config.Additional.KnifeEnabled) {
{ g_playersKnife.TryRemove(player.Slot, out _);
g_playersKnife.TryRemove(player.Slot, out _); }
} if (Config.Additional.GloveEnabled)
if (Config.Additional.GloveEnabled) {
{ g_playersGlove.TryRemove(player.Slot, out _);
g_playersGlove.TryRemove(player.Slot, out _);
}
} }
// Remove player's command cooldown
commandsCooldown.Remove(player.Slot); commandsCooldown.Remove(player.Slot);
return HookResult.Continue; return HookResult.Continue;
} }
private void OnEntityCreated(CEntityInstance entity) private void GivePlayerWeaponSkin(CCSPlayerController player, CBasePlayerWeapon weapon)
{ {
if (!Config.Additional.SkinEnabled) return; if (!Config.Additional.SkinEnabled) return;
if (entity == null || !entity.IsValid || string.IsNullOrEmpty(entity.DesignerName)) return;
string 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 (player is null || weapon is null || !weapon.IsValid || !Utility.IsPlayerValid(player)) return;
{
isKnife = true;
}
Server.NextFrame(() => if (!gPlayerWeaponsInfo.ContainsKey(player.Slot)) return;
bool isKnife = weapon.DesignerName.Contains("knife") || weapon.DesignerName.Contains("bayonet");
if (isKnife && !g_playersKnife.ContainsKey(player.Slot) || isKnife && g_playersKnife[player.Slot] == "weapon_knife") return;
int[] newPaints = { 1171, 1170, 1169, 1164, 1162, 1161, 1159, 1175, 1174, 1167, 1165, 1168, 1163, 1160, 1166, 1173 };
if (isKnife)
{ {
try var newDefIndex = WeaponDefindex.FirstOrDefault(x => x.Value == g_playersKnife[player.Slot]);
if (newDefIndex.Key == 0) return;
if (weapon.AttributeManager.Item.ItemDefinitionIndex != newDefIndex.Key)
{ {
if (!weapon.IsValid) return; SubclassChange(weapon, (ushort)newDefIndex.Key);
if (weapon.OwnerEntity.Value == null) return;
if (weapon.OwnerEntity.Index <= 0) return;
int weaponOwner = (int)weapon.OwnerEntity.Index;
CBasePlayerPawn? pawn = Utilities.GetEntityFromIndex<CCSPlayerPawn>(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) { }
});
}
public HookResult OnPickup(CEntityIOOutput output, string name, CEntityInstance activator, CEntityInstance caller, CVariant value, float delay) weapon.AttributeManager.Item.ItemDefinitionIndex = (ushort)newDefIndex.Key;
{ weapon.AttributeManager.Item.EntityQuality = 3;
if (!Config.Additional.GiveKnifeAfterRemove)
return HookResult.Continue;
CCSPlayerController? player = Utilities.GetEntityFromIndex<CCSPlayerPawn>((int)activator.Index).OriginalController.Value;
if (player == null || player.IsBot || player.IsHLTV ||
player.SteamID.ToString().Length != 17 || !g_knifePickupCount.TryGetValue(player.Slot, out var pickupCount) ||
!g_playersKnife.ContainsKey(player.Slot))
{
return HookResult.Continue;
} }
CBasePlayerWeapon weapon = new(caller.Handle); int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
int fallbackPaintKit = 0;
if (weapon.AttributeManager.Item.ItemDefinitionIndex != 42 && weapon.AttributeManager.Item.ItemDefinitionIndex != 59) if (_config.Additional.GiveRandomSkin &&
!gPlayerWeaponsInfo[player.Slot].ContainsKey(weaponDefIndex))
{ {
return HookResult.Continue; // 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.000001f;
CAttributeList_SetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture prefab", weapon.FallbackPaintKit);
fallbackPaintKit = weapon.FallbackPaintKit;
if (fallbackPaintKit == 0)
return;
if (!isKnife)
{
if (newPaints.Contains(fallbackPaintKit))
{
UpdatePlayerWeaponMeshGroupMask(player, weapon, false);
}
else
{
UpdatePlayerWeaponMeshGroupMask(player, weapon, true);
}
}
return;
} }
if (pickupCount >= 2) if (!gPlayerWeaponsInfo[player.Slot].ContainsKey(weaponDefIndex)) return;
WeaponInfo weaponInfo = gPlayerWeaponsInfo[player.Slot][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;
CAttributeList_SetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture prefab", weapon.FallbackPaintKit);
fallbackPaintKit = weapon.FallbackPaintKit;
if (fallbackPaintKit == 0)
return;
if (!isKnife)
{ {
return HookResult.Continue; if (newPaints.Contains(fallbackPaintKit))
{
UpdatePlayerWeaponMeshGroupMask(player, weapon, false);
}
else
{
UpdatePlayerWeaponMeshGroupMask(player, weapon, true);
}
} }
if (g_playersKnife[player.Slot] != "weapon_knife")
{
pickupCount++;
g_knifePickupCount[player.Slot] = pickupCount;
AddTimer(0.2f, () => RefreshWeapons(player));
}
return HookResult.Continue;
} }
private void OnMapStart(string mapName) private void OnMapStart(string mapName)
@@ -171,14 +191,18 @@ namespace WeaponPaints
if (_database != null) if (_database != null)
weaponSync = new WeaponSynchronization(_database, Config); weaponSync = new WeaponSynchronization(_database, Config);
// TODO
// needed for now // needed for now
/*
AddTimer(2.0f, () => AddTimer(2.0f, () =>
{ {
NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); NativeAPI.IssueServerCommand("mp_t_default_melee \"\"");
NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); NativeAPI.IssueServerCommand("mp_ct_default_melee \"\"");
NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0");
//NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0");
}); });
*/
} }
private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info)
@@ -188,10 +212,15 @@ namespace WeaponPaints
if (player is null || !player.IsValid || !Config.Additional.KnifeEnabled && !Config.Additional.GloveEnabled) if (player is null || !player.IsValid || !Config.Additional.KnifeEnabled && !Config.Additional.GloveEnabled)
return HookResult.Continue; return HookResult.Continue;
CCSPlayerPawn? pawn = player.PlayerPawn.Value;
if (pawn == null || !pawn.IsValid)
return HookResult.Continue;
g_knifePickupCount[player.Slot] = 0; g_knifePickupCount[player.Slot] = 0;
if (!PlayerHasKnife(player)) //if (!PlayerHasKnife(player))
GiveKnifeToPlayer(player); //GiveKnifeToPlayer(player);
Server.NextFrame(() => Server.NextFrame(() =>
{ {
@@ -210,100 +239,60 @@ namespace WeaponPaints
private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info) private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info)
{ {
/*
NativeAPI.IssueServerCommand("mp_t_default_melee \"\""); NativeAPI.IssueServerCommand("mp_t_default_melee \"\"");
NativeAPI.IssueServerCommand("mp_ct_default_melee \"\""); NativeAPI.IssueServerCommand("mp_ct_default_melee \"\"");
NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0"); NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0");
*/
g_bCommandsAllowed = true; g_bCommandsAllowed = true;
return HookResult.Continue; return HookResult.Continue;
} }
private void OnTick()
public HookResult OnGiveNamedItemPost(DynamicHook hook)
{ {
foreach (var player in Utilities.GetPlayers().Where(p => var itemServices = hook.GetParam<CCSPlayer_ItemServices>(0);
p is not null && p.IsValid && var weapon = hook.GetReturn<CBasePlayerWeapon>(0);
(LifeState_t)p.LifeState == LifeState_t.LIFE_ALIVE && p.SteamID.ToString().Length == 17 if (!weapon.DesignerName.Contains("weapon"))
&& !p.IsBot && !p.IsHLTV && p.Connected == PlayerConnectedState.PlayerConnected && p.Team != CounterStrikeSharp.API.Modules.Utils.CsTeam.None return HookResult.Continue;
)
) var player = GetPlayerFromItemServices(itemServices);
if (player != null)
GivePlayerWeaponSkin(player, weapon);
return HookResult.Continue;
}
public void OnEntitySpawned(CEntityInstance entity)
{
var designerName = entity.DesignerName;
if (designerName.Contains("weapon"))
{ {
try Server.NextFrame(() =>
{ {
if (Config.Additional.ShowSkinImage && PlayerWeaponImage.ContainsKey(player.Slot) && !string.IsNullOrEmpty(PlayerWeaponImage[player.Slot])) var weapon = new CBasePlayerWeapon(entity.Handle);
{ if (!weapon.IsValid) return;
player.PrintToCenterHtml("<img src='{PATH}'</img>".Replace("{PATH}", PlayerWeaponImage[player.Slot]));
}
if (player.PlayerPawn?.IsValid != true || player.PlayerPawn?.Value?.IsValid != true) var player = Utilities.GetPlayerFromSteamId(weapon.OriginalOwnerXuidLow);
continue; if (player == null || !Utility.IsPlayerValid(player)) return;
var viewModels = GetPlayerViewModels(player); GivePlayerWeaponSkin(player, weapon);
if (viewModels == null || viewModels.Length == 0) });
continue;
var viewModel = viewModels[0];
if (viewModel == null || viewModel.Value == null || viewModel.Value.Weapon == null || viewModel.Value.Weapon.Value == null)
continue;
var weapon = viewModel.Value.Weapon.Value;
if (weapon == null || !weapon.IsValid || weapon.FallbackPaintKit == 0)
continue;
if (viewModel.Value.VMName.Contains("knife"))
continue;
var sceneNode = viewModel.Value.CBodyComponent?.SceneNode;
if (sceneNode == null)
continue;
var skeleton = GetSkeletonInstance(sceneNode);
if (skeleton == null)
continue;
bool skeletonChange = false;
int[] newPaints = { 1171, 1170, 1169, 1164, 1162, 1161, 1159, 1175, 1174, 1167, 1165, 1168, 1163, 1160, 1166, 1173 };
if (newPaints.Contains(weapon.FallbackPaintKit))
{
if (skeleton.ModelState.MeshGroupMask != 1)
{
skeleton.ModelState.MeshGroupMask = 1;
skeletonChange = true;
}
}
else
{
if (skeleton.ModelState.MeshGroupMask != 2)
{
skeleton.ModelState.MeshGroupMask = 2;
skeletonChange = true;
}
}
if (skeletonChange)
Utilities.SetStateChanged(viewModel.Value, "CBaseEntity", "m_CBodyComponent");
}
catch (Exception)
{
}
} }
} }
private void RegisterListeners() private void RegisterListeners()
{ {
RegisterListener<Listeners.OnEntitySpawned>(OnEntityCreated);
//RegisterListener<Listeners.OnClientPutInServer>(OnClientPutInServer);
//RegisterListener<Listeners.OnClientDisconnect>(OnClientDisconnect);
RegisterListener<Listeners.OnMapStart>(OnMapStart); RegisterListener<Listeners.OnMapStart>(OnMapStart);
RegisterListener<Listeners.OnTick>(OnTick);
RegisterEventHandler<EventPlayerSpawn>(OnPlayerSpawn); RegisterEventHandler<EventPlayerSpawn>(OnPlayerSpawn);
RegisterEventHandler<EventRoundStart>(OnRoundStart, HookMode.Pre); RegisterEventHandler<EventRoundStart>(OnRoundStart, HookMode.Pre);
RegisterEventHandler<EventRoundEnd>(OnRoundEnd); RegisterEventHandler<EventRoundEnd>(OnRoundEnd);
RegisterListener<Listeners.OnEntitySpawned>(OnEntitySpawned);
//VirtualFunctions.GiveNamedItemFunc.Hook(OnGiveNamedItemPost, HookMode.Post);
//RegisterEventHandler<EventItemPickup>(OnItemPickup); //HookEntityOutput("weapon_knife", "OnPlayerPickup", OnPickup);
HookEntityOutput("weapon_knife", "OnPlayerPickup", OnPickup);
} }
} }
} }

View File

@@ -5,17 +5,11 @@ namespace WeaponPaints;
public static class PlayerExtensions public static class PlayerExtensions
{ {
public static void Print(this CCSPlayerController controller, string message) public static void Print(this CCSPlayerController controller, string message)
{ {
if (WeaponPaints._localizer == null) if (WeaponPaints._localizer == null) return;
{ StringBuilder _message = new(WeaponPaints._localizer["wp_prefix"]);
controller.PrintToChat(message); _message.Append(message);
} controller.PrintToChat(_message.ToString());
else }
{
StringBuilder _message = new(WeaponPaints._localizer["wp_prefix"]);
_message.Append(message);
controller.PrintToChat(_message.ToString());
}
}
} }

View File

@@ -1,24 +1,12 @@
namespace WeaponPaints namespace WeaponPaints
{ {
public class PlayerInfo public class PlayerInfo
{ {
public int Index { get; set; } public int Index { get; set; }
public int Slot { get; set; } public int Slot { get; set; }
public int? UserId { get; set; } public int? UserId { get; set; }
public string? SteamId { get; set; } public string? SteamId { get; set; }
public string? Name { get; set; } public string? Name { get; set; }
public string? IpAddress { get; set; } public string? IpAddress { get; set; }
}
public PlayerInfo() { }
public PlayerInfo(int index, int slot, int? userId, string? steamId, string? name, string? ipAddress)
{
Index = index;
Slot = slot;
UserId = userId;
SteamId = steamId;
Name = name;
IpAddress = ipAddress;
}
}
} }

View File

@@ -15,7 +15,6 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
- Added command **`!wp`** to refresh skins ***(with cooldown in seconds can be configured)*** - Added command **`!wp`** to refresh skins ***(with cooldown in seconds can be configured)***
- Added command **`!ws`** to show website - Added command **`!ws`** to show website
- Added command **`!knife`** to show menu with knives - 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 - Translations support, submit a PR if you want to share your translation
## CS2 Server ## CS2 Server
@@ -52,7 +51,6 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
"SkinMenuTitle": "Select skin for {WEAPON}" // Menu title (!skins menu, after weapon select) "SkinMenuTitle": "Select skin for {WEAPON}" // Menu title (!skins menu, after weapon select)
}, },
"Additional": { "Additional": {
"SkinVisibilityFix": true, // Enable or disable fix for skin visibility
"KnifeEnabled": true, // Enable or disable knife feature "KnifeEnabled": true, // Enable or disable knife feature
"SkinEnabled": true, // Enable or disable skin feature "SkinEnabled": true, // Enable or disable skin feature
"CommandWpEnabled": true, // Enable or disable refreshing command "CommandWpEnabled": true, // Enable or disable refreshing command
@@ -83,11 +81,6 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
- Steam login/logout - Steam login/logout
- Change knife, paint, seed and wear - Change knife, paint, seed and wear
## Known issues
- Issue on Windows servers, no knives are given.
- You can't change knife if it's equpied in cs2 inventory
- Can cause incompatibility with plugins/maps which manipulates weapons and knives
## Troubleshooting ## Troubleshooting
<details> <details>
**Skins are not changing:** **Skins are not changing:**

View File

@@ -10,16 +10,15 @@ public class SchemaString<TSchemaClass> : NativeObject where TSchemaClass : Nati
internal SchemaString(TSchemaClass instance, string member) : base(Schema.GetSchemaValue<nint>(instance.Handle, typeof(TSchemaClass).Name!, member)) internal SchemaString(TSchemaClass instance, string member) : base(Schema.GetSchemaValue<nint>(instance.Handle, typeof(TSchemaClass).Name!, member))
{ } { }
internal unsafe void Set(string str) internal unsafe void Set(string str)
{ {
var bytes = Encoding.UTF8.GetBytes(str); var bytes = Encoding.UTF8.GetBytes(str);
var handle = Handle.ToInt64();
for (var i = 0; i < bytes.Length; i++) for (var i = 0; i < bytes.Length; i++)
{ {
Unsafe.Write((void*)(handle + i), bytes[i]); Unsafe.Write((void*)(Handle.ToInt64() + i), bytes[i]);
} }
Unsafe.Write((void*)(handle + bytes.Length), 0); Unsafe.Write((void*)(Handle.ToInt64() + bytes.Length), 0);
} }
} }

View File

@@ -43,33 +43,26 @@ namespace WeaponPaints
{ {
string[] createTableQueries = new[] string[] createTableQueries = new[]
{ {
@"CREATE TABLE IF NOT EXISTS `wp_player_skins` ( @"CREATE TABLE IF NOT EXISTS `wp_player_skins` (
`steamid` varchar(64) NOT NULL, `steamid` varchar(64) NOT NULL,
`weapon_defindex` int(6) NOT NULL, `weapon_defindex` int(6) NOT NULL,
`weapon_paint_id` int(6) NOT NULL, `weapon_paint_id` int(6) NOT NULL,
`weapon_wear` float NOT NULL DEFAULT 0.000001, `weapon_wear` float NOT NULL DEFAULT 0.000001,
`weapon_seed` int(16) NOT NULL DEFAULT 0 `weapon_seed` int(16) NOT NULL DEFAULT 0
) ENGINE=InnoDB", ) ENGINE=InnoDB",
@"CREATE TABLE IF NOT EXISTS `wp_player_knife` ( @"CREATE TABLE IF NOT EXISTS `wp_player_knife` (
`steamid` varchar(64) NOT NULL, `steamid` varchar(64) NOT NULL,
`knife` varchar(64) NOT NULL, `knife` varchar(64) NOT NULL,
UNIQUE (`steamid`) UNIQUE (`steamid`)
) ENGINE = InnoDB", ) ENGINE = InnoDB",
@"CREATE TABLE IF NOT EXISTS `wp_player_gloves` ( @"CREATE TABLE IF NOT EXISTS `wp_player_gloves` (
`steamid` varchar(64) NOT NULL, `steamid` varchar(64) NOT NULL,
`weapon_defindex` int(11) NOT NULL, `weapon_defindex` int(11) NOT NULL,
UNIQUE (`steamid`) UNIQUE (`steamid`)
) ENGINE=InnoDB" ) ENGINE=InnoDB"
}; };
/*string[] createTableQueries = new[]
{ foreach (var query in createTableQueries)
@"CREATE TABLE IF NOT EXISTS `wp_players` (`steamid` BIGINT UNSIGNED NOT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
@"CREATE TABLE IF NOT EXISTS `wp_players_skins` (`steamid` BIGINT UNSIGNED NOT NULL, `team` SMALLINT UNSIGNED NOT NULL, `weapon` SMALLINT UNSIGNED NOT NULL, `paint` SMALLINT UNSIGNED NOT NULL, `wear` FLOAT NOT NULL DEFAULT 0.00001, `seed` SMALLINT UNSIGNED NOT NULL DEFAULT 0, `nametag` VARCHAR(20) DEFAULT NULL, `stattrack` INT UNSIGNED NOT NULL DEFAULT 0, `stattrack_enabled` SMALLINT NOT NULL DEFAULT 0, PRIMARY KEY (`steamid`,`weapon`,`team`), FOREIGN KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
@"CREATE TABLE IF NOT EXISTS `wp_players_knife` (`steamid` BIGINT UNSIGNED NOT NULL, `knife_t` SMALLINT UNSIGNED NOT NULL, `knife_ct` SMALLINT UNSIGNED NOT NULL, PRIMARY KEY (`steamid`), FOREIGN KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
@"CREATE TABLE IF NOT EXISTS `wp_players_music` (`steamid` BIGINT UNSIGNED NOT NULL, `music` SMALLINT UNSIGNED DEFAULT NULL, PRIMARY KEY (`steamid`), FOREIGN KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
@"CREATE TABLE IF NOT EXISTS `wp_players_gloves` (`steamid` BIGINT UNSIGNED NOT NULL, `glove_t` SMALLINT UNSIGNED NOT NULL, `glove_ct` SMALLINT UNSIGNED NOT NULL, PRIMARY KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"
};*/
foreach (var query in createTableQueries)
{ {
await connection.ExecuteAsync(query, transaction: transaction); await connection.ExecuteAsync(query, transaction: transaction);
} }

View File

@@ -1 +1 @@
1.9b 2.1a

View File

@@ -1,5 +1,6 @@
using CounterStrikeSharp.API; using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Entities.Constants;
using CounterStrikeSharp.API.Modules.Memory; using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Timers; using CounterStrikeSharp.API.Modules.Timers;
using CounterStrikeSharp.API.Modules.Utils; using CounterStrikeSharp.API.Modules.Utils;
@@ -10,165 +11,37 @@ namespace WeaponPaints
{ {
public partial class WeaponPaints public partial class WeaponPaints
{ {
internal static void ChangeWeaponAttributes(CBasePlayerWeapon? weapon, CCSPlayerController? player, bool isKnife = false)
{
if (player is null || weapon is null || !weapon.IsValid || !Utility.IsPlayerValid(player)) return;
if (!gPlayerWeaponsInfo.TryGetValue(player.Slot, out _)) return;
if (isKnife && !g_playersKnife.ContainsKey(player.Slot) || isKnife && g_playersKnife[player.Slot] == "weapon_knife") return;
int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
if (isKnife)
{
weapon.AttributeManager.Item.EntityQuality = 3;
}
int fallbackPaintKit = weapon.FallbackPaintKit;
if (_config.Additional.GiveRandomSkin &&
!gPlayerWeaponsInfo[player.Slot].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.000001f;
fallbackPaintKit = weapon.FallbackPaintKit;
if (fallbackPaintKit == 0)
return;
var foundSkin = skinsList.FirstOrDefault(skin =>
((int?)skin?["weapon_defindex"] ?? 0) == weaponDefIndex &&
((int?)skin?["paint"] ?? 0) == fallbackPaintKit &&
skin?["paint_name"] != null
);
string skinName = foundSkin?["paint_name"]?.ToString() ?? "";
if (!string.IsNullOrEmpty(skinName))
new SchemaString<CEconItemView>(weapon.AttributeManager.Item, "m_szCustomName").Set(skinName);
if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null)
{
var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode);
int[] newPaints = { 1171, 1170, 1169, 1164, 1162, 1161, 1159, 1175, 1174, 1167, 1165, 1168, 1163, 1160, 1166, 1173 };
if (newPaints.Contains(fallbackPaintKit))
{
skeleton.ModelState.MeshGroupMask = 1;
}
else
{
if (skeleton.ModelState.MeshGroupMask != 2)
{
skeleton.ModelState.MeshGroupMask = 2;
}
}
}
var viewModels = GetPlayerViewModels(player);
if (viewModels == null || viewModels.Length == 0)
return;
var viewModel = viewModels[0];
if (viewModel == null || viewModel.Value == null || viewModel.Value.Weapon == null || viewModel.Value.Weapon.Value == null)
return;
Utilities.SetStateChanged(viewModel.Value, "CBaseEntity", "m_CBodyComponent");
return;
}
if (!gPlayerWeaponsInfo[player.Slot].ContainsKey(weaponDefIndex)) return;
WeaponInfo weaponInfo = gPlayerWeaponsInfo[player.Slot][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;
fallbackPaintKit = weapon.FallbackPaintKit;
if (fallbackPaintKit == 0)
return;
var foundSkin1 = skinsList.FirstOrDefault(skin =>
((int?)skin?["weapon_defindex"] ?? 0) == weaponDefIndex &&
((int?)skin?["paint"] ?? 0) == fallbackPaintKit &&
skin?["paint_name"] != null
);
var skinName1 = foundSkin1?["paint_name"]?.ToString() ?? "";
if (!string.IsNullOrEmpty(skinName1))
new SchemaString<CEconItemView>(weapon.AttributeManager.Item, "m_szCustomName").Set(skinName1);
if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null)
{
var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode);
int[] newPaints = { 1171, 1170, 1169, 1164, 1162, 1161, 1159, 1175, 1174, 1167, 1165, 1168, 1163, 1160, 1166, 1173 };
if (newPaints.Contains(fallbackPaintKit))
{
skeleton.ModelState.MeshGroupMask = 1;
}
else
{
if (skeleton.ModelState.MeshGroupMask != 2)
{
skeleton.ModelState.MeshGroupMask = 2;
}
}
}
var viewModels1 = GetPlayerViewModels(player);
if (viewModels1 == null || viewModels1.Length == 0)
return;
var viewModel1 = viewModels1[0];
if (viewModel1 == null || viewModel1.Value == null || viewModel1.Value.Weapon == null || viewModel1.Value.Weapon.Value == null)
return;
Utilities.SetStateChanged(viewModel1.Value, "CBaseEntity", "m_CBodyComponent");
}
internal static void GiveKnifeToPlayer(CCSPlayerController? player) internal static void GiveKnifeToPlayer(CCSPlayerController? player)
{ {
if (!_config.Additional.KnifeEnabled || player == null || !player.IsValid) return; if (!_config.Additional.KnifeEnabled || player == null || !player.IsValid) return;
WeaponPaints.Instance.AddTimer(1.0f, () => if (PlayerHasKnife(player)) return;
{
string knifeToGive;
if (g_playersKnife.TryGetValue(player.Slot, out var knife))
{
knifeToGive = knife;
}
else if (_config.Additional.GiveRandomKnife)
{
var knifeTypes = weaponList.Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")).ToList();
if (knifeTypes.Count == 0) //string knifeToGive;
{ //if (g_playersKnife.TryGetValue(player.Slot, out var knife))
Utility.Log("No valid knife types found."); //{
return; // knifeToGive = knife;
} //}
//else if (_config.Additional.GiveRandomKnife)
//{
// var knifeTypes = weaponList.Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")).ToList();
Random random = new(); // if (knifeTypes.Count == 0)
int index = random.Next(knifeTypes.Count); // {
knifeToGive = knifeTypes[index].Key; // Utility.Log("No valid knife types found.");
} // return;
else // }
{
knifeToGive = (CsTeam)player.TeamNum == CsTeam.Terrorist ? "weapon_knife_t" : "weapon_knife";
}
player.GiveNamedItem(knifeToGive); // Random random = new();
}); // int index = random.Next(knifeTypes.Count);
// knifeToGive = knifeTypes[index].Key;
//}
//else
//{
//}
string knifeToGive = (CsTeam)player.TeamNum == CsTeam.Terrorist ? "weapon_knife_t" : "weapon_knife";
player.GiveNamedItem(CsItem.Knife);
} }
internal static bool PlayerHasKnife(CCSPlayerController? player) internal static bool PlayerHasKnife(CCSPlayerController? player)
@@ -180,7 +53,7 @@ namespace WeaponPaints
return false; 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; return false;
var weapons = player.PlayerPawn.Value.WeaponServices?.MyWeapons; var weapons = player.PlayerPawn.Value.WeaponServices?.MyWeapons;
@@ -200,6 +73,7 @@ namespace WeaponPaints
internal void RefreshWeapons(CCSPlayerController? player) internal void RefreshWeapons(CCSPlayerController? player)
{ {
if (!g_bCommandsAllowed) return;
if (player == null || !player.IsValid || player.PlayerPawn?.Value == null || (LifeState_t)player.LifeState != LifeState_t.LIFE_ALIVE) if (player == null || !player.IsValid || player.PlayerPawn?.Value == null || (LifeState_t)player.LifeState != LifeState_t.LIFE_ALIVE)
return; return;
if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null)
@@ -212,6 +86,8 @@ namespace WeaponPaints
if (player.Team == CsTeam.None || player.Team == CsTeam.Spectator) if (player.Team == CsTeam.None || player.Team == CsTeam.Spectator)
return; return;
int playerTeam = player.TeamNum;
//Dictionary<string, (int, int)> weaponsWithAmmo = new Dictionary<string, (int, int)>(); //Dictionary<string, (int, int)> weaponsWithAmmo = new Dictionary<string, (int, int)>();
Dictionary<string, List<(int, int)>> weaponsWithAmmo = new Dictionary<string, List<(int, int)>>(); Dictionary<string, List<(int, int)>> weaponsWithAmmo = new Dictionary<string, List<(int, int)>>();
@@ -254,9 +130,11 @@ namespace WeaponPaints
} }
weaponsWithAmmo[weaponByDefindex].Add((clip1, reservedAmmo)); weaponsWithAmmo[weaponByDefindex].Add((clip1, reservedAmmo));
}
//player.RemoveItemByDesignerName(weapon.Value.DesignerName, false); if (gun == null || gun.VData == null) return;
weapon.Value.Remove();
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -265,42 +143,55 @@ namespace WeaponPaints
} }
} }
for (int i = 1; i <= 3; i++) try
{ {
player.ExecuteClientCommand($"slot {i}"); player.ExecuteClientCommand("slot 3");
player.ExecuteClientCommand($"slot {i}"); player.ExecuteClientCommand("slot 3");
AddTimer(0.1f, () => var weapon = player.PlayerPawn.Value.WeaponServices.ActiveWeapon;
if (weapon is null || !weapon.IsValid || weapon.Value == null) return;
CCSWeaponBaseVData? weaponData = weapon.Value.As<CCSWeaponBase>().VData;
if (weapon.Value.DesignerName.Contains("knife") || weaponData?.GearSlot == gear_slot_t.GEAR_SLOT_KNIFE)
{ {
var weapon = player.PlayerPawn.Value.WeaponServices.ActiveWeapon.Value; CCSWeaponBaseGun gun = weapon.Value.As<CCSWeaponBaseGun>();
CCSWeaponBaseGun? gun = weapon?.As<CCSWeaponBaseGun>();
if (gun == null || gun.VData == null) return; AddTimer(0.3f, () =>
if (gun?.VData?.GearSlot == gear_slot_t.GEAR_SLOT_C4 || gun?.VData?.GearSlot == gear_slot_t.GEAR_SLOT_GRENADES) return;
player.DropActiveWeapon();
AddTimer(0.22f, () =>
{ {
if (gun != null && gun.IsValid && gun.State == CSWeaponState_t.WEAPON_NOT_CARRIED) if (player.TeamNum != playerTeam) return;
player.ExecuteClientCommand("slot 3");
gun = weapon.Value.As<CCSWeaponBaseGun>();
player.DropActiveWeapon();
AddTimer(0.7f, () =>
{ {
weapon?.Remove(); if (player.TeamNum != playerTeam) return;
}
if (gun == null || !gun.IsValid || gun.State != CSWeaponState_t.WEAPON_NOT_CARRIED) return;
gun.Remove();
});
GiveKnifeToPlayer(player);
}); });
}); }
}
catch (Exception ex)
{
Logger.LogWarning($"Cannot remove knife: {ex.Message}");
} }
AddTimer(1.2f, () => AddTimer(0.6f, () =>
{
GiveKnifeToPlayer(player);
foreach (var entry in weaponsWithAmmo)
{
foreach (var ammo in entry.Value)
{ {
var newWeapon = new CBasePlayerWeapon(player.GiveNamedItem(entry.Key)); if (!g_bCommandsAllowed) return;
Server.NextFrame(() =>
foreach (var entry in weaponsWithAmmo)
{
foreach (var ammo in entry.Value)
{
var newWeapon = new CBasePlayerWeapon(player.GiveNamedItem(entry.Key));
Server.NextFrame(() =>
{ {
try try
{ {
@@ -315,13 +206,16 @@ namespace WeaponPaints
Logger.LogWarning("Error setting weapon properties: " + ex.Message); Logger.LogWarning("Error setting weapon properties: " + ex.Message);
} }
}); });
} }
} }
}, TimerFlags.STOP_ON_MAPCHANGE);
}, TimerFlags.STOP_ON_MAPCHANGE);
} }
/*
internal void RefreshKnife(CCSPlayerController? player) internal void RefreshKnife(CCSPlayerController? player)
{ {
return;
if (player == null || !player.IsValid || player.PlayerPawn?.Value == null) if (player == null || !player.IsValid || player.PlayerPawn?.Value == null)
return; return;
@@ -331,30 +225,45 @@ namespace WeaponPaints
var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons;
if (weapons != null && weapons.Count > 0) if (weapons != null && weapons.Count > 0)
{ {
try
{
player.ExecuteClientCommand("slot 3");
player.ExecuteClientCommand("slot 3");
var weapon = player.PlayerPawn.Value.WeaponServices.ActiveWeapon;
if (weapon is null || !weapon.IsValid || weapon.Value == null) return;
CCSWeaponBaseVData? weaponData = weapon.Value.As<CCSWeaponBase>().VData;
if (weapon.Value.DesignerName.Contains("knife") || weaponData?.GearSlot == gear_slot_t.GEAR_SLOT_KNIFE)
{
AddTimer(0.2f, () =>
{
player.ExecuteClientCommand("slot 3");
player.DropActiveWeapon();
AddTimer(0.6f, () =>
{
if (weapon.IsValid)
weapon.Value.Remove();
GiveKnifeToPlayer(player);
});
});
}
}
catch (Exception ex)
{
Logger.LogWarning($"Cannot remove knife: {ex.Message}");
}
return;
foreach (var weapon in weapons) foreach (var weapon in weapons)
{ {
if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid && weapon.Index > 0) if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid && weapon.Index > 0)
{ {
try
{
CCSWeaponBaseVData? weaponData = weapon.Value.As<CCSWeaponBase>().VData;
if (weapon.Value.DesignerName.Contains("knife") || weaponData?.GearSlot == gear_slot_t.GEAR_SLOT_KNIFE)
{
player.ExecuteClientCommand("slot 3");
weapon.Value.Remove();
GiveKnifeToPlayer(player);
}
}
catch (Exception ex)
{
Logger.LogWarning($"Cannot remove knife: {ex.Message}");
}
} }
} }
} }
} }
*/
private static void RefreshGloves(CCSPlayerController player) private static void RefreshGloves(CCSPlayerController player)
{ {
@@ -428,23 +337,73 @@ namespace WeaponPaints
return 0; return 0;
} }
public static void SubclassChange(CBasePlayerWeapon weapon, ushort itemD)
{
var SubclassChangeFunc = VirtualFunction.Create<nint, string, int>(
GameData.GetSignature("ChangeSubclass")
);
SubclassChangeFunc(weapon.Handle, itemD.ToString());
}
public static void UpdateWeaponMeshGroupMask(CBaseEntity weapon, bool isLegacy = false)
{
if (weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null)
{
var skeleton = weapon.CBodyComponent.SceneNode.GetSkeletonInstance();
if (skeleton != null)
{
var value = (ulong)(isLegacy ? 2 : 1);
if (skeleton.ModelState.MeshGroupMask != value)
{
skeleton.ModelState.MeshGroupMask = value;
}
}
}
}
public static void UpdatePlayerWeaponMeshGroupMask(CCSPlayerController player, CBasePlayerWeapon weapon, bool isLegacy)
{
UpdateWeaponMeshGroupMask(weapon, isLegacy);
var viewModel = GetPlayerViewModel(player);
if (viewModel != null && viewModel.Weapon.Value != null && viewModel.Weapon.Value.Index == weapon.Index)
{
UpdateWeaponMeshGroupMask(viewModel, isLegacy);
Utilities.SetStateChanged(viewModel, "CBaseEntity", "m_CBodyComponent");
}
}
public static CCSPlayerController? GetPlayerFromItemServices(CCSPlayer_ItemServices itemServices)
{
var pawn = itemServices.Pawn.Value;
if (pawn == null || !pawn.IsValid || !pawn.Controller.IsValid || pawn.Controller.Value == null) return null;
var player = new CCSPlayerController(pawn.Controller.Value.Handle);
if (!Utility.IsPlayerValid(player)) return null;
return player;
}
private static CSkeletonInstance GetSkeletonInstance(CGameSceneNode node) private static CSkeletonInstance GetSkeletonInstance(CGameSceneNode node)
{ {
Func<nint, nint> GetSkeletonInstance = VirtualFunction.Create<nint, nint>(node.Handle, 8); Func<nint, nint> GetSkeletonInstance = VirtualFunction.Create<nint, nint>(node.Handle, 8);
return new CSkeletonInstance(GetSkeletonInstance(node.Handle)); return new CSkeletonInstance(GetSkeletonInstance(node.Handle));
} }
private static unsafe CBaseViewModel? GetPlayerViewModel(CCSPlayerController player)
private static unsafe CHandle<CBaseViewModel>[]? GetPlayerViewModels(CCSPlayerController player)
{ {
if (player.PlayerPawn.Value == null || player.PlayerPawn.Value.ViewModelServices == null) return null; if (player.PlayerPawn.Value == null || player.PlayerPawn.Value.ViewModelServices == null) return null;
CCSPlayer_ViewModelServices viewModelServices = new CCSPlayer_ViewModelServices(player.PlayerPawn.Value.ViewModelServices!.Handle); CCSPlayer_ViewModelServices viewModelServices = new(player.PlayerPawn.Value.ViewModelServices!.Handle);
return GetFixedArray<CHandle<CBaseViewModel>>(viewModelServices.Handle, "CCSPlayer_ViewModelServices", "m_hViewModel", 3); nint ptr = viewModelServices.Handle + Schema.GetSchemaOffset("CCSPlayer_ViewModelServices", "m_hViewModel");
var references = MemoryMarshal.CreateSpan(ref ptr, 3);
var viewModel = (CHandle<CBaseViewModel>)Activator.CreateInstance(typeof(CHandle<CBaseViewModel>), references[0])!;
if (viewModel == null || viewModel.Value == null) return null;
return viewModel.Value;
} }
public static unsafe T[] GetFixedArray<T>(nint pointer, string @class, string member, int length) where T : CHandle<CBaseViewModel> public static unsafe T[] GetFixedArray<T>(nint pointer, string @class, string member, int length) where T : CHandle<CBaseViewModel>
{ {
nint ptr = pointer + Schema.GetSchemaOffset(@class, member); nint ptr = pointer + Schema.GetSchemaOffset(@class, member);
Span<nint> references = MemoryMarshal.CreateSpan<nint>(ref ptr, length); Span<nint> references = MemoryMarshal.CreateSpan(ref ptr, length);
T[] values = new T[length]; T[] values = new T[length];
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)

View File

@@ -1,18 +1,9 @@
namespace WeaponPaints namespace WeaponPaints
{ {
public class WeaponInfo public class WeaponInfo
{ {
public int Paint { get; set; } public int Paint { get; set; }
public int Seed { get; set; } public int Seed { get; set; } = 0;
public float Wear { get; set; } public float Wear { get; set; } = 0f;
}
public WeaponInfo() : this(0, 0, 0f) { }
public WeaponInfo(int paint, int seed, float wear)
{
Paint = paint;
Seed = seed;
Wear = wear;
}
}
} }

View File

@@ -10,12 +10,12 @@ using System.Collections.Concurrent;
namespace WeaponPaints; namespace WeaponPaints;
[MinimumApiVersion(168)] [MinimumApiVersion(178)]
public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig> public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
{ {
internal static WeaponPaints Instance { get; private set; } = new(); internal static WeaponPaints Instance { get; private set; } = new();
internal static readonly int[] newPaints = { 1171, 1170, 1169, 1164, 1162, 1161, 1159, 1175, 1174, 1167, 1165, 1168, 1163, 1160, 1166, 1173 };
internal static readonly Dictionary<string, string> weaponList = new() internal static readonly Dictionary<string, string> weaponList = new()
{ {
{"weapon_deagle", "Desert Eagle"}, {"weapon_deagle", "Desert Eagle"},
{"weapon_elite", "Dual Berettas"}, {"weapon_elite", "Dual Berettas"},
@@ -151,12 +151,12 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
{ 525, "weapon_knife_skeleton" }, { 525, "weapon_knife_skeleton" },
{ 526, "weapon_knife_kukri" } { 526, "weapon_knife_kukri" }
}; };
public static MemoryFunctionVoid<IntPtr, string, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr> GiveNamedItem2 = new(@"\x55\x48\x89\xE5\x41\x57\x41\x56\x41\x55\x41\x54\x53\x48\x83\xEC\x18\x48\x89\x7D\xC8\x48\x85\xF6\x74");
public WeaponPaintsConfig Config { get; set; } = new(); public WeaponPaintsConfig Config { get; set; } = new();
public override string ModuleAuthor => "Nereziel & daffyy"; public override string ModuleAuthor => "Nereziel & daffyy";
public override string ModuleDescription => "Skin, gloves and knife selector, standalone and web-based"; public override string ModuleDescription => "Skin, gloves and knife selector, standalone and web-based";
public override string ModuleName => "WeaponPaints"; public override string ModuleName => "WeaponPaints";
public override string ModuleVersion => "1.9b"; public override string ModuleVersion => "2.1a";
public static WeaponPaintsConfig GetWeaponPaintsConfig() public static WeaponPaintsConfig GetWeaponPaintsConfig()
{ {
@@ -181,13 +181,15 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
gPlayerWeaponsInfo.TryRemove((int)player.Slot, out _); gPlayerWeaponsInfo.TryRemove((int)player.Slot, out _);
g_playersKnife.TryRemove((int)player.Slot, out _); g_playersKnife.TryRemove((int)player.Slot, out _);
PlayerInfo playerInfo = new PlayerInfo( PlayerInfo playerInfo = new PlayerInfo
(int)player.Slot, {
player.Slot, UserId = player.UserId,
player.UserId, Slot = player.Slot,
player?.SteamID.ToString(), Index = (int)player.Slot,
player?.PlayerName, SteamId = player?.SteamID.ToString(),
player?.IpAddress?.Split(":")[0]); Name = player?.PlayerName,
IpAddress = player?.IpAddress?.Split(":")[0]
};
if (Config.Additional.SkinEnabled) if (Config.Additional.SkinEnabled)
{ {
@@ -217,16 +219,8 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
RegisterListeners(); RegisterListeners();
RegisterCommands(); RegisterCommands();
} }
public static void PlayerGiveNamedItem(CCSPlayerController player, string item)
{
if (!player.PlayerPawn.IsValid) return;
if (player.PlayerPawn.Value == null) return;
if (!player.PlayerPawn.Value.IsValid) return;
if (player.PlayerPawn.Value.ItemServices == null) return;
GiveNamedItem2.Invoke(player.PlayerPawn.Value.ItemServices.Handle, item, 0, 0, 0, 0, 0, 0); public void OnConfigParsed(WeaponPaintsConfig config)
}
public void OnConfigParsed(WeaponPaintsConfig config)
{ {
if (config.DatabaseHost.Length < 1 || config.DatabaseName.Length < 1 || config.DatabaseUser.Length < 1) if (config.DatabaseHost.Length < 1 || config.DatabaseName.Length < 1 || config.DatabaseUser.Length < 1)
{ {

View File

@@ -9,7 +9,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.172" /> <PackageReference Include="CounterStrikeSharp.API" Version="1.0.179" />
<PackageReference Include="Dapper" Version="2.1.28" /> <PackageReference Include="Dapper" Version="2.1.28" />
<PackageReference Include="MySqlConnector" Version="2.3.5" /> <PackageReference Include="MySqlConnector" Version="2.3.5" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
@@ -18,4 +18,8 @@
<ItemGroup> <ItemGroup>
<None Update="lang\**\*.*" CopyToOutputDirectory="PreserveNewest" /> <None Update="lang\**\*.*" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="gamedata\*.*" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project> </Project>

View File

@@ -14,11 +14,13 @@ namespace WeaponPaints
_config = config; _config = config;
} }
internal async Task GetKnifeFromDatabase(PlayerInfo player) public async Task GetKnifeFromDatabase(PlayerInfo player)
{ {
if (!_config.Additional.KnifeEnabled) return;
try try
{ {
if (!_config.Additional.KnifeEnabled || string.IsNullOrEmpty(player?.SteamId))
return;
await using var connection = await _database.GetConnectionAsync(); await using var connection = await _database.GetConnectionAsync();
string query = "SELECT `knife` FROM `wp_player_knife` WHERE `steamid` = @steamid"; string query = "SELECT `knife` FROM `wp_player_knife` WHERE `steamid` = @steamid";
string? playerKnife = await connection.QueryFirstOrDefaultAsync<string>(query, new { steamid = player.SteamId }); string? playerKnife = await connection.QueryFirstOrDefaultAsync<string>(query, new { steamid = player.SteamId });
@@ -28,54 +30,47 @@ namespace WeaponPaints
WeaponPaints.g_playersKnife[player.Slot] = playerKnife; WeaponPaints.g_playersKnife[player.Slot] = playerKnife;
} }
} }
catch (Exception e) catch (Exception ex)
{ {
Utility.Log(e.Message); Utility.Log($"An error occurred in GetKnifeFromDatabase: {ex.Message}");
return;
} }
} }
internal async Task GetGloveFromDatabase(PlayerInfo player) public async Task GetGloveFromDatabase(PlayerInfo player)
{ {
if (!_config.Additional.GloveEnabled) return;
try try
{ {
// Ensure proper disposal of resources using "using" statement if (!_config.Additional.GloveEnabled || string.IsNullOrEmpty(player?.SteamId))
return;
await using var connection = await _database.GetConnectionAsync(); await using var connection = await _database.GetConnectionAsync();
// Construct the SQL query with specific columns for better performance
string query = "SELECT `weapon_defindex` FROM `wp_player_gloves` WHERE `steamid` = @steamid"; string query = "SELECT `weapon_defindex` FROM `wp_player_gloves` WHERE `steamid` = @steamid";
// Execute the query and retrieve glove data
ushort? gloveData = await connection.QueryFirstOrDefaultAsync<ushort?>(query, new { steamid = player.SteamId }); ushort? gloveData = await connection.QueryFirstOrDefaultAsync<ushort?>(query, new { steamid = player.SteamId });
// Check if glove data is retrieved successfully
if (gloveData != null) if (gloveData != null)
{ {
// Update g_playersGlove dictionary with glove data
WeaponPaints.g_playersGlove[player.Slot] = gloveData.Value; WeaponPaints.g_playersGlove[player.Slot] = gloveData.Value;
} }
} }
catch (Exception e) catch (Exception ex)
{ {
// Log any exceptions occurred during database operation Utility.Log($"An error occurred in GetGloveFromDatabase: {ex.Message}");
Utility.Log("An error occurred while fetching glove data: " + e.Message);
} }
} }
internal async Task GetWeaponPaintsFromDatabase(PlayerInfo player) public async Task GetWeaponPaintsFromDatabase(PlayerInfo player)
{ {
if (!_config.Additional.SkinEnabled || player == null || player.SteamId == null) return;
try try
{ {
if (!_config.Additional.SkinEnabled || player == null || string.IsNullOrEmpty(player.SteamId))
return;
await using var connection = await _database.GetConnectionAsync(); await using var connection = await _database.GetConnectionAsync();
string query = "SELECT `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed` FROM `wp_player_skins` WHERE `steamid` = @steamid"; string query = "SELECT * FROM `wp_player_skins` WHERE `steamid` = @steamid";
var playerSkins = await connection.QueryAsync<dynamic>(query, new { steamid = player.SteamId });
var playerSkins = await connection.QueryAsync<dynamic>(query, new { steamid = player.SteamId }); if (playerSkins == null)
return;
if (playerSkins == null) return;
var weaponInfos = new ConcurrentDictionary<int, WeaponInfo>(); var weaponInfos = new ConcurrentDictionary<int, WeaponInfo>();
@@ -98,9 +93,9 @@ namespace WeaponPaints
WeaponPaints.gPlayerWeaponsInfo[player.Slot] = weaponInfos; WeaponPaints.gPlayerWeaponsInfo[player.Slot] = weaponInfos;
} }
catch (Exception e) catch (Exception ex)
{ {
Utility.Log($"Database error occurred: {e.Message}"); Utility.Log($"An error occurred in GetWeaponPaintsFromDatabase: {ex.Message}");
} }
} }
@@ -120,7 +115,6 @@ namespace WeaponPaints
} }
} }
internal async Task SyncGloveToDatabase(PlayerInfo player, int defindex) internal async Task SyncGloveToDatabase(PlayerInfo player, int defindex)
{ {
if (!_config.Additional.GloveEnabled || player == null || string.IsNullOrEmpty(player.SteamId)) return; if (!_config.Additional.GloveEnabled || player == null || string.IsNullOrEmpty(player.SteamId)) return;
@@ -146,35 +140,40 @@ namespace WeaponPaints
{ {
await using var connection = await _database.GetConnectionAsync(); await using var connection = await _database.GetConnectionAsync();
foreach (var weaponInfoPair in weaponsInfo) foreach (var weaponInfoPair in weaponsInfo)
{ {
int weaponDefIndex = weaponInfoPair.Key; int weaponDefIndex = weaponInfoPair.Key;
WeaponInfo weaponInfo = weaponInfoPair.Value; WeaponInfo weaponInfo = weaponInfoPair.Value;
int paintId = weaponInfo.Paint; int paintId = weaponInfo.Paint;
float wear = weaponInfo.Wear; float wear = weaponInfo.Wear;
int seed = weaponInfo.Seed; int seed = weaponInfo.Seed;
string query = "INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed`) " + string queryCheckExistence = "SELECT COUNT(*) FROM `wp_player_skins` WHERE `steamid` = @steamid AND `weapon_defindex` = @weaponDefIndex";
"VALUES (@steamid, @weaponDefIndex, @paintId, @wear, @seed) " +
"ON DUPLICATE KEY UPDATE `weapon_paint_id` = @paintId, `weapon_wear` = @wear, `weapon_seed` = @seed";
var parameters = new DynamicParameters(); int existingRecordCount = await connection.ExecuteScalarAsync<int>(queryCheckExistence, new { steamid = player.SteamId, weaponDefIndex });
parameters.Add("@steamid", player.SteamId);
parameters.Add("@weaponDefIndex", weaponDefIndex);
parameters.Add("@paintId", paintId);
parameters.Add("@wear", wear);
parameters.Add("@seed", seed);
await connection.ExecuteAsync(query, parameters); string query;
} object parameters;
if (existingRecordCount > 0)
{
query = "UPDATE `wp_player_skins` SET `weapon_paint_id` = @paintId, `weapon_wear` = @wear, `weapon_seed` = @seed WHERE `steamid` = @steamid AND `weapon_defindex` = @weaponDefIndex";
parameters = new { steamid = player.SteamId, weaponDefIndex, paintId, wear, seed };
}
else
{
query = "INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed`) " +
"VALUES (@steamid, @weaponDefIndex, @paintId, @wear, @seed)";
parameters = new { steamid = player.SteamId, weaponDefIndex, paintId, wear, seed };
}
} await connection.ExecuteAsync(query, parameters);
catch (Exception e) }
}
catch (Exception e)
{ {
Utility.Log($"Error syncing weapon paints to database: {e.Message}"); Utility.Log($"Error syncing weapon paints to database: {e.Message}");
} }
} }
} }
} }

View File

@@ -1,16 +0,0 @@
{
"CAttributeList_SetOrAddAttributeValueByName": {
"signatures": {
"library": "server",
"windows": "\\x40\\x53\\x41\\x56\\x41\\x57\\x48\\x81\\xEC\\x90\\x00\\x00\\x00\\x0F\\x29\\x74\\x24\\x70",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x49\\x89\\xFE\\x41\\x55\\x41\\x54\\x49\\x89\\xF4\\x53\\x48\\x83\\xEC\\x78"
}
},
"CBaseModelEntity_SetBodygroup": {
"signatures": {
"library": "server",
"windows": "\\x48\\x89\\x5C\\x24\\x08\\x48\\x89\\x74\\x24\\x10\\x57\\x48\\x83\\xEC\\x20\\x41\\x8B\\xF8\\x48\\x8B\\xF2\\x48\\x8B\\xD9\\xE8\\x2A\\x2A\\x2A\\x2A",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x56\\x49\\x89\\xF6\\x41\\x55\\x41\\x89\\xD5\\x41\\x54\\x49\\x89\\xFC\\x48\\x83\\xEC\\x08"
}
}
}

View File

@@ -0,0 +1,23 @@
{
"ChangeSubclass": {
"signatures": {
"library": "server",
"windows": "\\x48\\x89\\x5C\\x24\\x08\\x57\\x48\\x83\\xEC\\x20\\x48\\x8B\\xDA\\x48\\x8B\\xF9\\xE8\\x2A\\x2A\\x2A\\x2A\\x84\\xC0\\x74\\x2A\\x41\\xB0\\x01",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x41\\x55\\x49\\x89\\xF5\\x41\\x54\\x49\\x89\\xFC\\x53\\x48\\x81\\xEC\\xA8\\x00\\x00\\x00"
}
},
"CAttributeList_SetOrAddAttributeValueByName": {
"signatures": {
"library": "server",
"windows": "\\x40\\x53\\x41\\x56\\x41\\x57\\x48\\x81\\xEC\\x90\\x00\\x00\\x00\\x0F\\x29\\x74\\x24\\x70",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x49\\x89\\xFE\\x41\\x55\\x41\\x54\\x49\\x89\\xF4\\x53\\x48\\x83\\xEC\\x78"
}
},
"CBaseModelEntity_SetBodygroup": {
"signatures": {
"library": "server",
"windows": "\\x48\\x89\\x5C\\x24\\x08\\x48\\x89\\x74\\x24\\x10\\x57\\x48\\x83\\xEC\\x20\\x41\\x8B\\xF8\\x48\\x8B\\xF2\\x48\\x8B\\xD9\\xE8\\x2A\\x2A\\x2A\\x2A",
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x56\\x49\\x89\\xF6\\x41\\x55\\x41\\x89\\xD5\\x41\\x54\\x49\\x89\\xFC\\x48\\x83\\xEC\\x08"
}
}
}

View File

@@ -14,4 +14,4 @@
"wp_skin_menu_weapon_title": "Ieroču Izvēlne", "wp_skin_menu_weapon_title": "Ieroču Izvēlne",
"wp_skin_menu_skin_title": "Izvēlieties ādu {lime}{0}{default}", "wp_skin_menu_skin_title": "Izvēlieties ādu {lime}{0}{default}",
"wp_skin_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu ādu" "wp_skin_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu ādu"
} }

View File

@@ -14,4 +14,4 @@
"wp_skin_menu_weapon_title": "Menu Broni", "wp_skin_menu_weapon_title": "Menu Broni",
"wp_skin_menu_skin_title": "Wybierz skórkę dla {lime}{0}{default}", "wp_skin_menu_skin_title": "Wybierz skórkę dla {lime}{0}{default}",
"wp_skin_menu_select": "Wybrałeś {lime}{0}{default} jako swoją skórkę" "wp_skin_menu_select": "Wybrałeś {lime}{0}{default} jako swoją skórkę"
} }

View File

@@ -14,4 +14,4 @@
"wp_skin_menu_weapon_title": "Menu de Armas", "wp_skin_menu_weapon_title": "Menu de Armas",
"wp_skin_menu_skin_title": "Selecione uma skin para {lime}{0}{default}", "wp_skin_menu_skin_title": "Selecione uma skin para {lime}{0}{default}",
"wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin" "wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin"
} }

View File

@@ -14,4 +14,4 @@
"wp_skin_menu_weapon_title": "Menu de Armas", "wp_skin_menu_weapon_title": "Menu de Armas",
"wp_skin_menu_skin_title": "Selecione uma skin para {lime}{0}{default}", "wp_skin_menu_skin_title": "Selecione uma skin para {lime}{0}{default}",
"wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin" "wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin"
} }

View File

@@ -14,4 +14,4 @@
"wp_skin_menu_weapon_title": "Silah Menüsü", "wp_skin_menu_weapon_title": "Silah Menüsü",
"wp_skin_menu_skin_title": "{lime}{0}{default} için bir skin seçin", "wp_skin_menu_skin_title": "{lime}{0}{default} için bir skin seçin",
"wp_skin_menu_select": "{lime}{0}{default} olarak bir skin seçtiniz" "wp_skin_menu_select": "{lime}{0}{default} olarak bir skin seçtiniz"
} }

View File

@@ -14,4 +14,4 @@
"wp_skin_menu_weapon_title": "武器菜单", "wp_skin_menu_weapon_title": "武器菜单",
"wp_skin_menu_skin_title": "为 {lime}{0}{default} 选择皮肤", "wp_skin_menu_skin_title": "为 {lime}{0}{default} 选择皮肤",
"wp_skin_menu_select": "您已选择 {lime}{0}{default} 作为您的皮肤" "wp_skin_menu_select": "您已选择 {lime}{0}{default} 作为您的皮肤"
} }

File diff suppressed because one or more lines are too long