Compare commits

...

83 Commits

Author SHA1 Message Date
Nereziel
edd3733940 updated new skins and added optional languages
to use another lang just rename it to skins.json
2024-10-04 10:36:37 +02:00
Nereziel
492b8a7976 Merge pull request #303 from stefanx111/fix-armory-update
fix gamedata
2024-10-04 01:10:31 +02:00
Dawid Bepierszcz
6db3d00893 Update weaponpaints.json 2024-10-04 00:02:40 +02:00
StefanX
fc79381e1e fix gamedata 2024-10-04 00:34:59 +03:00
Nereziel
7b12d29227 Merge pull request #299 from originalaidn/main
fixing latest skins size & editing every weapon, knife and glove defa…
2024-10-01 21:48:38 +02:00
AiDN™
abff60a1db fixing latest skins size & editing every weapon, knife and glove default images 2024-09-16 23:11:26 +02:00
Nereziel
edb848b4f9 Merge pull request #298 from originalaidn/main
adding missing skins & missing agent #282
2024-09-11 21:55:17 +02:00
AiDN™
694cc548c8 adding missing skins & missing agent #282 2024-09-11 21:34:57 +02:00
Nereziel
201f723a3c Update README.md 2024-06-21 22:38:18 +02:00
Dawid Bepierszcz
d6384f4ecf Merge pull request #270 from stefanx111/fix-sig
fix wildcard
2024-06-01 12:47:09 +02:00
StefanX
85fc0bd4bc fix wildcard 2024-06-01 13:44:33 +03:00
Dawid Bepierszcz
5c7df833cc Merge pull request #269 from stefanx111/fix-win
win sig mistake
2024-05-31 12:15:23 +02:00
StefanX
5640919b09 win sig mistake 2024-05-31 03:18:17 +03:00
Dawid Bepierszcz
0550ef68f4 Merge pull request #267 from stefanx111/sig-workground
sig workground
2024-05-31 01:08:58 +02:00
Dawid Bepierszcz
c0d42a3d9c Merge branch 'main' into sig-workground 2024-05-31 01:06:58 +02:00
StefanX
5636b401ea sig workground 2024-05-31 00:20:48 +03:00
Dawid Bepierszcz
06cfda21f2 Fix for gameupdate - linux only 2024-05-30 00:38:16 +02:00
Dawid Bepierszcz
771a832ae8 Update weaponpaints.json 2024-05-25 20:58:48 +02:00
Dawid Bepierszcz
323c74b49c Merge pull request #261 from daffyyyy/main
2.5a
2024-05-25 15:32:04 +02:00
Dawid Bepierszcz
ee1dffa06b Merge branch 'main' of https://github.com/daffyyyy/cs2-WeaponPaints 2024-05-25 15:30:49 +02:00
Dawid Bepierszcz
d90055ad89 2.5a
- Minor changes
- New gloves applied only on spawn (probably new cs2 update issue)
2024-05-25 15:30:47 +02:00
Dawid Bepierszcz
48854c4eaf Merge pull request #253 from daffyyyy/main
2.4e
2024-04-28 02:20:36 +02:00
Dawid Bepierszcz
c06f7ae3be Merge branch 'Nereziel:main' into main 2024-04-28 02:18:14 +02:00
Dawid Bepierszcz
29461e9de2 2.4e
- Minor changes
2024-04-28 02:17:24 +02:00
Dawid Bepierszcz
74ec584d9a Merge pull request #241 from daffyyyy/main
2.4d
2024-04-26 10:07:05 +02:00
Dawid Bepierszcz
ec0d4f4d5a 2.4d
- Updated for latest css
2024-04-26 10:02:32 +02:00
Dawid Bepierszcz
a8ba645292 Merge branch 'Nereziel:main' into main 2024-04-14 22:10:17 +02:00
Dawid Bepierszcz
e04dd312e8 Merge branch 'main' of https://github.com/daffyyyy/cs2-WeaponPaints 2024-04-14 22:09:55 +02:00
Dawid Bepierszcz
702dea9450 2.4c
- Disable hooking giveitemfunc on windows (counterstrikesharp issue) (fix)
2024-04-14 22:09:53 +02:00
Dawid Bepierszcz
c594cd534e Merge pull request #237 from daffyyyy/main
2.4b
2024-04-08 21:31:43 +02:00
Dawid Bepierszcz
5aaf0e6f62 Merge branch 'Nereziel:main' into main 2024-04-08 21:30:46 +02:00
Dawid Bepierszcz
942e776688 2.4b
- Nothing big ;P
2024-04-08 21:30:22 +02:00
Dawid Bepierszcz
36046fee2d Merge pull request #231 from daffyyyy/main
2.4a
2024-04-03 01:35:07 +02:00
Dawid Bepierszcz
d6de0ce6c8 Merge branch 'Nereziel:main' into main 2024-04-03 01:33:52 +02:00
Dawid Bepierszcz
d2c19d8af8 Merge branch 'main' of https://github.com/daffyyyy/cs2-WeaponPaints 2024-04-03 01:32:27 +02:00
Dawid Bepierszcz
0f6d334621 2.4a
- .NET8
- Mysql queries optimization
- CounterStrikeSharp updated
2024-04-03 01:32:24 +02:00
Dawid Bepierszcz
f99c9b2767 Merge pull request #226 from daffyyyy/main
2.3c
2024-03-27 23:20:43 +01:00
Dawid Bepierszcz
6a2d28c303 Merge branch 'Nereziel:main' into main 2024-03-27 23:19:42 +01:00
Dawid Bepierszcz
27a2ae5be9 Merge branch 'main' of https://github.com/daffyyyy/cs2-WeaponPaints 2024-03-27 23:19:26 +01:00
Dawid Bepierszcz
0af9177f07 2.3c
- Small change for mysql performance
2024-03-27 23:19:24 +01:00
Dawid Bepierszcz
cfb49e1498 Merge pull request #222 from daffyyyy/main
2.3b
2024-03-21 01:13:14 +01:00
Dawid Bepierszcz
bbdb4b82ce Merge branch 'Nereziel:main' into main 2024-03-21 01:12:08 +01:00
Dawid Bepierszcz
bfb7defcaa 2.3b
- Fixed agent team bug
- Minor changes
2024-03-21 01:09:45 +01:00
Dawid Bepierszcz
5626c32d30 Merge pull request #213 from daffyyyy/main
2.3a
2024-03-12 14:19:05 +01:00
Dawid Bepierszcz
a6575e9ab2 Music images 2024-03-12 14:17:56 +01:00
Dawid Bepierszcz
683efbd0ae Merge branch 'main' of https://github.com/daffyyyy/cs2-WeaponPaints 2024-03-12 14:17:02 +01:00
Dawid Bepierszcz
3bc9123a71 Update build.yml 2024-03-12 14:16:52 +01:00
Dawid Bepierszcz
f75ea76deb Merge branch 'Nereziel:main' into main 2024-03-12 14:15:42 +01:00
Dawid Bepierszcz
bf0deda5d7 Merge branch 'main' of https://github.com/daffyyyy/cs2-WeaponPaints 2024-03-12 14:14:52 +01:00
Dawid Bepierszcz
548fdb642d 2.3a
- Added music kits
- Minor changes
2024-03-12 14:14:50 +01:00
Dawid Bepierszcz
8cb9563994 Merge pull request #211 from daffyyyy/main
2.2e
2024-03-11 11:19:41 +01:00
Dawid Bepierszcz
e45714cb9a Merge branch 'Nereziel:main' into main 2024-03-11 11:16:45 +01:00
Dawid Bepierszcz
97e73bfd53 2.2e 2024-03-11 11:16:18 +01:00
Dawid Bepierszcz
6dc047477c Merge branch 'main' of https://github.com/daffyyyy/cs2-WeaponPaints 2024-03-11 11:10:28 +01:00
Dawid Bepierszcz
ced010645d 2.2e
- Changed data loading to non-block main thread
- Additional check for empty agent
2024-03-11 11:10:25 +01:00
Dawid Bepierszcz
b030d5c7e1 Merge pull request #206 from daffyyyy/main
2.2d
2024-03-09 18:47:02 +01:00
Dawid Bepierszcz
90771f76c7 Merge branch 'Nereziel:main' into main 2024-03-09 18:46:39 +01:00
Dawid Bepierszcz
ad8e51a403 Merge branch 'main' of https://github.com/daffyyyy/cs2-WeaponPaints 2024-03-09 18:46:23 +01:00
Dawid Bepierszcz
8c1edddd1e 2.2d
- Fixed skins
2024-03-09 18:46:12 +01:00
Dawid Bepierszcz
6f86cddd13 Merge pull request #204 from daffyyyy/main
2.2c
2024-03-09 12:03:20 +01:00
Dawid Bepierszcz
24801e814b Merge branch 'Nereziel:main' into main 2024-03-09 12:02:55 +01:00
Dawid Bepierszcz
77def8a305 2.2c
- Minor changes
- Updated translations
- Updated css
2024-03-09 12:01:25 +01:00
Dawid Bepierszcz
651fae4d23 Merge pull request #191 from daffyyyy/main
2.2b
2024-03-05 12:28:48 +01:00
Dawid Bepierszcz
692c22f7d1 Merge branch 'Nereziel:main' into main 2024-03-05 12:27:46 +01:00
Dawid Bepierszcz
cf07106641 Merge branch 'main' of https://github.com/daffyyyy/cs2-WeaponPaints 2024-03-05 12:27:21 +01:00
Dawid Bepierszcz
44177f18fe 2.2b
- Small fixes 🍕
2024-03-05 12:27:19 +01:00
Dawid Bepierszcz
88274d78d8 Merge pull request #188 from daffyyyy/main
2.2a

- Updated languages
- Added agents
- Minor changes
- Updated readme
- Gloves small fix
- Fixed showimage
2024-03-05 02:24:48 +01:00
Dawid Bepierszcz
5cef723674 Merge branch 'Nereziel:main' into main 2024-03-05 02:23:07 +01:00
Dawid Bepierszcz
5f83645867 2.2a
- Added agents images
2024-03-05 02:22:02 +01:00
Dawid Bepierszcz
89005fdd4b Merge branch 'main' of https://github.com/daffyyyy/cs2-WeaponPaints 2024-03-05 02:20:42 +01:00
Dawid Bepierszcz
4f432cddf5 2.2a
- Updated languages
- Added agents
- Minor changes
- Updated readme
- Gloves small fix
- Fixed showimage
2024-03-05 02:20:33 +01:00
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
259 changed files with 2583 additions and 13071 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

@@ -62,6 +62,10 @@ jobs:
run: cp website/data/skins.json ${{ env.OUTPUT_PATH }}/skins.json run: cp website/data/skins.json ${{ env.OUTPUT_PATH }}/skins.json
- name: Copy gloves.json - name: Copy gloves.json
run: cp website/data/gloves.json ${{ env.OUTPUT_PATH }}/gloves.json run: cp website/data/gloves.json ${{ env.OUTPUT_PATH }}/gloves.json
- name: Copy agents.json
run: cp website/data/agents.json ${{ env.OUTPUT_PATH }}/agents.json
- name: Copy music.json
run: cp website/data/music.json ${{ env.OUTPUT_PATH }}/music.json
- name: Zip - name: Zip
run: zip -r "${{ env.PROJECT_NAME }}.zip" "${{ env.OUTPUT_PATH }}" gamedata/ run: zip -r "${{ env.PROJECT_NAME }}.zip" "${{ env.OUTPUT_PATH }}" gamedata/
- name: Clean files Website - name: Clean files Website

1
.gitignore vendored
View File

@@ -2,3 +2,4 @@
bin/ bin/
obj/ obj/
website/getskins.php website/getskins.php
.idea/

View File

@@ -1,6 +1,8 @@
using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API;
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;
namespace WeaponPaints namespace WeaponPaints
{ {
@@ -13,7 +15,7 @@ namespace WeaponPaints
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 PlayerInfo PlayerInfo? playerInfo = new PlayerInfo
{ {
UserId = player.UserId, UserId = player.UserId,
Slot = player.Slot, Slot = player.Slot,
@@ -25,39 +27,16 @@ namespace WeaponPaints
try try
{ {
if (player != null && !commandsCooldown.TryGetValue(player.Slot, out DateTime cooldownEndTime) || if (player != null && !commandsCooldown.TryGetValue(player.Slot, out var cooldownEndTime) ||
player != null && DateTime.UtcNow >= (commandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow)) player != null && DateTime.UtcNow >= (commandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
{ {
commandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds); commandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
if (weaponSync != null) if (weaponSync != null)
{ {
var weaponTasks = new List<Task>(); _ = Task.Run(async () => await weaponSync.GetPlayerData(playerInfo));
weaponTasks.Add(Task.Run(async () => GivePlayerGloves(player);
{
await weaponSync.GetWeaponPaintsFromDatabase(playerInfo);
}));
if (Config.Additional.GloveEnabled)
{
weaponTasks.Add(Task.Run(async () =>
{
await weaponSync.GetGloveFromDatabase(playerInfo);
}));
}
if (Config.Additional.KnifeEnabled)
{
weaponTasks.Add(Task.Run(async () =>
{
await weaponSync.GetKnifeFromDatabase(playerInfo);
}));
}
Task.WaitAll(weaponTasks.ToArray());
RefreshGloves(player);
RefreshWeapons(player); RefreshWeapons(player);
} }
@@ -95,11 +74,23 @@ namespace WeaponPaints
player!.Print(Localizer["wp_info_glove"]); player!.Print(Localizer["wp_info_glove"]);
} }
if (Config.Additional.KnifeEnabled) if (Config.Additional.AgentEnabled)
if (!string.IsNullOrEmpty(Localizer["wp_info_knife"])) if (!string.IsNullOrEmpty(Localizer["wp_info_agent"]))
{ {
player!.Print(Localizer["wp_info_knife"]); player!.Print(Localizer["wp_info_agent"]);
} }
if (Config.Additional.MusicEnabled)
if (!string.IsNullOrEmpty(Localizer["wp_info_music"]))
{
player!.Print(Localizer["wp_info_music"]);
}
if (!Config.Additional.KnifeEnabled) return;
if (!string.IsNullOrEmpty(Localizer["wp_info_knife"]))
{
player!.Print(Localizer["wp_info_knife"]);
}
} }
private void RegisterCommands() private void RegisterCommands()
@@ -129,7 +120,7 @@ namespace WeaponPaints
{ {
if (!Config.Additional.KnifeEnabled || !g_bCommandsAllowed) return; if (!Config.Additional.KnifeEnabled || !g_bCommandsAllowed) return;
var knivesOnly = weaponList var knivesOnly = WeaponList
.Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")) .Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet"))
.ToDictionary(pair => pair.Key, pair => pair.Value); .ToDictionary(pair => pair.Key, pair => pair.Value);
@@ -140,37 +131,34 @@ namespace WeaponPaints
var knifeName = option.Text; var knifeName = option.Text;
var knifeKey = knivesOnly.FirstOrDefault(x => x.Value == knifeName).Key; var knifeKey = knivesOnly.FirstOrDefault(x => x.Value == knifeName).Key;
if (!string.IsNullOrEmpty(knifeKey)) if (string.IsNullOrEmpty(knifeKey)) return;
if (!string.IsNullOrEmpty(Localizer["wp_knife_menu_select"]))
{ {
if (!string.IsNullOrEmpty(Localizer["wp_knife_menu_select"])) player!.Print(Localizer["wp_knife_menu_select", knifeName]);
{
player!.Print(Localizer["wp_knife_menu_select", knifeName]);
}
if (!string.IsNullOrEmpty(Localizer["wp_knife_menu_kill"]) && Config.Additional.CommandKillEnabled)
{
player!.Print(Localizer["wp_knife_menu_kill"]);
}
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]
};
g_playersKnife[player.Slot] = knifeKey;
if (g_bCommandsAllowed && (LifeState_t)player.LifeState == LifeState_t.LIFE_ALIVE)
RefreshWeapons(player);
if (weaponSync != null)
Task.Run(async () => await weaponSync.SyncKnifeToDatabase(playerInfo, knifeKey));
} }
if (!string.IsNullOrEmpty(Localizer["wp_knife_menu_kill"]) && Config.Additional.CommandKillEnabled)
{
player!.Print(Localizer["wp_knife_menu_kill"]);
}
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]
};
g_playersKnife[player.Slot] = knifeKey;
if (g_bCommandsAllowed && (LifeState_t)player.LifeState == LifeState_t.LIFE_ALIVE)
RefreshWeapons(player);
if (weaponSync != null)
_ = Task.Run(async () => await weaponSync.SyncKnifeToDatabase(playerInfo, knifeKey));
}; };
foreach (var knifePair in knivesOnly) foreach (var knifePair in knivesOnly)
{ {
@@ -182,8 +170,8 @@ namespace WeaponPaints
if (player == null || player.UserId == null) return; if (player == null || player.UserId == null) return;
if (player != null && !commandsCooldown.TryGetValue(player.Slot, out DateTime cooldownEndTime) || if (!commandsCooldown.TryGetValue(player.Slot, out var cooldownEndTime) ||
player != null && DateTime.UtcNow >= (commandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow)) DateTime.UtcNow >= (commandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
{ {
commandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds); commandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
giveItemMenu.PostSelectAction = PostSelectAction.Close; giveItemMenu.PostSelectAction = PostSelectAction.Close;
@@ -199,7 +187,7 @@ namespace WeaponPaints
private void SetupSkinsMenu() private void SetupSkinsMenu()
{ {
var classNamesByWeapon = weaponList.ToDictionary(kvp => kvp.Value, kvp => kvp.Key); var classNamesByWeapon = WeaponList.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
var weaponSelectionMenu = new ChatMenu(Localizer["wp_skin_menu_weapon_title"]); var weaponSelectionMenu = new ChatMenu(Localizer["wp_skin_menu_weapon_title"]);
// Function to handle skin selection for a specific weapon // Function to handle skin selection for a specific weapon
@@ -207,109 +195,110 @@ namespace WeaponPaints
{ {
if (!Utility.IsPlayerValid(player)) return; if (!Utility.IsPlayerValid(player)) return;
string selectedWeapon = option.Text; var selectedWeapon = option.Text;
if (classNamesByWeapon.TryGetValue(selectedWeapon, out string? selectedWeaponClassname)) if (!classNamesByWeapon.TryGetValue(selectedWeapon, out var selectedWeaponClassname)) return;
{ var skinsForSelectedWeapon = skinsList?.Where(skin =>
if (selectedWeaponClassname == null) return;
var skinsForSelectedWeapon = skinsList?.Where(skin =>
skin != null &&
skin.TryGetValue("weapon_name", out var weaponName) && skin.TryGetValue("weapon_name", out var weaponName) &&
weaponName?.ToString() == selectedWeaponClassname weaponName?.ToString() == selectedWeaponClassname
)?.ToList(); )?.ToList();
var skinSubMenu = new ChatMenu(Localizer["wp_skin_menu_skin_title", selectedWeapon]); var skinSubMenu = new ChatMenu(Localizer["wp_skin_menu_skin_title", selectedWeapon]);
skinSubMenu.PostSelectAction = PostSelectAction.Close;
// Function to handle skin selection for the chosen weapon // Function to handle skin selection for the chosen weapon
var handleSkinSelection = (CCSPlayerController p, ChatMenuOption opt) => var handleSkinSelection = (CCSPlayerController p, ChatMenuOption opt) =>
{
if (!Utility.IsPlayerValid(p)) return;
var steamId = p.SteamID.ToString();
var firstSkin = skinsList?.FirstOrDefault(skin =>
{ {
if (!Utility.IsPlayerValid(p)) return; if (skin.TryGetValue("weapon_name", out var weaponName))
string steamId = p.SteamID.ToString();
var firstSkin = skinsList?.FirstOrDefault(skin =>
{ {
if (skin != null && skin.TryGetValue("weapon_name", out var weaponName)) return weaponName?.ToString() == selectedWeaponClassname;
{
return weaponName?.ToString() == selectedWeaponClassname;
}
return false;
});
string selectedSkin = opt.Text;
string selectedPaintID = selectedSkin.Split('(')[1].Trim(')').Trim();
if (firstSkin != null &&
firstSkin.TryGetValue("weapon_defindex", out var weaponDefIndexObj) &&
weaponDefIndexObj != null &&
int.TryParse(weaponDefIndexObj.ToString(), out var weaponDefIndex) &&
int.TryParse(selectedPaintID, out var paintID))
{
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);
}
p.Print(Localizer["wp_skin_menu_select", selectedSkin]);
if (!gPlayerWeaponsInfo[p.Slot].ContainsKey(weaponDefIndex))
{
gPlayerWeaponsInfo[p.Slot][weaponDefIndex] = new WeaponInfo();
}
gPlayerWeaponsInfo[p.Slot][weaponDefIndex].Paint = paintID;
gPlayerWeaponsInfo[p.Slot][weaponDefIndex].Wear = 0.00f;
gPlayerWeaponsInfo[p.Slot][weaponDefIndex].Seed = 0;
PlayerInfo playerInfo = new PlayerInfo
{
UserId = p.UserId,
Index = (int)p.Index,
SteamId = p.SteamID.ToString(),
Name = p.PlayerName,
IpAddress = p.IpAddress?.Split(":")[0]
};
if (g_bCommandsAllowed && (LifeState_t)p.LifeState == LifeState_t.LIFE_ALIVE)
{
RefreshWeapons(player);
}
} }
}; return false;
});
// Add skin options to the submenu for the selected weapon var selectedSkin = opt.Text;
if (skinsForSelectedWeapon != null) var selectedPaintId = selectedSkin[(selectedSkin.LastIndexOf('(') + 1)..].Trim(')');
if (firstSkin == null ||
!firstSkin.TryGetValue("weapon_defindex", out var weaponDefIndexObj) ||
!int.TryParse(weaponDefIndexObj.ToString(), out var weaponDefIndex) ||
!int.TryParse(selectedPaintId, out var paintId)) return;
{ {
foreach (var skin in skinsForSelectedWeapon.Where(s => s != null)) if (Config.Additional.ShowSkinImage && skinsList != null)
{ {
if (skin.TryGetValue("paint_name", out var paintNameObj) && skin.TryGetValue("paint", out var paintObj)) var foundSkin = skinsList.FirstOrDefault(skin =>
{ ((int?)skin?["weapon_defindex"] ?? 0) == weaponDefIndex &&
var paintName = paintNameObj?.ToString(); ((int?)skin?["paint"] ?? 0) == paintId &&
var paint = paintObj?.ToString(); skin?["image"] != null
);
var image = foundSkin?["image"]?.ToString() ?? "";
PlayerWeaponImage[p.Slot] = image;
AddTimer(2.0f, () => PlayerWeaponImage.Remove(p.Slot), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
if (!string.IsNullOrEmpty(paintName) && !string.IsNullOrEmpty(paint)) p.Print(Localizer["wp_skin_menu_select", selectedSkin]);
{
skinSubMenu.AddMenuOption($"{paintName} ({paint})", handleSkinSelection); if (!gPlayerWeaponsInfo[p.Slot].TryGetValue(weaponDefIndex, out var value))
} {
} value = new WeaponInfo();
gPlayerWeaponsInfo[p.Slot][weaponDefIndex] = value;
}
value.Paint = paintId;
value.Wear = 0.01f;
value.Seed = 0;
PlayerInfo playerInfo = new PlayerInfo
{
UserId = p.UserId,
Slot = p.Slot,
Index = (int)p.Index,
SteamId = p.SteamID.ToString(),
Name = p.PlayerName,
IpAddress = p.IpAddress?.Split(":")[0]
};
if (!g_bCommandsAllowed || (LifeState_t)p.LifeState != LifeState_t.LIFE_ALIVE ||
weaponSync == null) return;
RefreshWeapons(player);
try
{
_ = Task.Run(async () => await weaponSync.SyncWeaponPaintsToDatabase(playerInfo));
}
catch (Exception ex)
{
Utility.Log($"Error syncing weapon paints: {ex.Message}");
}
}
};
// Add skin options to the submenu for the selected weapon
if (skinsForSelectedWeapon != null)
{
foreach (var skin in skinsForSelectedWeapon)
{
if (!skin.TryGetValue("paint_name", out var paintNameObj) ||
!skin.TryGetValue("paint", out var paintObj)) continue;
var paintName = paintNameObj?.ToString();
var paint = paintObj?.ToString();
if (!string.IsNullOrEmpty(paintName) && !string.IsNullOrEmpty(paint))
{
skinSubMenu.AddMenuOption($"{paintName} ({paint})", handleSkinSelection);
} }
} }
if (player != null && Utility.IsPlayerValid(player))
MenuManager.OpenChatMenu(player, skinSubMenu);
} }
if (player != null && Utility.IsPlayerValid(player))
MenuManager.OpenChatMenu(player, skinSubMenu);
}; };
// Add weapon options to the weapon selection menu // Add weapon options to the weapon selection menu
foreach (var weaponClass in weaponList.Keys) foreach (var weaponName in WeaponList.Keys.Select(weaponClass => WeaponList[weaponClass]))
{ {
string weaponName = weaponList[weaponClass];
weaponSelectionMenu.AddMenuOption(weaponName, handleWeaponSelection); weaponSelectionMenu.AddMenuOption(weaponName, handleWeaponSelection);
} }
// Command to open the weapon selection menu for players // Command to open the weapon selection menu for players
@@ -319,8 +308,8 @@ namespace WeaponPaints
if (player == null || player.UserId == null) return; if (player == null || player.UserId == null) return;
if (player != null && !commandsCooldown.TryGetValue(player.Slot, out DateTime cooldownEndTime) || if (!commandsCooldown.TryGetValue(player.Slot, out var cooldownEndTime) ||
player != null && DateTime.UtcNow >= (commandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow)) DateTime.UtcNow >= (commandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
{ {
commandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds); commandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
MenuManager.OpenChatMenu(player, weaponSelectionMenu); MenuManager.OpenChatMenu(player, weaponSelectionMenu);
@@ -335,94 +324,90 @@ namespace WeaponPaints
private void SetupGlovesMenu() private void SetupGlovesMenu()
{ {
var glovesSelectionMenu = new ChatMenu(Localizer["wp_glove_menu_title"]); var glovesSelectionMenu = new ChatMenu(Localizer["wp_glove_menu_title"])
glovesSelectionMenu.PostSelectAction = PostSelectAction.Close; {
PostSelectAction = PostSelectAction.Close
};
var handleGloveSelection = (CCSPlayerController? player, ChatMenuOption option) => var handleGloveSelection = (CCSPlayerController? player, ChatMenuOption option) =>
{ {
if (!Utility.IsPlayerValid(player) || player is null) return; if (!Utility.IsPlayerValid(player) || player is null) return;
string selectedPaintName = option.Text; var selectedPaintName = option.Text;
var selectedGlove = glovesList.FirstOrDefault(g => g.ContainsKey("paint_name") && g["paint_name"]?.ToString() == selectedPaintName); var selectedGlove = glovesList.FirstOrDefault(g => g.ContainsKey("paint_name") && g["paint_name"]?.ToString() == selectedPaintName);
if (selectedGlove != null) var image = selectedGlove?["image"]?.ToString() ?? "";
if (selectedGlove == null ||
!selectedGlove.ContainsKey("weapon_defindex") ||
!selectedGlove.ContainsKey("paint") ||
!int.TryParse(selectedGlove["weapon_defindex"]?.ToString(), out var weaponDefindex) ||
!int.TryParse(selectedGlove["paint"]?.ToString(), out var paint)) return;
if (Config.Additional.ShowSkinImage)
{ {
if ( PlayerWeaponImage[player.Slot] = image;
selectedGlove != null && AddTimer(2.0f, () => PlayerWeaponImage.Remove(player.Slot), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
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 PlayerInfo PlayerInfo playerInfo = new PlayerInfo
{ {
UserId = player.UserId, UserId = player.UserId,
Slot = player.Slot, Slot = player.Slot,
Index = (int)player.Index, Index = (int)player.Index,
SteamId = player.SteamID.ToString(), SteamId = player.SteamID.ToString(),
Name = player.PlayerName, Name = player.PlayerName,
IpAddress = player.IpAddress?.Split(":")[0] IpAddress = player.IpAddress?.Split(":")[0]
};
if (paint != 0)
{
g_playersGlove[player.Slot] = (ushort)weaponDefindex;
if (!gPlayerWeaponsInfo[player.Slot].ContainsKey(weaponDefindex))
{
WeaponInfo weaponInfo = new();
weaponInfo.Paint = paint;
gPlayerWeaponsInfo[player.Slot][weaponDefindex] = weaponInfo;
}
}
else
{
g_playersGlove.TryRemove(player.Slot, out _);
}
if (!string.IsNullOrEmpty(Localizer["wp_glove_menu_select"]))
{
player!.Print(Localizer["wp_glove_menu_select", selectedPaintName]);
}
if (weaponSync != null)
{
Task.Run(async () =>
{
await weaponSync.SyncGloveToDatabase(playerInfo, weaponDefindex);
if (!gPlayerWeaponsInfo[playerInfo.Slot].ContainsKey(weaponDefindex))
{
gPlayerWeaponsInfo[playerInfo.Slot][weaponDefindex] = new WeaponInfo();
}
gPlayerWeaponsInfo[playerInfo.Slot][weaponDefindex].Paint = paint;
gPlayerWeaponsInfo[playerInfo.Slot][weaponDefindex].Wear = 0.00f;
gPlayerWeaponsInfo[playerInfo.Slot][weaponDefindex].Seed = 0;
});
}
RefreshGloves(player);
}
}; };
if (paint != 0)
{
g_playersGlove[player.Slot] = (ushort)weaponDefindex;
if (!gPlayerWeaponsInfo[player.Slot].ContainsKey(weaponDefindex))
{
WeaponInfo weaponInfo = new()
{
Paint = paint
};
gPlayerWeaponsInfo[player.Slot][weaponDefindex] = weaponInfo;
}
}
else
{
g_playersGlove.TryRemove(player.Slot, out _);
}
if (!string.IsNullOrEmpty(Localizer["wp_glove_menu_select"]))
{
player!.Print(Localizer["wp_glove_menu_select", selectedPaintName]);
}
if (weaponSync == null) return;
_ = Task.Run(async () =>
{
await weaponSync.SyncGloveToDatabase(playerInfo, weaponDefindex);
if (!gPlayerWeaponsInfo[playerInfo.Slot].TryGetValue(weaponDefindex, out var value))
{
value = new WeaponInfo();
gPlayerWeaponsInfo[playerInfo.Slot][weaponDefindex] = value;
}
value.Paint = paint;
value.Wear = 0.00f;
value.Seed = 0;
await weaponSync.SyncWeaponPaintsToDatabase(playerInfo);
});
AddTimer(0.1f, () => GivePlayerGloves(player));
AddTimer(0.15f, () => GivePlayerGloves(player));
}; };
// Add weapon options to the weapon selection menu // Add weapon options to the weapon selection menu
foreach (var gloveObject in glovesList) foreach (var paintName in glovesList.Select(gloveObject => gloveObject["paint_name"]?.ToString() ?? "").Where(paintName => paintName.Length > 0))
{ {
string paintName = gloveObject["paint_name"]?.ToString() ?? ""; glovesSelectionMenu.AddMenuOption(paintName, handleGloveSelection);
if (paintName.Length > 0)
glovesSelectionMenu.AddMenuOption(paintName, handleGloveSelection);
} }
// Command to open the weapon selection menu for players // Command to open the weapon selection menu for players
@@ -432,8 +417,8 @@ namespace WeaponPaints
if (player == null || player.UserId == null) return; if (player == null || player.UserId == null) return;
if (player != null && !commandsCooldown.TryGetValue(player.Slot, out DateTime cooldownEndTime) || if (!commandsCooldown.TryGetValue(player.Slot, out var cooldownEndTime) ||
player != null && DateTime.UtcNow >= (commandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow)) DateTime.UtcNow >= (commandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
{ {
commandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds); commandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
MenuManager.OpenChatMenu(player, glovesSelectionMenu); MenuManager.OpenChatMenu(player, glovesSelectionMenu);
@@ -445,5 +430,234 @@ namespace WeaponPaints
} }
}); });
} }
private void SetupAgentsMenu()
{
var handleAgentSelection = (CCSPlayerController? player, ChatMenuOption option) =>
{
if (!Utility.IsPlayerValid(player) || player is null) return;
var selectedPaintName = option.Text;
var selectedAgent = agentsList.FirstOrDefault(g =>
g.ContainsKey("agent_name") &&
g["agent_name"] != null && g["agent_name"]!.ToString() == selectedPaintName &&
g["team"] != null && (int)(g["team"]!) == player.TeamNum);
if (selectedAgent == null) return;
if (
selectedAgent.ContainsKey("model")
)
{
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 (Config.Additional.ShowSkinImage)
{
var image = selectedAgent["image"]?.ToString() ?? "";
PlayerWeaponImage[player.Slot] = image;
AddTimer(2.0f, () => PlayerWeaponImage.Remove(player.Slot), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
if (!string.IsNullOrEmpty(Localizer["wp_agent_menu_select"]))
{
player!.Print(Localizer["wp_agent_menu_select", selectedPaintName]);
}
if (player.TeamNum == 3)
{
g_playersAgent.AddOrUpdate(player.Slot,
key => (selectedAgent["model"]!.ToString().Equals("null") ? null : selectedAgent["model"]!.ToString(), null),
(key, oldValue) => (selectedAgent["model"]!.ToString().Equals("null") ? null : selectedAgent["model"]!.ToString(), oldValue.T));
}
else
{
g_playersAgent.AddOrUpdate(player.Slot,
key => (null, selectedAgent["model"]!.ToString().Equals("null") ? null : selectedAgent["model"]!.ToString()),
(key, oldValue) => (oldValue.CT, selectedAgent["model"]!.ToString().Equals("null") ? null : selectedAgent["model"]!.ToString())
);
}
if (weaponSync != null)
{
_ = Task.Run(async () =>
{
await weaponSync.SyncAgentToDatabase(playerInfo);
});
}
};
};
// Command to open the weapon selection menu for players
AddCommand($"css_{Config.Additional.CommandAgent}", "Agents selection menu", (player, info) =>
{
if (!Utility.IsPlayerValid(player) || !g_bCommandsAllowed) return;
if (player == null || player.UserId == null) return;
if (!commandsCooldown.TryGetValue(player.Slot, out DateTime cooldownEndTime) ||
DateTime.UtcNow >= (commandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
{
var agentsSelectionMenu = new ChatMenu(Localizer["wp_agent_menu_title"])
{
PostSelectAction = PostSelectAction.Close
};
var filteredAgents = agentsList.Where(agentObject =>
{
if (agentObject["team"]?.Value<int>() is { } teamNum)
{
return teamNum == player.TeamNum;
}
else
{
return false;
}
});
// Add weapon options to the weapon selection menu
foreach (var agentObject in filteredAgents)
{
var paintName = agentObject["agent_name"]?.ToString() ?? "";
if (paintName.Length > 0)
agentsSelectionMenu.AddMenuOption(paintName, handleAgentSelection);
}
commandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
MenuManager.OpenChatMenu(player, agentsSelectionMenu);
return;
}
if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"]))
{
player!.Print(Localizer["wp_command_cooldown"]);
}
});
}
private void SetupMusicMenu()
{
var musicSelectionMenu = new ChatMenu(Localizer["wp_music_menu_title"])
{
PostSelectAction = PostSelectAction.Close
};
var handleMusicSelection = (CCSPlayerController? player, ChatMenuOption option) =>
{
if (!Utility.IsPlayerValid(player) || player is null) return;
var selectedPaintName = option.Text;
var selectedMusic = musicList.FirstOrDefault(g => g.ContainsKey("name") && g["name"]?.ToString() == selectedPaintName);
if (selectedMusic != null)
{
if (!selectedMusic.ContainsKey("id") ||
!selectedMusic.ContainsKey("name") ||
!int.TryParse(selectedMusic["id"]?.ToString(), out var paint)) return;
var image = selectedMusic["image"]?.ToString() ?? "";
if (Config.Additional.ShowSkinImage)
{
PlayerWeaponImage[player.Slot] = image;
AddTimer(2.0f, () => PlayerWeaponImage.Remove(player.Slot), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
}
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 (paint != 0)
{
g_playersMusic[player.Slot] = (ushort)paint;
}
else
{
g_playersMusic[player.Slot] = 0;
}
if (!string.IsNullOrEmpty(Localizer["wp_music_menu_select"]))
{
player!.Print(Localizer["wp_music_menu_select", selectedPaintName]);
}
if (weaponSync != null)
{
_ = Task.Run(async () =>
{
await weaponSync.SyncMusicToDatabase(playerInfo, (ushort)paint);
});
}
//RefreshGloves(player);
}
else
{
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]
};
g_playersMusic[player.Slot] = 0;
if (!string.IsNullOrEmpty(Localizer["wp_music_menu_select"]))
{
player!.Print(Localizer["wp_music_menu_select", Localizer["None"]]);
}
if (weaponSync != null)
{
_ = Task.Run(async () =>
{
await weaponSync.SyncMusicToDatabase(playerInfo, 0);
});
}
}
};
musicSelectionMenu.AddMenuOption(Localizer["None"], handleMusicSelection);
// Add weapon options to the weapon selection menu
foreach (var paintName in musicList.Select(musicObject => musicObject["name"]?.ToString() ?? "").Where(paintName => paintName.Length > 0))
{
musicSelectionMenu.AddMenuOption(paintName, handleMusicSelection);
}
// Command to open the weapon selection menu for players
AddCommand($"css_{Config.Additional.CommandMusic}", "Music selection menu", (player, info) =>
{
if (!Utility.IsPlayerValid(player) || !g_bCommandsAllowed) return;
if (player == null || player.UserId == null) return;
if (!commandsCooldown.TryGetValue(player.Slot, out var cooldownEndTime) ||
DateTime.UtcNow >= (commandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
{
commandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
MenuManager.OpenChatMenu(player, musicSelectionMenu);
return;
}
if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"]))
{
player!.Print(Localizer["wp_command_cooldown"]);
}
});
}
} }
} }

View File

@@ -5,15 +5,18 @@ 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;
[JsonPropertyName("GloveEnabled")] [JsonPropertyName("GloveEnabled")]
public bool GloveEnabled { get; set; } = true; public bool GloveEnabled { get; set; } = true;
[JsonPropertyName("MusicEnabled")]
public bool MusicEnabled { get; set; } = true;
[JsonPropertyName("AgentEnabled")]
public bool AgentEnabled { get; set; } = true;
[JsonPropertyName("SkinEnabled")] [JsonPropertyName("SkinEnabled")]
public bool SkinEnabled { get; set; } = true; public bool SkinEnabled { get; set; } = true;
@@ -26,9 +29,15 @@ namespace WeaponPaints
[JsonPropertyName("CommandKnife")] [JsonPropertyName("CommandKnife")]
public string CommandKnife { get; set; } = "knife"; public string CommandKnife { get; set; } = "knife";
[JsonPropertyName("CommandMusic")]
public string CommandMusic { get; set; } = "music";
[JsonPropertyName("CommandGlove")] [JsonPropertyName("CommandGlove")]
public string CommandGlove { get; set; } = "gloves"; public string CommandGlove { get; set; } = "gloves";
[JsonPropertyName("CommandAgent")]
public string CommandAgent { get; set; } = "agents";
[JsonPropertyName("CommandSkin")] [JsonPropertyName("CommandSkin")]
public string CommandSkin { get; set; } = "ws"; public string CommandSkin { get; set; } = "ws";
@@ -47,16 +56,13 @@ namespace WeaponPaints
[JsonPropertyName("GiveRandomSkin")] [JsonPropertyName("GiveRandomSkin")]
public bool GiveRandomSkin { get; set; } = false; public bool GiveRandomSkin { get; set; } = false;
[JsonPropertyName("GiveKnifeAfterRemove")]
public bool GiveKnifeAfterRemove { get; set; } = false;
[JsonPropertyName("ShowSkinImage")] [JsonPropertyName("ShowSkinImage")]
public bool ShowSkinImage { get; set; } = true; public bool ShowSkinImage { get; set; } = true;
} }
public class WeaponPaintsConfig : BasePluginConfig public class WeaponPaintsConfig : BasePluginConfig
{ {
public override int Version { get; set; } = 4; public override int Version { get; set; } = 6;
[JsonPropertyName("DatabaseHost")] [JsonPropertyName("DatabaseHost")]
public string DatabaseHost { get; set; } = ""; public string DatabaseHost { get; set; } = "";
@@ -83,6 +89,6 @@ namespace WeaponPaints
public string Website { get; set; } = "example.com/skins"; public string Website { get; set; } = "example.com/skins";
[JsonPropertyName("Additional")] [JsonPropertyName("Additional")]
public Additional Additional { get; set; } = new Additional(); public Additional Additional { get; set; } = new();
} }
} }

View File

@@ -1,15 +1,11 @@
using MySqlConnector; using Microsoft.Extensions.Logging;
using MySqlConnector;
namespace WeaponPaints namespace WeaponPaints
{ {
public class Database public class Database(string dbConnectionString)
{ {
private readonly string _dbConnectionString; private readonly string _dbConnectionString = dbConnectionString;
public Database(string dbConnectionString)
{
_dbConnectionString = dbConnectionString;
}
public async Task<MySqlConnection> GetConnectionAsync() public async Task<MySqlConnection> GetConnectionAsync()
{ {
@@ -19,8 +15,9 @@ namespace WeaponPaints
await connection.OpenAsync(); await connection.OpenAsync();
return connection; return connection;
} }
catch (Exception) catch (Exception ex)
{ {
WeaponPaints.Instance.Logger.LogError($"Unable to connect to database: {ex.Message}");
throw; throw;
} }
} }

342
Events.cs
View File

@@ -1,6 +1,10 @@
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.Entities;
using CounterStrikeSharp.API.Modules.Memory;
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
using System.Runtime.InteropServices;
namespace WeaponPaints namespace WeaponPaints
{ {
@@ -11,7 +15,7 @@ namespace WeaponPaints
{ {
CCSPlayerController? player = @event.Userid; CCSPlayerController? player = @event.Userid;
if (player is null || !player.IsValid || player.IsBot || player.IsHLTV || player.SteamID.ToString().Length != 17 || if (player is null || !player.IsValid || player.IsBot ||
weaponSync == null || _database == null) return HookResult.Continue; weaponSync == null || _database == null) return HookResult.Continue;
PlayerInfo playerInfo = new PlayerInfo PlayerInfo playerInfo = new PlayerInfo
@@ -26,22 +30,32 @@ namespace WeaponPaints
try try
{ {
_ = Task.Run(async () => await weaponSync.GetPlayerData(playerInfo));
/*
if (Config.Additional.SkinEnabled) if (Config.Additional.SkinEnabled)
{ {
Task.Run(() => weaponSync.GetWeaponPaintsFromDatabase(playerInfo)); _ = Task.Run(async () => await weaponSync.GetWeaponPaintsFromDatabase(playerInfo));
} }
if (Config.Additional.KnifeEnabled) if (Config.Additional.KnifeEnabled)
{ {
Task.Run(() => weaponSync.GetKnifeFromDatabase(playerInfo)); _ = Task.Run(async () => await weaponSync.GetKnifeFromDatabase(playerInfo));
} }
if (Config.Additional.GloveEnabled) if (Config.Additional.GloveEnabled)
{ {
Task.Run(() => weaponSync.GetGloveFromDatabase(playerInfo)); _ = Task.Run(async () => await weaponSync.GetGloveFromDatabase(playerInfo));
} }
if (Config.Additional.AgentEnabled)
{
_ = Task.Run(async () => await weaponSync.GetAgentFromDatabase(playerInfo));
}
if (Config.Additional.MusicEnabled)
{
_ = Task.Run(async () => await weaponSync.GetMusicFromDatabase(playerInfo));
}
*/
} }
catch (Exception ex) catch
{ {
Console.WriteLine($"An error occurred: {ex.Message}");
} }
return HookResult.Continue; return HookResult.Continue;
@@ -50,155 +64,61 @@ namespace WeaponPaints
[GameEventHandler] [GameEventHandler]
public HookResult OnPlayerDisconnect(EventPlayerDisconnect @event, GameEventInfo info) public HookResult OnPlayerDisconnect(EventPlayerDisconnect @event, GameEventInfo info)
{ {
CCSPlayerController player = @event.Userid; CCSPlayerController? player = @event.Userid;
if (player is null || !player.IsValid || player.IsBot || if (player is null || !player.IsValid || player.IsBot) return HookResult.Continue;
player.IsHLTV || player.SteamID.ToString().Length != 17) return HookResult.Continue;
PlayerInfo playerInfo = new PlayerInfo if (Config.Additional.SkinEnabled)
{ {
UserId = player.UserId, gPlayerWeaponsInfo.TryRemove(player.Slot, out _);
Slot = player.Slot, }
Index = (int)player.Index, if (Config.Additional.KnifeEnabled)
SteamId = player.SteamID.ToString(),
Name = player.PlayerName,
IpAddress = player.IpAddress?.Split(":")[0]
};
if (weaponSync != null)
{ {
// Run weapon sync tasks asynchronously g_playersKnife.TryRemove(player.Slot, out _);
Task.Run(async () => }
{ if (Config.Additional.GloveEnabled)
await weaponSync.SyncWeaponPaintsToDatabase(playerInfo); {
}); g_playersGlove.TryRemove(player.Slot, out _);
}
// Remove player data if (Config.Additional.AgentEnabled)
if (Config.Additional.SkinEnabled) {
{ g_playersAgent.TryRemove(player.Slot, out _);
gPlayerWeaponsInfo.TryRemove(player.Slot, out _); }
} if (Config.Additional.MusicEnabled)
if (Config.Additional.KnifeEnabled) {
{ g_playersMusic.TryRemove(player.Slot, out _);
g_playersKnife.TryRemove(player.Slot, out _);
}
if (Config.Additional.GloveEnabled)
{
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)
{
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"))
{
isKnife = true;
}
Server.NextFrame(() =>
{
try
{
if (!weapon.IsValid) return;
if (weapon.OwnerEntity.Value == null) return;
if (weapon.OwnerEntity.Index <= 0) return;
int weaponOwner = (int)weapon.OwnerEntity.Index;
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)
{
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);
if (weapon.AttributeManager.Item.ItemDefinitionIndex != 42 && weapon.AttributeManager.Item.ItemDefinitionIndex != 59)
{
return HookResult.Continue;
}
if (pickupCount >= 2)
{
return HookResult.Continue;
}
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)
{ {
if (!Config.Additional.KnifeEnabled && !Config.Additional.SkinEnabled && !Config.Additional.GloveEnabled) return; if (Config.Additional is { KnifeEnabled: false, SkinEnabled: false, GloveEnabled: false }) return;
if (_database != null) if (_database != null)
weaponSync = new WeaponSynchronization(_database, Config); weaponSync = new WeaponSynchronization(_database, Config);
// TODO
// needed for now
AddTimer(2.0f, () =>
{
NativeAPI.IssueServerCommand("mp_t_default_melee \"\"");
NativeAPI.IssueServerCommand("mp_ct_default_melee \"\"");
NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0");
});
} }
private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info)
{ {
CCSPlayerController? player = @event.Userid; CCSPlayerController? player = @event.Userid;
if (player is null || !player.IsValid || !Config.Additional.KnifeEnabled && !Config.Additional.GloveEnabled) if (player is null || !player.IsValid || Config.Additional is { KnifeEnabled: false, GloveEnabled: false })
return HookResult.Continue;
CCSPlayerPawn? pawn = player.PlayerPawn.Value;
if (pawn == null || !pawn.IsValid)
return HookResult.Continue; return HookResult.Continue;
g_knifePickupCount[player.Slot] = 0; g_knifePickupCount[player.Slot] = 0;
if (!PlayerHasKnife(player)) GivePlayerMusicKit(player);
GiveKnifeToPlayer(player); GivePlayerAgent(player);
GivePlayerGloves(player);
Server.NextFrame(() =>
{
RefreshGloves(player);
});
return HookResult.Continue; return HookResult.Continue;
} }
@@ -212,100 +132,120 @@ 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_ct_default_melee \"\"");
NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0");
g_bCommandsAllowed = true; g_bCommandsAllowed = true;
return HookResult.Continue;
}
public HookResult OnGiveNamedItemPost(DynamicHook hook)
{
try
{
var itemServices = hook.GetParam<CCSPlayer_ItemServices>(0);
var weapon = hook.GetReturn<CBasePlayerWeapon>();
if (!weapon.DesignerName.Contains("weapon"))
return HookResult.Continue;
var player = GetPlayerFromItemServices(itemServices);
if (player != null)
GivePlayerWeaponSkin(player, weapon);
}
catch { }
return HookResult.Continue; return HookResult.Continue;
} }
public void OnEntityCreated(CEntityInstance entity)
{
var designerName = entity.DesignerName;
if (designerName.Contains("weapon"))
{
Server.NextFrame(() =>
{
var weapon = new CBasePlayerWeapon(entity.Handle);
if (!weapon.IsValid) return;
try
{
SteamID? _steamid = null;
if (weapon.OriginalOwnerXuidLow > 0)
_steamid = new(weapon.OriginalOwnerXuidLow);
CCSPlayerController? player = null;
if (_steamid != null && _steamid.IsValid())
{
player = Utilities.GetPlayers().FirstOrDefault(p => p.IsValid && p.SteamID == _steamid.SteamId64);
if (player == null)
player = Utilities.GetPlayerFromSteamId(weapon.OriginalOwnerXuidLow);
}
else
{
CCSWeaponBaseGun gun = weapon.As<CCSWeaponBaseGun>();
player = Utilities.GetPlayerFromIndex((int)weapon.OwnerEntity.Index) ?? Utilities.GetPlayerFromIndex((int)gun.OwnerEntity.Value!.Index);
}
if (string.IsNullOrEmpty(player?.PlayerName)) return;
if (!Utility.IsPlayerValid(player)) return;
GivePlayerWeaponSkin(player, weapon);
}
catch (Exception)
{
return;
}
});
}
}
private void OnTick() private void OnTick()
{ {
if (!Config.Additional.ShowSkinImage) return;
foreach (var player in Utilities.GetPlayers().Where(p => foreach (var player in Utilities.GetPlayers().Where(p =>
p is not null && p.IsValid && p is { IsValid: true, PlayerPawn.IsValid: true } &&
(LifeState_t)p.LifeState == LifeState_t.LIFE_ALIVE && p.SteamID.ToString().Length == 17 (LifeState_t)p.LifeState == LifeState_t.LIFE_ALIVE
&& !p.IsBot && !p.IsHLTV && p.Connected == PlayerConnectedState.PlayerConnected && p.Team != CounterStrikeSharp.API.Modules.Utils.CsTeam.None && !p.IsBot && p is { Connected: PlayerConnectedState.PlayerConnected }
) )
) )
{ {
try if (PlayerWeaponImage.TryGetValue(player.Slot, out var value) && !string.IsNullOrEmpty(value))
{
if (Config.Additional.ShowSkinImage && PlayerWeaponImage.ContainsKey(player.Slot) && !string.IsNullOrEmpty(PlayerWeaponImage[player.Slot]))
{
player.PrintToCenterHtml("<img src='{PATH}'</img>".Replace("{PATH}", PlayerWeaponImage[player.Slot]));
}
if (player.PlayerPawn?.IsValid != true || player.PlayerPawn?.Value?.IsValid != true)
continue;
var viewModels = GetPlayerViewModels(player);
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)
{ {
player.PrintToCenterHtml("<img src='{PATH}'</img>".Replace("{PATH}", value));
} }
} }
} }
[GameEventHandler]
public HookResult OnItemPickup(EventItemPickup @event, GameEventInfo _)
{
if (!IsWindows) return HookResult.Continue;
var player = @event.Userid;
if (player != null && player is { IsValid: true, Connected: PlayerConnectedState.PlayerConnected, PawnIsAlive: true, PlayerPawn.IsValid: true })
{
GiveOnItemPickup(player);
}
return HookResult.Continue;
}
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);
RegisterEventHandler<EventRoundEnd>(OnRoundEnd); RegisterEventHandler<EventRoundEnd>(OnRoundEnd);
RegisterListener<Listeners.OnEntityCreated>(OnEntityCreated);
//RegisterEventHandler<EventItemPickup>(OnItemPickup); if (Config.Additional.ShowSkinImage)
RegisterListener<Listeners.OnTick>(OnTick);
HookEntityOutput("weapon_knife", "OnPlayerPickup", OnPickup); if (!IsWindows)
VirtualFunctions.GiveNamedItemFunc.Hook(OnGiveNamedItemPost, HookMode.Post);
} }
} }
} }

View File

@@ -8,6 +8,7 @@ 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) return; if (WeaponPaints._localizer == null) return;
StringBuilder _message = new(WeaponPaints._localizer["wp_prefix"]); StringBuilder _message = new(WeaponPaints._localizer["wp_prefix"]);
_message.Append(message); _message.Append(message);
controller.PrintToChat(_message.ToString()); controller.PrintToChat(_message.ToString());

View File

@@ -3,9 +3,9 @@
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; init; }
public int? UserId { get; set; } public int? UserId { get; set; }
public string? SteamId { get; set; } public string? SteamId { get; init; }
public string? Name { get; set; } public string? Name { get; set; }
public string? IpAddress { get; set; } public string? IpAddress { get; set; }
} }

View File

@@ -9,13 +9,14 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/E1E2G0P2O) or [![Donate on Steam](https://github.com/Nereziel/cs2-WeaponPaints/assets/32937653/a0d53822-4ca7-4caf-83b4-e1a9b5f8c94e)](https://steamcommunity.com/tradeoffer/new/?partner=41515647&token=gW2W-nXE) [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/E1E2G0P2O) or [![Donate on Steam](https://github.com/Nereziel/cs2-WeaponPaints/assets/32937653/a0d53822-4ca7-4caf-83b4-e1a9b5f8c94e)](https://steamcommunity.com/tradeoffer/new/?partner=41515647&token=gW2W-nXE)
## Features ## Features
- Changes only paint, seed and wear on weapons and knives - Changes only paint, seed and wear on weapons, knives, gloves and agents
- MySQL based - MySQL based
- Data syncs on player connect - Data syncs on player connect
- 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 ""`** - Added command **`!gloves`** to show menu with gloves
- Added command **`!agents`** to show menu with agents
- 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 +53,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
@@ -71,7 +71,7 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
</details> </details>
## Web install ## Web install
- Requires PHP >= 7.4 ***(Tested on php ver **`8.2.3`** and nginx webserver)*** - Requires PHP >= 7.4 with curl and pdo_mysql ***(Tested on php ver **`8.2.3`** and nginx webserver)***
- **Before using website, make sure the plugin is correctly loaded in cs2 server!** Mysql tables are created by plugin not by website. - **Before using website, make sure the plugin is correctly loaded in cs2 server!** Mysql tables are created by plugin not by website.
- Copy website to web server ***(Folder `img` not needed)*** - Copy website to web server ***(Folder `img` not needed)***
- Get [Steam API Key](https://steamcommunity.com/dev/apikey) - Get [Steam API Key](https://steamcommunity.com/dev/apikey)
@@ -83,11 +83,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:**
@@ -96,11 +91,6 @@ Set FollowCSGOGuidelines to false in cssharps core.jcon config
**Database error table does not exists:** **Database error table does not exists:**
Plugin is not loaded or configured with mysql credentials. Tables are auto-created by plugin. Plugin is not loaded or configured with mysql credentials. Tables are auto-created by plugin.
**Knives are disappearing:**
Set in config GiveKnifeAfterRemove to true
**Knives are not changing for players:**
You can't change knife if you have your own equipped
</details> </details>
### Use this plugin at your own risk! Using this may lead to GSLT ban or something else Valve come with. [Valve Server guidelines](https://blog.counter-strike.net/index.php/server_guidelines/) ### Use this plugin at your own risk! Using this may lead to GSLT ban or something else Valve come with. [Valve Server guidelines](https://blog.counter-strike.net/index.php/server_guidelines/)

View File

@@ -1,11 +1,9 @@
using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Utils; using CounterStrikeSharp.API.Core.Translations;
using Dapper; using Dapper;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using MySqlConnector;
using Newtonsoft.Json; using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System.Reflection;
namespace WeaponPaints namespace WeaponPaints
{ {
@@ -13,22 +11,6 @@ namespace WeaponPaints
{ {
internal static WeaponPaintsConfig? Config { get; set; } internal static WeaponPaintsConfig? Config { get; set; }
internal static string BuildDatabaseConnectionString()
{
if (Config == null) return string.Empty;
var builder = new MySqlConnectionStringBuilder
{
Server = Config.DatabaseHost,
UserID = Config.DatabaseUser,
Password = Config.DatabasePassword,
Database = Config.DatabaseName,
Port = (uint)Config.DatabasePort,
Pooling = true
};
return builder.ConnectionString;
}
internal static async Task CheckDatabaseTables() internal static async Task CheckDatabaseTables()
{ {
if (WeaponPaints._database is null) return; if (WeaponPaints._database is null) return;
@@ -41,26 +23,45 @@ namespace WeaponPaints
try try
{ {
string[] createTableQueries = new[] string[] createTableQueries =
{ [
@"CREATE TABLE IF NOT EXISTS `wp_player_skins` ( """
`steamid` varchar(64) NOT NULL, CREATE TABLE IF NOT EXISTS `wp_player_skins` (
`weapon_defindex` int(6) NOT NULL, `steamid` varchar(18) NOT NULL,
`weapon_paint_id` int(6) NOT NULL, `weapon_defindex` int(6) NOT NULL,
`weapon_wear` float NOT NULL DEFAULT 0.000001, `weapon_paint_id` int(6) NOT NULL,
`weapon_seed` int(16) NOT NULL DEFAULT 0 `weapon_wear` float NOT NULL DEFAULT 0.000001,
) ENGINE=InnoDB", `weapon_seed` int(16) NOT NULL DEFAULT 0
@"CREATE TABLE IF NOT EXISTS `wp_player_knife` ( ) ENGINE=InnoDB
`steamid` varchar(64) NOT NULL, """,
@"CREATE TABLE IF NOT EXISTS `wp_player_knife` (
`steamid` varchar(18) 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` ( """
`steamid` varchar(64) NOT NULL, CREATE TABLE IF NOT EXISTS `wp_player_gloves` (
`weapon_defindex` int(11) NOT NULL, `steamid` varchar(18) NOT NULL,
UNIQUE (`steamid`) `weapon_defindex` int(11) NOT NULL,
) ENGINE=InnoDB" UNIQUE (`steamid`)
}; ) ENGINE=InnoDB
""",
"""
CREATE TABLE IF NOT EXISTS `wp_player_agents` (
`steamid` varchar(18) NOT NULL,
`agent_ct` varchar(64) DEFAULT NULL,
`agent_t` varchar(64) DEFAULT NULL,
UNIQUE (`steamid`)
) ENGINE=InnoDB
""",
"""
CREATE TABLE IF NOT EXISTS `wp_player_music` (
`steamid` varchar(64) NOT NULL,
`music_id` int(11) NOT NULL,
UNIQUE (`steamid`)
) ENGINE=InnoDB
""",
];
foreach (var query in createTableQueries) foreach (var query in createTableQueries)
{ {
@@ -83,37 +84,64 @@ namespace WeaponPaints
internal static bool IsPlayerValid(CCSPlayerController? player) internal static bool IsPlayerValid(CCSPlayerController? player)
{ {
if (player is null) return false; if (player is null || WeaponPaints.weaponSync is null) return false;
return (player is not null && player.IsValid && !player.IsBot && !player.IsHLTV && player.UserId.HasValue return player is { IsValid: true, IsBot: false, IsHLTV: false, UserId: not null };
&& WeaponPaints.weaponSync != null && player.Connected == PlayerConnectedState.PlayerConnected && player.SteamID.ToString().Length == 17);
} }
internal static void LoadSkinsFromFile(string filePath) internal static void LoadSkinsFromFile(string filePath, ILogger logger)
{ {
var json = File.ReadAllText(filePath);
try try
{ {
string json = File.ReadAllText(filePath);
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json); var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
WeaponPaints.skinsList = deserializedSkins ?? new List<JObject>(); WeaponPaints.skinsList = deserializedSkins ?? [];
} }
catch (FileNotFoundException) catch (FileNotFoundException)
{ {
throw; logger?.LogError("Not found \"skins.json\" file");
} }
} }
internal static void LoadGlovesFromFile(string filePath) internal static void LoadGlovesFromFile(string filePath, ILogger logger)
{ {
try try
{ {
string json = File.ReadAllText(filePath); var json = File.ReadAllText(filePath);
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json); var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
WeaponPaints.glovesList = deserializedSkins ?? new List<JObject>(); WeaponPaints.glovesList = deserializedSkins ?? [];
} }
catch (FileNotFoundException) catch (FileNotFoundException)
{ {
throw; logger?.LogError("Not found \"gloves.json\" file");
}
}
internal static void LoadAgentsFromFile(string filePath, ILogger logger)
{
try
{
var json = File.ReadAllText(filePath);
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
WeaponPaints.agentsList = deserializedSkins ?? [];
}
catch (FileNotFoundException)
{
logger?.LogError("Not found \"agents.json\" file");
}
}
internal static void LoadMusicFromFile(string filePath, ILogger logger)
{
try
{
var json = File.ReadAllText(filePath);
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
WeaponPaints.musicList = deserializedSkins ?? [];
}
catch (FileNotFoundException)
{
logger?.LogError("Not found \"music.json\" file");
} }
} }
@@ -127,69 +155,50 @@ namespace WeaponPaints
internal static string ReplaceTags(string message) internal static string ReplaceTags(string message)
{ {
if (message.Contains('{')) return message.ReplaceColorTags();
{
string modifiedValue = message;
if (Config != null)
{
modifiedValue = modifiedValue.Replace("{WEBSITE}", Config.Website);
}
foreach (FieldInfo field in typeof(ChatColors).GetFields())
{
string pattern = $"{{{field.Name}}}";
if (message.Contains(pattern, StringComparison.OrdinalIgnoreCase))
{
modifiedValue = modifiedValue.Replace(pattern, field.GetValue(null)!.ToString(), StringComparison.OrdinalIgnoreCase);
}
}
return modifiedValue;
}
return message;
} }
internal static async Task CheckVersion(string version, ILogger logger) internal static async Task CheckVersion(string version, ILogger logger)
{ {
using (HttpClient client = new HttpClient()) using HttpClient client = new();
try
{ {
try var response = await client.GetAsync("https://raw.githubusercontent.com/Nereziel/cs2-WeaponPaints/main/VERSION").ConfigureAwait(false);
if (response.IsSuccessStatusCode)
{ {
HttpResponseMessage response = await client.GetAsync("https://raw.githubusercontent.com/Nereziel/cs2-WeaponPaints/main/VERSION").ConfigureAwait(false); var remoteVersion = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
remoteVersion = remoteVersion.Trim();
if (response.IsSuccessStatusCode) var comparisonResult = string.CompareOrdinal(version, remoteVersion);
switch (comparisonResult)
{ {
string remoteVersion = await response.Content.ReadAsStringAsync().ConfigureAwait(false); case < 0:
remoteVersion = remoteVersion.Trim();
int comparisonResult = string.Compare(version, remoteVersion);
if (comparisonResult < 0)
{
logger.LogWarning("Plugin is outdated! Check https://github.com/Nereziel/cs2-WeaponPaints"); logger.LogWarning("Plugin is outdated! Check https://github.com/Nereziel/cs2-WeaponPaints");
} break;
else if (comparisonResult > 0) case > 0:
{
logger.LogInformation("Probably dev version detected"); logger.LogInformation("Probably dev version detected");
} break;
else default:
{
logger.LogInformation("Plugin is up to date"); logger.LogInformation("Plugin is up to date");
} break;
}
else
{
logger.LogWarning("Failed to check version");
} }
} }
catch (HttpRequestException ex) else
{ {
logger.LogError(ex, "Failed to connect to the version server."); logger.LogWarning("Failed to check version");
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred while checking version.");
} }
} }
catch (HttpRequestException ex)
{
logger.LogError(ex, "Failed to connect to the version server.");
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred while checking version.");
}
} }
internal static void ShowAd(string moduleVersion) internal static void ShowAd(string moduleVersion)
@@ -206,26 +215,5 @@ namespace WeaponPaints
Console.WriteLine(" >> GitHub: https://github.com/Nereziel/cs2-WeaponPaints"); Console.WriteLine(" >> GitHub: https://github.com/Nereziel/cs2-WeaponPaints");
Console.WriteLine(" "); Console.WriteLine(" ");
} }
/*(
internal static void TestDatabaseConnection()
{
try
{
using var connection = new MySqlConnection(BuildDatabaseConnectionString());
connection.Open();
if (connection.State != System.Data.ConnectionState.Open)
{
throw new Exception("[WeaponPaints] Unable connect to database!");
}
}
catch (Exception ex)
{
throw new Exception("[WeaponPaints] Unknown mysql exception! " + ex.Message);
}
CheckDatabaseTables();
}
*/
} }
} }

View File

@@ -1 +1 @@
1.9b 2.5a

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,83 +11,69 @@ namespace WeaponPaints
{ {
public partial class WeaponPaints public partial class WeaponPaints
{ {
internal static void ChangeWeaponAttributes(CBasePlayerWeapon? weapon, CCSPlayerController? player, bool isKnife = false) private void GivePlayerWeaponSkin(CCSPlayerController player, CBasePlayerWeapon weapon)
{ {
if (player is null || weapon is null || !weapon.IsValid || !Utility.IsPlayerValid(player)) return; if (!Config.Additional.SkinEnabled) return;
if (!gPlayerWeaponsInfo.TryGetValue(player.Slot, out _)) return;
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; if (isKnife && !g_playersKnife.ContainsKey(player.Slot) || isKnife && g_playersKnife[player.Slot] == "weapon_knife") return;
int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex; int[] newPaints = { 106, 112, 113, 114, 115, 117, 118, 120, 121, 123, 126, 127, 128, 129, 130, 131, 133, 134, 137, 138, 139, 140, 142, 144, 145, 146, 152, 160, 161, 163, 173, 239, 292, 324, 331, 412, 461, 513, 766, 768, 770, 773, 774, 830, 831, 832, 834, 874, 875, 877, 878, 882, 883, 901, 912, 936, 937, 938, 939, 940, 1054, 1062, 1159, 1160, 1161, 1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, 1170, 1171, 1172, 1173, 1174, 1175, 1177, 1178, 1179, 1180 };
if (isKnife) if (isKnife)
{ {
var newDefIndex = WeaponDefindex.FirstOrDefault(x => x.Value == g_playersKnife[player.Slot]);
if (newDefIndex.Key == 0) return;
if (weapon.AttributeManager.Item.ItemDefinitionIndex != newDefIndex.Key)
{
SubclassChange(weapon, (ushort)newDefIndex.Key);
}
weapon.AttributeManager.Item.ItemDefinitionIndex = (ushort)newDefIndex.Key;
weapon.AttributeManager.Item.EntityQuality = 3; weapon.AttributeManager.Item.EntityQuality = 3;
} }
UpdatePlayerEconItemId(weapon.AttributeManager.Item);
int fallbackPaintKit = weapon.FallbackPaintKit; int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
int fallbackPaintKit = 0;
weapon.AttributeManager.Item.AccountID = (uint)player.SteamID;
if (_config.Additional.GiveRandomSkin && if (_config.Additional.GiveRandomSkin &&
!gPlayerWeaponsInfo[player.Slot].ContainsKey(weaponDefIndex)) !gPlayerWeaponsInfo[player.Slot].ContainsKey(weaponDefIndex))
{ {
// Random skins // 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.FallbackPaintKit = GetRandomPaint(weaponDefIndex);
weapon.FallbackSeed = 0; weapon.FallbackSeed = 0;
weapon.FallbackWear = 0.000001f; weapon.FallbackWear = 0.01f;
weapon.AttributeManager.Item.NetworkedDynamicAttributes.Attributes.RemoveAll();
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture prefab", GetRandomPaint(weaponDefIndex));
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture seed", 0);
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture wear", 0.01f);
weapon.AttributeManager.Item.AttributeList.Attributes.RemoveAll();
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "set item texture prefab", GetRandomPaint(weaponDefIndex));
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "set item texture seed", 0);
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "set item texture wear", 0.01f);
fallbackPaintKit = weapon.FallbackPaintKit; fallbackPaintKit = weapon.FallbackPaintKit;
if (fallbackPaintKit == 0) if (fallbackPaintKit == 0)
return; return;
var foundSkin = skinsList.FirstOrDefault(skin => if (isKnife) return;
((int?)skin?["weapon_defindex"] ?? 0) == weaponDefIndex && UpdatePlayerWeaponMeshGroupMask(player, weapon, !newPaints.Contains(fallbackPaintKit));
((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; return;
} }
if (!gPlayerWeaponsInfo[player.Slot].ContainsKey(weaponDefIndex)) return; if (!gPlayerWeaponsInfo[player.Slot].TryGetValue(weaponDefIndex, out var value) || value.Paint == 0) return;
WeaponInfo weaponInfo = gPlayerWeaponsInfo[player.Slot][weaponDefIndex];
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]}"); //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.ItemID = 16384;
weapon.AttributeManager.Item.ItemIDLow = 16384 & 0xFFFFFFFF; weapon.AttributeManager.Item.ItemIDLow = 16384 & 0xFFFFFFFF;
@@ -94,113 +81,55 @@ namespace WeaponPaints
weapon.FallbackPaintKit = weaponInfo.Paint; weapon.FallbackPaintKit = weaponInfo.Paint;
weapon.FallbackSeed = weaponInfo.Seed; weapon.FallbackSeed = weaponInfo.Seed;
weapon.FallbackWear = weaponInfo.Wear; weapon.FallbackWear = weaponInfo.Wear;
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture prefab", weapon.FallbackPaintKit);
fallbackPaintKit = weapon.FallbackPaintKit; fallbackPaintKit = weapon.FallbackPaintKit;
if (fallbackPaintKit == 0) if (fallbackPaintKit == 0)
return; return;
var foundSkin1 = skinsList.FirstOrDefault(skin => if (isKnife) return;
((int?)skin?["weapon_defindex"] ?? 0) == weaponDefIndex && UpdatePlayerWeaponMeshGroupMask(player, weapon, !newPaints.Contains(fallbackPaintKit));
((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) private 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 = (CsTeam)player.TeamNum == CsTeam.Terrorist ? "weapon_knife_t" : "weapon_knife";
{ player.GiveNamedItem(CsItem.Knife);
Utility.Log("No valid knife types found.");
return;
}
Random random = new();
int index = random.Next(knifeTypes.Count);
knifeToGive = knifeTypes[index].Key;
}
else
{
knifeToGive = (CsTeam)player.TeamNum == CsTeam.Terrorist ? "weapon_knife_t" : "weapon_knife";
}
player.GiveNamedItem(knifeToGive);
});
} }
internal static bool PlayerHasKnife(CCSPlayerController? player) private static bool PlayerHasKnife(CCSPlayerController? player)
{ {
if (!_config.Additional.KnifeEnabled) return false; if (!_config.Additional.KnifeEnabled) return false;
if (player == null || !player.IsValid || player.PlayerPawn == null || !player.PlayerPawn.IsValid) if (player == null || !player.IsValid || !player.PlayerPawn.IsValid)
{ {
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;
if (weapons == null) return false; if (weapons == null) return false;
foreach (var weapon in weapons) foreach (var weapon in weapons)
{ {
if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid) if (!weapon.IsValid || weapon.Value == null || !weapon.Value.IsValid) continue;
if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet"))
{ {
if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet")) return true;
{
return true;
}
} }
} }
return false; return false;
} }
internal void RefreshWeapons(CCSPlayerController? player) private 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)
@@ -208,56 +137,55 @@ namespace WeaponPaints
var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons;
if (weapons == null || weapons.Count == 0) if (weapons.Count == 0)
return; return;
if (player.Team == CsTeam.None || player.Team == CsTeam.Spectator) if (player.Team is CsTeam.None or CsTeam.Spectator)
return; return;
//Dictionary<string, (int, int)> weaponsWithAmmo = new Dictionary<string, (int, int)>(); int playerTeam = player.TeamNum;
Dictionary<string, List<(int, int)>> weaponsWithAmmo = new Dictionary<string, List<(int, int)>>();
Dictionary<string, List<(int, int)>> weaponsWithAmmo = [];
// Iterate through each weapon
foreach (var weapon in weapons) foreach (var weapon in weapons)
{ {
if (weapon == null || !weapon.IsValid || weapon.Value == null || if (!weapon.IsValid || weapon.Value == null ||
!weapon.Value.IsValid || !weapon.Value.DesignerName.Contains("weapon_")) !weapon.Value.IsValid || !weapon.Value.DesignerName.Contains("weapon_"))
continue; continue;
CCSWeaponBaseGun gun = weapon.Value.As<CCSWeaponBaseGun>(); CCSWeaponBaseGun gun = weapon.Value.As<CCSWeaponBaseGun>();
if (weapon.Value.Entity == null) continue; if (weapon.Value.Entity == null) continue;
if (weapon.Value.OwnerEntity == null) continue;
if (!weapon.Value.OwnerEntity.IsValid) continue; if (!weapon.Value.OwnerEntity.IsValid) continue;
if (gun == null) continue;
if (gun.Entity == null) continue; if (gun.Entity == null) continue;
if (!gun.IsValid) continue; if (!gun.IsValid) continue;
if (!gun.VisibleinPVS) continue; if (!gun.VisibleinPVS) continue;
try try
{ {
string? weaponByDefindex = null;
CCSWeaponBaseVData? weaponData = weapon.Value.As<CCSWeaponBase>().VData; CCSWeaponBaseVData? weaponData = weapon.Value.As<CCSWeaponBase>().VData;
if (weaponData == null) continue; if (weaponData == null) continue;
if (weaponData.GearSlot == gear_slot_t.GEAR_SLOT_RIFLE || weaponData.GearSlot == gear_slot_t.GEAR_SLOT_PISTOL) if (weaponData.GearSlot == gear_slot_t.GEAR_SLOT_RIFLE || weaponData.GearSlot == gear_slot_t.GEAR_SLOT_PISTOL)
{ {
if (!WeaponDefindex.TryGetValue(weapon.Value.AttributeManager.Item.ItemDefinitionIndex, out weaponByDefindex)) if (!WeaponDefindex.TryGetValue(weapon.Value.AttributeManager.Item.ItemDefinitionIndex, out var weaponByDefindex))
continue; continue;
int clip1 = weapon.Value.Clip1; int clip1 = weapon.Value.Clip1;
int reservedAmmo = weapon.Value.ReserveAmmo[0]; int reservedAmmo = weapon.Value.ReserveAmmo[0];
if (!weaponsWithAmmo.ContainsKey(weaponByDefindex)) if (!weaponsWithAmmo.TryGetValue(weaponByDefindex, out var value))
{ {
weaponsWithAmmo.Add(weaponByDefindex, new List<(int, int)>()); value = [];
weaponsWithAmmo.Add(weaponByDefindex, value);
} }
weaponsWithAmmo[weaponByDefindex].Add((clip1, reservedAmmo)); value.Add((clip1, reservedAmmo));
}
//player.RemoveItemByDesignerName(weapon.Value.DesignerName, false); if (gun.VData == null) return;
weapon.Value.Remove();
}
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -266,106 +194,80 @@ 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.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;
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.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
{ {
if (newWeapon != null) newWeapon.Clip1 = ammo.Item1;
{ newWeapon.ReserveAmmo[0] = ammo.Item2;
newWeapon.Clip1 = ammo.Item1;
newWeapon.ReserveAmmo[0] = ammo.Item2;
}
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogWarning("Error setting weapon properties: " + ex.Message); Logger.LogWarning("Error setting weapon properties: " + ex.Message);
} }
}); });
}
}
}, TimerFlags.STOP_ON_MAPCHANGE);
}
internal void RefreshKnife(CCSPlayerController? player)
{
if (player == null || !player.IsValid || player.PlayerPawn?.Value == null)
return;
if (player.PlayerPawn.Value.WeaponServices == null)
return;
var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons;
if (weapons != null && weapons.Count > 0)
{
foreach (var weapon in weapons)
{
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) }, TimerFlags.STOP_ON_MAPCHANGE);
{
Logger.LogWarning($"Cannot remove knife: {ex.Message}");
}
}
}
}
} }
private static void RefreshGloves(CCSPlayerController player) private void GivePlayerGloves(CCSPlayerController player)
{ {
if (!Utility.IsPlayerValid(player) || (LifeState_t)player.LifeState != LifeState_t.LIFE_ALIVE) return; if (!Utility.IsPlayerValid(player) || (LifeState_t)player.LifeState != LifeState_t.LIFE_ALIVE) return;
CCSPlayerPawn? pawn = player.PlayerPawn.Value; CCSPlayerPawn? pawn = player.PlayerPawn.Value;
if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) if (pawn == null || !pawn.IsValid)
return; return;
string model = pawn.CBodyComponent?.SceneNode?.GetSkeletonInstance()?.ModelState.ModelName ?? string.Empty; var model = pawn.CBodyComponent?.SceneNode?.GetSkeletonInstance()?.ModelState.ModelName ?? string.Empty;
if (!string.IsNullOrEmpty(model)) if (!string.IsNullOrEmpty(model))
{ {
pawn.SetModel("characters/models/tm_jumpsuit/tm_jumpsuit_varianta.vmdl"); pawn.SetModel("characters/models/tm_jumpsuit/tm_jumpsuit_varianta.vmdl");
@@ -374,35 +276,30 @@ namespace WeaponPaints
Instance.AddTimer(0.06f, () => Instance.AddTimer(0.06f, () =>
{ {
CEconItemView item = pawn.EconGloves;
try try
{ {
if (player == null || !player.IsValid) if (!player.IsValid)
return; return;
if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE) if (!player.PawnIsAlive)
return; return;
if (g_playersGlove.TryGetValue(player.Slot, out var gloveInfo) && gloveInfo != 0) if (!g_playersGlove.TryGetValue(player.Slot, out var gloveInfo) || gloveInfo == 0) return;
{
CCSPlayerPawn? pawn = player.PlayerPawn.Value;
if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE)
return;
WeaponInfo weaponInfo = gPlayerWeaponsInfo[player.Slot][gloveInfo]; WeaponInfo weaponInfo = gPlayerWeaponsInfo[player.Slot][gloveInfo];
CEconItemView item = pawn.EconGloves; item.ItemDefinitionIndex = gloveInfo;
item.ItemDefinitionIndex = gloveInfo; item.ItemIDLow = 16384 & 0xFFFFFFFF;
item.ItemIDLow = 16384 & 0xFFFFFFFF; item.ItemIDHigh = 16384;
item.ItemIDHigh = 16384;
CAttributeList_SetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture prefab", weaponInfo.Paint); CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture prefab", weaponInfo.Paint);
CAttributeList_SetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture seed", weaponInfo.Seed); CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture seed", weaponInfo.Seed);
CAttributeList_SetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture wear", weaponInfo.Wear); CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture wear", weaponInfo.Wear);
item.Initialized = true; item.Initialized = true;
CBaseModelEntity_SetBodygroup.Invoke(pawn, "default_gloves", 1); SetBodygroup(pawn.Handle, "default_gloves", 1);
}
} }
catch (Exception) { } catch (Exception) { }
}, TimerFlags.STOP_ON_MAPCHANGE); }, TimerFlags.STOP_ON_MAPCHANGE);
@@ -410,7 +307,7 @@ namespace WeaponPaints
private static int GetRandomPaint(int defindex) private static int GetRandomPaint(int defindex)
{ {
if (skinsList == null || skinsList.Count == 0) if (skinsList.Count == 0)
return 0; return 0;
Random rnd = new Random(); Random rnd = new Random();
@@ -423,32 +320,127 @@ namespace WeaponPaints
var randomWeapon = filteredWeapons[rnd.Next(filteredWeapons.Count)]; var randomWeapon = filteredWeapons[rnd.Next(filteredWeapons.Count)];
if (int.TryParse(randomWeapon["paint"]?.ToString(), out int paintValue)) return int.TryParse(randomWeapon["paint"]?.ToString(), out var paintValue) ? paintValue : 0;
return paintValue;
return 0;
} }
private static CSkeletonInstance GetSkeletonInstance(CGameSceneNode node) private static void SubclassChange(CBasePlayerWeapon weapon, ushort itemD)
{ {
Func<nint, nint> GetSkeletonInstance = VirtualFunction.Create<nint, nint>(node.Handle, 8); var subclassChangeFunc = VirtualFunction.Create<nint, string, int>(
return new CSkeletonInstance(GetSkeletonInstance(node.Handle)); GameData.GetSignature("ChangeSubclass")
);
subclassChangeFunc(weapon.Handle, itemD.ToString());
} }
private static unsafe CHandle<CBaseViewModel>[]? GetPlayerViewModels(CCSPlayerController player) private static void UpdateWeaponMeshGroupMask(CBaseEntity weapon, bool isLegacy = false)
{
if (weapon.CBodyComponent?.SceneNode == null) return;
var skeleton = weapon.CBodyComponent.SceneNode.GetSkeletonInstance();
var value = (ulong)(isLegacy ? 2 : 1);
if (skeleton.ModelState.MeshGroupMask != value)
{
skeleton.ModelState.MeshGroupMask = value;
}
}
private 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) return;
UpdateWeaponMeshGroupMask(viewModel, isLegacy);
Utilities.SetStateChanged(viewModel, "CBaseEntity", "m_CBodyComponent");
}
private static void GivePlayerAgent(CCSPlayerController player)
{
if (!g_playersAgent.TryGetValue(player.Slot, out var value)) return;
var model = player.TeamNum == 3 ? value.CT : value.T;
if (string.IsNullOrEmpty(model)) return;
if (player.PlayerPawn.Value == null)
return;
try
{
Server.NextFrame(() =>
{
player.PlayerPawn.Value.SetModel(
$"characters/models/{model}.vmdl"
);
});
}
catch (Exception)
{
}
}
private static void GivePlayerMusicKit(CCSPlayerController player)
{
if (!g_playersMusic.TryGetValue(player.Slot, out var value)) return;
if (player.InventoryServices == null) return;
player.InventoryServices.MusicID = value;
Utilities.SetStateChanged(player, "CCSPlayerController", "m_pInventoryServices");
player.MusicKitID = value;
Utilities.SetStateChanged(player, "CCSPlayerController", "m_iMusicKitID");
}
private void GiveOnItemPickup(CCSPlayerController player)
{
var pawn = player.PlayerPawn.Value;
if (pawn == null) return;
var myWeapons = pawn.WeaponServices?.MyWeapons;
if (myWeapons == null) return;
foreach (var handle in myWeapons)
{
var weapon = handle.Value;
if (weapon != null && weapon.DesignerName.Contains("knife"))
{
GivePlayerWeaponSkin(player, weapon);
}
}
}
private void UpdatePlayerEconItemId(CEconItemView econItemView)
{
var itemId = _nextItemId++;
econItemView.ItemID = itemId;
econItemView.ItemIDLow = (uint)itemId & 0xFFFFFFFF;
econItemView.ItemIDHigh = (uint)itemId >> 32;
}
private static CCSPlayerController? GetPlayerFromItemServices(CCSPlayer_ItemServices itemServices)
{
var pawn = itemServices.Pawn.Value;
if (!pawn.IsValid || !pawn.Controller.IsValid || pawn.Controller.Value == null) return null;
var player = new CCSPlayerController(pawn.Controller.Value.Handle);
return !Utility.IsPlayerValid(player) ? null : player;
}
private static unsafe CBaseViewModel? GetPlayerViewModel(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); var 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])!;
return viewModel.Value == null ? null : 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); var ptr = pointer + Schema.GetSchemaOffset(@class, member);
Span<nint> references = MemoryMarshal.CreateSpan<nint>(ref ptr, length); var references = MemoryMarshal.CreateSpan(ref ptr, length);
T[] values = new T[length]; var values = new T[length];
for (int i = 0; i < length; i++) for (var i = 0; i < length; i++)
{ {
values[i] = (T)Activator.CreateInstance(typeof(T), references[i])!; values[i] = (T)Activator.CreateInstance(typeof(T), references[i])!;
} }

View File

@@ -7,14 +7,16 @@ using Microsoft.Extensions.Logging;
using MySqlConnector; using MySqlConnector;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Runtime.InteropServices;
namespace WeaponPaints; namespace WeaponPaints;
[MinimumApiVersion(168)] [MinimumApiVersion(230)]
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 Dictionary<string, string> weaponList = new()
private static readonly Dictionary<string, string> WeaponList = new()
{ {
{"weapon_deagle", "Desert Eagle"}, {"weapon_deagle", "Desert Eagle"},
{"weapon_elite", "Dual Berettas"}, {"weapon_elite", "Dual Berettas"},
@@ -74,25 +76,33 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
{ "weapon_knife_kukri", "Kukri Knife" } { "weapon_knife_kukri", "Kukri Knife" }
}; };
internal static WeaponPaintsConfig _config = new WeaponPaintsConfig(); private static WeaponPaintsConfig _config = new();
internal static IStringLocalizer? _localizer; internal static IStringLocalizer? _localizer;
internal static Dictionary<int, int> g_knifePickupCount = new Dictionary<int, int>(); private static Dictionary<int, int> g_knifePickupCount = new();
internal static ConcurrentDictionary<int, string> g_playersKnife = new ConcurrentDictionary<int, string>(); internal static ConcurrentDictionary<int, string> g_playersKnife = new();
internal static ConcurrentDictionary<int, ushort> g_playersGlove = new ConcurrentDictionary<int, ushort>(); internal static ConcurrentDictionary<int, ushort> g_playersGlove = new();
internal static ConcurrentDictionary<int, ConcurrentDictionary<int, WeaponInfo>> gPlayerWeaponsInfo = new ConcurrentDictionary<int, ConcurrentDictionary<int, WeaponInfo>>(); internal static ConcurrentDictionary<int, ushort> g_playersMusic = new();
internal static List<JObject> skinsList = new List<JObject>(); internal static ConcurrentDictionary<int, (string? CT, string? T)> g_playersAgent = new();
internal static List<JObject> glovesList = new List<JObject>(); internal static ConcurrentDictionary<int, ConcurrentDictionary<int, WeaponInfo>> gPlayerWeaponsInfo = new();
internal static List<JObject> skinsList = new();
internal static List<JObject> glovesList = new();
internal static List<JObject> agentsList = new();
internal static List<JObject> musicList = new();
internal static WeaponSynchronization? weaponSync; internal static WeaponSynchronization? weaponSync;
public static bool g_bCommandsAllowed = true; private static bool g_bCommandsAllowed = true;
internal Dictionary<int, string> PlayerWeaponImage = new(); private Dictionary<int, string> PlayerWeaponImage = new();
internal static Dictionary<int, DateTime> commandsCooldown = new Dictionary<int, DateTime>(); private static Dictionary<int, DateTime> commandsCooldown = new();
internal static Database? _database; internal static Database? _database;
internal static MemoryFunctionVoid<nint, string, float> CAttributeList_SetOrAddAttributeValueByName = new(GameData.GetSignature("CAttributeList_SetOrAddAttributeValueByName")); private static readonly MemoryFunctionVoid<nint, string, float> CAttributeListSetOrAddAttributeValueByName = new(GameData.GetSignature("CAttributeList_SetOrAddAttributeValueByName"));
internal static MemoryFunctionVoid<CBaseModelEntity, string, UInt64> CBaseModelEntity_SetBodygroup = new(GameData.GetSignature("CBaseModelEntity_SetBodygroup"));
public static Dictionary<int, string> WeaponDefindex { get; } = new Dictionary<int, string> private static readonly MemoryFunctionWithReturn<nint, string, int, int> SetBodygroupFunc = new(
GameData.GetSignature("CBaseModelEntity_SetBodygroup"));
private static readonly Func<nint, string, int, int> SetBodygroup = SetBodygroupFunc.Invoke;
private static Dictionary<int, string> WeaponDefindex { get; } = new Dictionary<int, string>
{ {
{ 1, "weapon_deagle" }, { 1, "weapon_deagle" },
{ 2, "weapon_elite" }, { 2, "weapon_elite" },
@@ -151,16 +161,15 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
{ 526, "weapon_knife_kukri" } { 526, "weapon_knife_kukri" }
}; };
private const ulong MinimumCustomItemId = 65578;
private ulong _nextItemId = MinimumCustomItemId;
public static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
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, agents 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.5a";
public static WeaponPaintsConfig GetWeaponPaintsConfig()
{
return _config;
}
public override void Load(bool hotReload) public override void Load(bool hotReload)
{ {
@@ -170,43 +179,39 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
{ {
OnMapStart(string.Empty); OnMapStart(string.Empty);
foreach (var player in Utilities.GetPlayers()) foreach (var player in Enumerable
.OfType<CCSPlayerController>(Utilities.GetPlayers().TakeWhile(player => weaponSync != null))
.Where(player => player.IsValid &&
!string.IsNullOrEmpty(player.IpAddress) && player is
{ IsBot: false, Connected: PlayerConnectedState.PlayerConnected }))
{ {
if (weaponSync == null || player is null || !player.IsValid || player.SteamID.ToString().Length != 17 || !player.PawnIsAlive || player.IsBot || g_knifePickupCount[player.Slot] = 0;
player.IsHLTV || player.Connected != PlayerConnectedState.PlayerConnected) gPlayerWeaponsInfo.TryRemove(player.Slot, out _);
continue; g_playersKnife.TryRemove(player.Slot, out _);
g_playersGlove.TryRemove(player.Slot, out _);
g_playersAgent.TryRemove(player.Slot, out _);
g_knifePickupCount[(int)player.Slot] = 0; PlayerInfo? playerInfo = new PlayerInfo
gPlayerWeaponsInfo.TryRemove((int)player.Slot, out _);
g_playersKnife.TryRemove((int)player.Slot, out _);
PlayerInfo playerInfo = new PlayerInfo
{ {
UserId = player.UserId, UserId = player.UserId,
Slot = player.Slot, Slot = player.Slot,
Index = (int)player.Slot, Index = (int)player.Index,
SteamId = player?.SteamID.ToString(), SteamId = player?.SteamID.ToString(),
Name = player?.PlayerName, Name = player?.PlayerName,
IpAddress = player?.IpAddress?.Split(":")[0] IpAddress = player?.IpAddress?.Split(":")[0]
}; };
if (Config.Additional.SkinEnabled) _ = Task.Run(async () =>
{ {
Task.Run(() => weaponSync.GetWeaponPaintsFromDatabase(playerInfo)); if (weaponSync != null) await weaponSync.GetPlayerData(playerInfo);
} });
if (Config.Additional.KnifeEnabled)
{
Task.Run(() => weaponSync.GetKnifeFromDatabase(playerInfo));
}
if (Config.Additional.GloveEnabled)
{
Task.Run(() => weaponSync.GetGloveFromDatabase(playerInfo));
}
} }
} }
Utility.LoadSkinsFromFile(ModuleDirectory + "/skins.json"); Utility.LoadSkinsFromFile(ModuleDirectory + "/skins.json", Logger);
Utility.LoadGlovesFromFile(ModuleDirectory + "/gloves.json"); Utility.LoadGlovesFromFile(ModuleDirectory + "/gloves.json", Logger);
Utility.LoadAgentsFromFile(ModuleDirectory + "/agents.json", Logger);
Utility.LoadMusicFromFile(ModuleDirectory + "/music.json", Logger);
if (Config.Additional.KnifeEnabled) if (Config.Additional.KnifeEnabled)
SetupKnifeMenu(); SetupKnifeMenu();
@@ -214,6 +219,10 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
SetupSkinsMenu(); SetupSkinsMenu();
if (Config.Additional.GloveEnabled) if (Config.Additional.GloveEnabled)
SetupGlovesMenu(); SetupGlovesMenu();
if (Config.Additional.AgentEnabled)
SetupAgentsMenu();
if (Config.Additional.MusicEnabled)
SetupMusicMenu();
RegisterListeners(); RegisterListeners();
RegisterCommands(); RegisterCommands();
@@ -223,10 +232,18 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
{ {
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)
{ {
Logger.LogError("You need to setup Database credentials in config!"); Logger.LogError("You need to setup Database credentials in \"configs/plugins/WeaponPaints/WeaponPaints.json\"!");
throw new Exception("[WeaponPaints] You need to setup Database credentials in config!"); Unload(false);
return;
} }
if (!File.Exists(Path.GetDirectoryName(Path.GetDirectoryName(ModuleDirectory)) + "/gamedata/weaponpaints.json"))
{
Logger.LogError("You need to upload \"weaponpaints.json\" to \"gamedata directory\"!");
Unload(false);
return;
}
var builder = new MySqlConnectionStringBuilder var builder = new MySqlConnectionStringBuilder
{ {
Server = config.DatabaseHost, Server = config.DatabaseHost,
@@ -234,10 +251,11 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
Password = config.DatabasePassword, Password = config.DatabasePassword,
Database = config.DatabaseName, Database = config.DatabaseName,
Port = (uint)config.DatabasePort, Port = (uint)config.DatabasePort,
Pooling = true Pooling = true,
MaximumPoolSize = 640,
}; };
_database = new(builder.ConnectionString); _database = new Database(builder.ConnectionString);
_ = Utility.CheckDatabaseTables(); _ = Utility.CheckDatabaseTables();

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
@@ -9,13 +9,17 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.168" /> <PackageReference Include="CounterStrikeSharp.API" Version="1.0.233" />
<PackageReference Include="Dapper" Version="2.1.28" /> <PackageReference Include="Dapper" Version="2.1.35" />
<PackageReference Include="MySqlConnector" Version="2.3.5" /> <PackageReference Include="MySqlConnector" Version="2.3.7" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup> </ItemGroup>
<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

@@ -1,4 +1,5 @@
using Dapper; using Dapper;
using MySqlConnector;
using System.Collections.Concurrent; using System.Collections.Concurrent;
namespace WeaponPaints namespace WeaponPaints
@@ -14,70 +15,112 @@ namespace WeaponPaints
_config = config; _config = config;
} }
internal async Task GetKnifeFromDatabase(PlayerInfo player) internal async Task GetPlayerData(PlayerInfo? player)
{ {
if (!_config.Additional.KnifeEnabled) return;
try try
{ {
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? playerKnife = await connection.QueryFirstOrDefaultAsync<string>(query, new { steamid = player.SteamId }); if (_config.Additional.KnifeEnabled)
GetKnifeFromDatabase(player, connection);
if (_config.Additional.GloveEnabled)
GetGloveFromDatabase(player, connection);
if (_config.Additional.AgentEnabled)
GetAgentFromDatabase(player, connection);
if (_config.Additional.MusicEnabled)
GetMusicFromDatabase(player, connection);
if (_config.Additional.SkinEnabled)
GetWeaponPaintsFromDatabase(player, connection);
}
catch (Exception ex)
{
// Log the exception or handle it appropriately
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
private void GetKnifeFromDatabase(PlayerInfo? player, MySqlConnection connection)
{
try
{
if (!_config.Additional.KnifeEnabled || string.IsNullOrEmpty(player?.SteamId))
return;
const string query = "SELECT `knife` FROM `wp_player_knife` WHERE `steamid` = @steamid";
var playerKnife = connection.QueryFirstOrDefault<string>(query, new { steamid = player.SteamId });
if (!string.IsNullOrEmpty(playerKnife)) if (!string.IsNullOrEmpty(playerKnife))
{ {
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) private void GetGloveFromDatabase(PlayerInfo? player, MySqlConnection connection)
{ {
if (!_config.Additional.GloveEnabled) return;
try try
{ {
// Ensure proper disposal of resources using "using" statement if (!_config.Additional.GloveEnabled || string.IsNullOrEmpty(player?.SteamId))
await using var connection = await _database.GetConnectionAsync(); return;
// Construct the SQL query with specific columns for better performance const string query = "SELECT `weapon_defindex` FROM `wp_player_gloves` WHERE `steamid` = @steamid";
string query = "SELECT `weapon_defindex` FROM `wp_player_gloves` WHERE `steamid` = @steamid"; var gloveData = connection.QueryFirstOrDefault<ushort?>(query, new { steamid = player.SteamId });
// Execute the query and retrieve glove data
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) private void GetAgentFromDatabase(PlayerInfo? player, MySqlConnection connection)
{ {
if (!_config.Additional.SkinEnabled || player == null || player.SteamId == null) return;
try try
{ {
await using var connection = await _database.GetConnectionAsync(); if (!_config.Additional.AgentEnabled || string.IsNullOrEmpty(player?.SteamId))
string query = "SELECT * FROM `wp_player_skins` WHERE `steamid` = @steamid"; return;
var playerSkins = await connection.QueryAsync<dynamic>(query, new { steamid = player.SteamId });
if (playerSkins == null) return; const string query = "SELECT `agent_ct`, `agent_t` FROM `wp_player_agents` WHERE `steamid` = @steamid";
var agentData = connection.QueryFirstOrDefault<(string, string)>(query, new { steamid = player.SteamId });
if (agentData == default) return;
var agentCT = agentData.Item1;
var agentT = agentData.Item2;
if (!string.IsNullOrEmpty(agentCT) || !string.IsNullOrEmpty(agentT))
{
WeaponPaints.g_playersAgent[player.Slot] = (
agentCT,
agentT
);
}
}
catch (Exception ex)
{
Utility.Log($"An error occurred in GetAgentFromDatabase: {ex.Message}");
}
}
private void GetWeaponPaintsFromDatabase(PlayerInfo? player, MySqlConnection connection)
{
try
{
if (!_config.Additional.SkinEnabled || player == null || string.IsNullOrEmpty(player.SteamId))
return;
var weaponInfos = new ConcurrentDictionary<int, WeaponInfo>(); var weaponInfos = new ConcurrentDictionary<int, WeaponInfo>();
const string query = "SELECT * FROM `wp_player_skins` WHERE `steamid` = @steamid";
var playerSkins = connection.Query<dynamic>(query, new { steamid = player.SteamId });
foreach (var row in playerSkins) foreach (var row in playerSkins)
{ {
int weaponDefIndex = row?.weapon_defindex ?? 0; int weaponDefIndex = row?.weapon_defindex ?? 0;
@@ -97,20 +140,44 @@ 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}");
} }
} }
private void GetMusicFromDatabase(PlayerInfo? player, MySqlConnection connection)
{
try
{
if (!_config.Additional.MusicEnabled || string.IsNullOrEmpty(player?.SteamId))
return;
const string query = "SELECT `music_id` FROM `wp_player_music` WHERE `steamid` = @steamid";
var musicData = connection.QueryFirstOrDefault<ushort?>(query, new { steamid = player.SteamId });
if (musicData != null)
{
WeaponPaints.g_playersMusic[player.Slot] = musicData.Value;
}
}
catch (Exception ex)
{
Utility.Log($"An error occurred in GetMusicFromDatabase: {ex.Message}");
}
}
internal async Task SyncKnifeToDatabase(PlayerInfo player, string knife) internal async Task SyncKnifeToDatabase(PlayerInfo player, string knife)
{ {
if (!_config.Additional.KnifeEnabled || player == null || string.IsNullOrEmpty(player.SteamId) || string.IsNullOrEmpty(knife)) return; if (!_config.Additional.KnifeEnabled || string.IsNullOrEmpty(player.SteamId) || string.IsNullOrEmpty(knife)) return;
const string query = "INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(@steamid, @newKnife) ON DUPLICATE KEY UPDATE `knife` = @newKnife";
try try
{ {
await using var connection = await _database.GetConnectionAsync(); await using var connection = await _database.GetConnectionAsync();
string query = "INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(@steamid, @newKnife) ON DUPLICATE KEY UPDATE `knife` = @newKnife";
await connection.ExecuteAsync(query, new { steamid = player.SteamId, newKnife = knife }); await connection.ExecuteAsync(query, new { steamid = player.SteamId, newKnife = knife });
} }
catch (Exception e) catch (Exception e)
@@ -119,15 +186,14 @@ 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 || string.IsNullOrEmpty(player.SteamId)) return;
try try
{ {
await using var connection = await _database.GetConnectionAsync(); await using var connection = await _database.GetConnectionAsync();
string query = "INSERT INTO `wp_player_gloves` (`steamid`, `weapon_defindex`) VALUES(@steamid, @weapon_defindex) ON DUPLICATE KEY UPDATE `weapon_defindex` = @weapon_defindex"; const string query = "INSERT INTO `wp_player_gloves` (`steamid`, `weapon_defindex`) VALUES(@steamid, @weapon_defindex) ON DUPLICATE KEY UPDATE `weapon_defindex` = @weapon_defindex";
await connection.ExecuteAsync(query, new { steamid = player.SteamId, weapon_defindex = defindex }); await connection.ExecuteAsync(query, new { steamid = player.SteamId, weapon_defindex = defindex });
} }
catch (Exception e) catch (Exception e)
@@ -136,29 +202,63 @@ namespace WeaponPaints
} }
} }
internal async Task SyncAgentToDatabase(PlayerInfo player)
{
if (!_config.Additional.AgentEnabled || string.IsNullOrEmpty(player.SteamId)) return;
const string query = """
INSERT INTO `wp_player_agents` (`steamid`, `agent_ct`, `agent_t`)
VALUES(@steamid, @agent_ct, @agent_t)
ON DUPLICATE KEY UPDATE
`agent_ct` = @agent_ct,
`agent_t` = @agent_t
""";
try
{
await using var connection = await _database.GetConnectionAsync();
await connection.ExecuteAsync(query, new { steamid = player.SteamId, agent_ct = WeaponPaints.g_playersAgent[player.Slot].CT, agent_t = WeaponPaints.g_playersAgent[player.Slot].T });
}
catch (Exception e)
{
Utility.Log($"Error syncing agents to database: {e.Message}");
}
}
internal async Task SyncWeaponPaintsToDatabase(PlayerInfo player) internal async Task SyncWeaponPaintsToDatabase(PlayerInfo player)
{ {
if (player == null || string.IsNullOrEmpty(player.SteamId) || !WeaponPaints.gPlayerWeaponsInfo.TryGetValue(player.Slot, out var weaponsInfo)) if (string.IsNullOrEmpty(player.SteamId) || !WeaponPaints.gPlayerWeaponsInfo.TryGetValue(player.Slot, out var weaponsInfo))
return; return;
try try
{ {
await using var connection = await _database.GetConnectionAsync(); await using var connection = await _database.GetConnectionAsync();
foreach (var weaponInfoPair in weaponsInfo) foreach (var (weaponDefIndex, weaponInfo) in weaponsInfo)
{ {
int weaponDefIndex = weaponInfoPair.Key; var paintId = weaponInfo.Paint;
WeaponInfo weaponInfo = weaponInfoPair.Value; var wear = weaponInfo.Wear;
var seed = weaponInfo.Seed;
int paintId = weaponInfo.Paint; const string queryCheckExistence = "SELECT COUNT(*) FROM `wp_player_skins` WHERE `steamid` = @steamid AND `weapon_defindex` = @weaponDefIndex";
float wear = weaponInfo.Wear;
int seed = weaponInfo.Seed;
string query = "INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed`) " + var existingRecordCount = await connection.ExecuteScalarAsync<int>(queryCheckExistence, new { steamid = player.SteamId, weaponDefIndex = weaponDefIndex });
"VALUES (@steamid, @weaponDefIndex, @paintId, @wear, @seed) " +
"ON DUPLICATE KEY UPDATE `weapon_paint_id` = @paintId, `weapon_wear` = @wear, `weapon_seed` = @seed"; 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 = 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 = weaponDefIndex, paintId, wear, seed };
}
var parameters = new { steamid = player.SteamId, weaponDefIndex, paintId, wear, seed };
await connection.ExecuteAsync(query, parameters); await connection.ExecuteAsync(query, parameters);
} }
} }
@@ -168,5 +268,20 @@ namespace WeaponPaints
} }
} }
internal async Task SyncMusicToDatabase(PlayerInfo player, ushort music)
{
if (!_config.Additional.MusicEnabled || string.IsNullOrEmpty(player.SteamId)) return;
try
{
await using var connection = await _database.GetConnectionAsync();
const string query = "INSERT INTO `wp_player_music` (`steamid`, `music_id`) VALUES(@steamid, @newMusic) ON DUPLICATE KEY UPDATE `music_id` = @newMusic";
await connection.ExecuteAsync(query, new { steamid = player.SteamId, newMusic = music });
}
catch (Exception e)
{
Utility.Log($"Error syncing music kit 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": "40 57 48 83 EC 20 48 8B F9 41 B0 01",
"linux": "55 48 89 E5 41 57 41 56 41 55 49 89 F5 41 54 49 89 FC 53 48 81 EC A8 00 00 00"
}
},
"CAttributeList_SetOrAddAttributeValueByName": {
"signatures": {
"library": "server",
"windows": "40 53 41 56 41 57 48 81 EC 90 00 00 00 0F 29 74 24 70",
"linux": "55 48 89 E5 41 57 41 56 49 89 FE 41 55 41 54 49 89 F4 53 48 83 EC 78"
}
},
"CBaseModelEntity_SetBodygroup": {
"signatures": {
"library": "server",
"windows": "48 89 5C 24 08 48 89 74 24 10 57 48 83 EC 20 41 8B F8 48 8B F2 48 8B D9 E8 ? ? ? ?",
"linux": "55 48 89 E5 41 56 49 89 F6 41 55 41 89 D5 41 54 49 89 FC 48 83 EC 08"
}
}
}

View File

@@ -4,6 +4,8 @@
"wp_info_refresh": "Type {lime}!wp{default} to synchronize chosen skins", "wp_info_refresh": "Type {lime}!wp{default} to synchronize chosen skins",
"wp_info_knife": "Type {lime}!knife{default} to open knife menu", "wp_info_knife": "Type {lime}!knife{default} to open knife menu",
"wp_info_glove": "Type {lime}!gloves{default} to open gloves menu", "wp_info_glove": "Type {lime}!gloves{default} to open gloves menu",
"wp_info_agent": "Type {lime}!agents{default} to open agents menu",
"wp_info_music": "Type {lime}!music{default} to open music menu",
"wp_command_cooldown": "{lightred}You can't refresh weapon paints right now", "wp_command_cooldown": "{lightred}You can't refresh weapon paints right now",
"wp_command_refresh_done": "{lime}Refreshing weapon paints", "wp_command_refresh_done": "{lime}Refreshing weapon paints",
"wp_knife_menu_select": "You have chosen {lime}{0}{default} as your knife", "wp_knife_menu_select": "You have chosen {lime}{0}{default} as your knife",
@@ -11,7 +13,13 @@
"wp_knife_menu_title": "Knife Menu", "wp_knife_menu_title": "Knife Menu",
"wp_glove_menu_select": "You have chosen {lime}{0}{default} as your glove", "wp_glove_menu_select": "You have chosen {lime}{0}{default} as your glove",
"wp_glove_menu_title": "Gloves Menu", "wp_glove_menu_title": "Gloves Menu",
"wp_agent_menu_select": "You have chosen {lime}{0}{default} as your agent",
"wp_agent_menu_title": "Agents Menu",
"wp_music_menu_title": "Music Menu",
"wp_music_menu_select": "You have chosen {lime}{0}{default} as your music kit",
"wp_skin_menu_weapon_title": "Weapon Menu", "wp_skin_menu_weapon_title": "Weapon Menu",
"wp_skin_menu_skin_title": "Select skin for {lime}{0}{default}", "wp_skin_menu_skin_title": "Select skin for {lime}{0}{default}",
"wp_skin_menu_select": "You have chosen {lime}{0}{default} as your skin" "wp_skin_menu_select": "You have chosen {lime}{0}{default} as your skin",
"None": "None"
} }

View File

@@ -1,17 +1,25 @@
{ {
"wp_prefix": "{lightblue}[WeaponPaints] {default}", "wp_prefix": "{lightblue}[WeaponPaints] {default}",
"wp_info_website": "Apmeklējiet {lime}{0}{default}, kur varat mainīt ādas", "wp_info_website": "Apmeklē {lime}{0}{default}, kur varat mainīt ādas",
"wp_info_refresh": "Ievadiet {lime}!wp{default}, lai sinhronizētu izvēlētās ādas", "wp_info_refresh": "Ievadiet {lime}!wp{default}, lai sinhronizētu izvēlētās ādas",
"wp_info_knife": "Ievadiet {lime}!knife{default}, lai atvērtu nazis izvēlni", "wp_info_knife": "Ievadiet {lime}!knife{default}, lai atvērtu nazis izvēlni",
"wp_info_glove": "Ievadiet {lime}!gloves{default}, lai atvērtu cimdi izvēlni", "wp_info_glove": "Ievadiet {lime}!gloves{default}, lai atvērtu cimdi izvēlni",
"wp_command_cooldown": "{lightred}Šobrīd jūs nevarat atjaunot ieroču ādas", "wp_info_agent": "Ievadiet {lime}!agents{default}, lai atvērtu aģentu izvēlni",
"wp_command_refresh_done": "{lime}Atjauno ieroču ādas", "wp_info_music": "Ievadiet {lime}!music{default}, lai atvērtu mūzikas izvēlni",
"wp_command_cooldown": "{lightred}Šobrīd nevarat atsvaidzināt ieroča krāsas",
"wp_command_refresh_done": "{lime}Atsvaidzinot ieroča krāsas",
"wp_knife_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu nazi", "wp_knife_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu nazi",
"wp_knife_menu_kill": "", "wp_knife_menu_kill": "",
"wp_knife_menu_title": "Nazis Izvēlne", "wp_knife_menu_title": "Nazi Izvēlne",
"wp_glove_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savus cimdus", "wp_glove_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu cimdu",
"wp_glove_menu_title": "Cimdu Izvēlne", "wp_glove_menu_title": "Cimdu Izvēlne",
"wp_agent_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu aģentu",
"wp_agent_menu_title": "Aģentu Izvēlne",
"wp_music_menu_title": "Mūzikas Izvēlne",
"wp_music_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu mūzikas komplektu",
"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 priekš {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",
}
"None": "Nav"
}

View File

@@ -3,15 +3,23 @@
"wp_info_website": "Odwiedź {lime}{0}{default}, gdzie możesz zmieniać skórki", "wp_info_website": "Odwiedź {lime}{0}{default}, gdzie możesz zmieniać skórki",
"wp_info_refresh": "Wpisz {lime}!wp{default}, aby zsynchronizować wybrane skórki", "wp_info_refresh": "Wpisz {lime}!wp{default}, aby zsynchronizować wybrane skórki",
"wp_info_knife": "Wpisz {lime}!knife{default}, aby otworzyć menu noży", "wp_info_knife": "Wpisz {lime}!knife{default}, aby otworzyć menu noży",
"wp_info_glove": "Wpisz {lime}!gloves{default}, aby otworzyć menu rękawiczek", "wp_info_glove": "Wpisz {lime}!gloves{default}, aby otworzyć menu rękawic",
"wp_command_cooldown": "{lightred}Nie możesz teraz odświeżyć skórek broni", "wp_info_agent": "Wpisz {lime}!agents{default}, aby otworzyć menu agentów",
"wp_command_refresh_done": "{lime}Odświeżanie skórek broni", "wp_info_music": "Wpisz {lime}!music{default}, aby otworzyć menu muzyczne",
"wp_command_cooldown": "{lightred}Nie możesz teraz odświeżyć kolorów broni",
"wp_command_refresh_done": "{lime}Odświeżanie kolorów broni",
"wp_knife_menu_select": "Wybrałeś {lime}{0}{default} jako swój nóż", "wp_knife_menu_select": "Wybrałeś {lime}{0}{default} jako swój nóż",
"wp_knife_menu_kill": "{lime}Wybrane skiny będą ustawione dopiero po ponownym wejściu na serwer lub wpisaniu komendy {orange}!kill", "wp_knife_menu_kill": "",
"wp_knife_menu_title": "Menu Noży", "wp_knife_menu_title": "Menu Noży",
"wp_glove_menu_select": "Wybrałeś {lime}{0}{default} jako swoje rękawiczki", "wp_glove_menu_select": "Wybrałeś {lime}{0}{default} jako swoją rękawiczkę",
"wp_glove_menu_title": "Menu Rękawiczek", "wp_glove_menu_title": "Menu Rękawiczek",
"wp_agent_menu_select": "Wybrałeś {lime}{0}{default} jako swojego agenta",
"wp_agent_menu_title": "Menu Agentów",
"wp_music_menu_title": "Menu Muzyczne",
"wp_music_menu_select": "Wybrałeś {lime}{0}{default} jako swój zestaw muzyczny",
"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ę",
}
"None": "Brak"
}

View File

@@ -1,17 +1,25 @@
{ {
"wp_prefix": "{lightblue}[WeaponPaints] {default}", "wp_prefix": "{lightblue}[WeaponPaints] {default}",
"wp_info_website": "Visite {lime}{0}{default}, onde você pode alterar skins", "wp_info_website": "Visite {lime}{0}{default}, onde você pode alterar as skins",
"wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins selecionadas", "wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins escolhidas",
"wp_info_knife": "Digite {lime}!knife{default} para abrir o menu de facas", "wp_info_knife": "Digite {lime}!knife{default} para abrir o menu de facas",
"wp_info_glove": "Digite {lime}!gloves{default} para abrir o menu de luvas", "wp_info_glove": "Digite {lime}!gloves{default} para abrir o menu de luvas",
"wp_command_cooldown": "{lightred}Você não pode atualizar as skins de arma agora", "wp_info_agent": "Digite {lime}!agents{default} para abrir o menu de agentes",
"wp_command_refresh_done": "{lime}Atualizando as skins de arma", "wp_info_music": "Digite {lime}!music{default} para abrir o menu de música",
"wp_command_cooldown": "{lightred}Você não pode atualizar as skins de armas agora",
"wp_command_refresh_done": "{lime}Atualizando as skins de armas",
"wp_knife_menu_select": "Você escolheu {lime}{0}{default} como sua faca", "wp_knife_menu_select": "Você escolheu {lime}{0}{default} como sua faca",
"wp_knife_menu_kill": "", "wp_knife_menu_kill": "",
"wp_knife_menu_title": "Menu de Facas", "wp_knife_menu_title": "Menu de Facas",
"wp_glove_menu_select": "Você escolheu {lime}{0}{default} como suas luvas", "wp_glove_menu_select": "Você escolheu {lime}{0}{default} como sua luva",
"wp_glove_menu_title": "Menu de Luvas", "wp_glove_menu_title": "Menu de Luvas",
"wp_agent_menu_select": "Você escolheu {lime}{0}{default} como seu agente",
"wp_agent_menu_title": "Menu de Agentes",
"wp_music_menu_title": "Menu de Música",
"wp_music_menu_select": "Você escolheu {lime}{0}{default} como seu kit de música",
"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 a 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",
}
"None": "Nenhum"
}

View File

@@ -1,17 +1,25 @@
{ {
"wp_prefix": "{lightblue}[WeaponPaints] {default}", "wp_prefix": "{lightblue}[WeaponPaints] {default}",
"wp_info_website": "Visite {lime}{0}{default}, onde pode alterar skins", "wp_info_website": "Visite {lime}{0}{default}, onde pode alterar as skins",
"wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins selecionadas", "wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins escolhidas",
"wp_info_knife": "Digite {lime}!knife{default} para abrir o menu de facas", "wp_info_knife": "Digite {lime}!knife{default} para abrir o menu de facas",
"wp_info_glove": "Digite {lime}!gloves{default} para abrir o menu de luvas", "wp_info_glove": "Digite {lime}!gloves{default} para abrir o menu de luvas",
"wp_command_cooldown": "{lightred}Você não pode atualizar as skins de arma agora", "wp_info_agent": "Digite {lime}!agents{default} para abrir o menu de agentes",
"wp_command_refresh_done": "{lime}Atualizando as skins de arma", "wp_info_music": "Digite {lime}!music{default} para abrir o menu de música",
"wp_knife_menu_select": "Você escolheu {lime}{0}{default} como sua faca", "wp_command_cooldown": "{lightred}Não pode atualizar as skins de armas de momento",
"wp_command_refresh_done": "{lime}Atualizando as skins de armas",
"wp_knife_menu_select": "Escolheu {lime}{0}{default} como a sua faca",
"wp_knife_menu_kill": "", "wp_knife_menu_kill": "",
"wp_knife_menu_title": "Menu de Facas", "wp_knife_menu_title": "Menu de Facas",
"wp_glove_menu_select": "Você escolheu {lime}{0}{default} como suas luvas", "wp_glove_menu_select": "Escolheu {lime}{0}{default} como a sua luva",
"wp_glove_menu_title": "Menu de Luvas", "wp_glove_menu_title": "Menu de Luvas",
"wp_agent_menu_select": "Escolheu {lime}{0}{default} como o seu agente",
"wp_agent_menu_title": "Menu de Agentes",
"wp_music_menu_title": "Menu de Música",
"wp_music_menu_select": "Escolheu {lime}{0}{default} como o seu kit de música",
"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 a skin para {lime}{0}{default}",
"wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin" "wp_skin_menu_select": "Escolheu {lime}{0}{default} como a sua skin",
}
"None": "Nenhum"
}

View File

@@ -4,14 +4,22 @@
"wp_info_refresh": "Введите {lime}!wp{default}, чтобы синхронизировать выбранные скины", "wp_info_refresh": "Введите {lime}!wp{default}, чтобы синхронизировать выбранные скины",
"wp_info_knife": "Введите {lime}!knife{default}, чтобы открыть меню ножей", "wp_info_knife": "Введите {lime}!knife{default}, чтобы открыть меню ножей",
"wp_info_glove": "Введите {lime}!gloves{default}, чтобы открыть меню перчаток", "wp_info_glove": "Введите {lime}!gloves{default}, чтобы открыть меню перчаток",
"wp_command_cooldown": "{lightred}Вы не можете обновить скины оружия сейчас", "wp_info_agent": "Введите {lime}!agents{default}, чтобы открыть меню агентов",
"wp_command_refresh_done": "{lime}Обновление скинов оружия", "wp_info_music": "Введите {lime}!music{default}, чтобы открыть меню музыки",
"wp_command_cooldown": "{lightred}Вы не можете обновить раскраску оружия сейчас",
"wp_command_refresh_done": "{lime}Обновление раскраски оружия",
"wp_knife_menu_select": "Вы выбрали {lime}{0}{default} в качестве вашего ножа", "wp_knife_menu_select": "Вы выбрали {lime}{0}{default} в качестве вашего ножа",
"wp_knife_menu_kill": "", "wp_knife_menu_kill": "",
"wp_knife_menu_title": "Меню Ножей", "wp_knife_menu_title": "Меню Ножей",
"wp_glove_menu_select": "Вы выбрали {lime}{0}{default} в качестве ваших перчаток", "wp_glove_menu_select": "Вы выбрали {lime}{0}{default} в качестве ваших перчаток",
"wp_glove_menu_title": "Меню Перчаток", "wp_glove_menu_title": "Меню Перчаток",
"wp_agent_menu_select": "Вы выбрали {lime}{0}{default} в качестве вашего агента",
"wp_agent_menu_title": "Меню Агентов",
"wp_music_menu_title": "Меню Музыки",
"wp_music_menu_select": "Вы выбрали {lime}{0}{default} в качестве вашего музыкального набора",
"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} в качестве вашего скина",
"None": "Нет"
} }

View File

@@ -1,17 +1,25 @@
{ {
"wp_prefix": "{lightblue}[WeaponPaints] {default}", "wp_prefix": "{lightblue}[WeaponPaints] {default}",
"wp_info_website": "Ziyaret edin {lime}{0}{default}, burada skinleri değiştirebilirsiniz", "wp_info_website": "Ziyaret edin {lime}{0}{default}, nerede derileri değiştirebilirsiniz",
"wp_info_refresh": "Senkronize edilen skinleri görmek için {lime}!wp{default} yazın", "wp_info_refresh": "Senkronize etmek için {lime}!wp{default} yazın seçilen deriler",
"wp_info_knife": "Bıçak menüsünü açmak için {lime}!knife{default} yazın", "wp_info_knife": "Bıçak menüsünü açmak için {lime}!knife{default} yazın",
"wp_info_glove": "Eldiven menüsünü açmak için {lime}!gloves{default} yazın", "wp_info_glove": "Handskar menüsünü açmak için {lime}!gloves{default} yazın",
"wp_command_cooldown": "{lightred}Şu anda silah skinlerini yenileyemezsiniz", "wp_info_agent": "Ajan menüsünü açmak için {lime}!agents{default} yazın",
"wp_command_refresh_done": "{lime}Silah skinleri yenileniyor", "wp_info_music": "Müzik menüsünü açmak için {lime}!music{default} yazın",
"wp_command_cooldown": "{lightred}Şu anda silah boyalarını yenileyemezsiniz",
"wp_command_refresh_done": "{lime}Silah boyaları yenileniyor",
"wp_knife_menu_select": "{lime}{0}{default} olarak bıçağınızı seçtiniz", "wp_knife_menu_select": "{lime}{0}{default} olarak bıçağınızı seçtiniz",
"wp_knife_menu_kill": "", "wp_knife_menu_kill": "",
"wp_knife_menu_title": "Bıçak Menüsü", "wp_knife_menu_title": "Bıçak Menüsü",
"wp_glove_menu_select": "{lime}{0}{default} olarak eldiveninizi seçtiniz", "wp_glove_menu_select": "{lime}{0}{default} olarak eldiveninizi seçtiniz",
"wp_glove_menu_title": "Eldiven Menüsü", "wp_glove_menu_title": "Eldiven Menüsü",
"wp_agent_menu_select": "{lime}{0}{default} olarak ajanınızı seçtiniz",
"wp_agent_menu_title": "Ajanlar Menüsü",
"wp_music_menu_title": "Müzik Menüsü",
"wp_music_menu_select": "{lime}{0}{default} olarak müzik setinizi seçtiniz",
"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 cilt seçin",
"wp_skin_menu_select": "{lime}{0}{default} olarak bir skin seçtiniz" "wp_skin_menu_select": "{lime}{0}{default} olarak cildinizi seçtiniz",
}
"None": "Hiçbiri"
}

View File

@@ -4,14 +4,22 @@
"wp_info_refresh": "Введіть {lime}!wp{default}, щоб синхронізувати обрані шкури", "wp_info_refresh": "Введіть {lime}!wp{default}, щоб синхронізувати обрані шкури",
"wp_info_knife": "Введіть {lime}!knife{default}, щоб відкрити меню ножів", "wp_info_knife": "Введіть {lime}!knife{default}, щоб відкрити меню ножів",
"wp_info_glove": "Введіть {lime}!gloves{default}, щоб відкрити меню рукавичок", "wp_info_glove": "Введіть {lime}!gloves{default}, щоб відкрити меню рукавичок",
"wp_command_cooldown": "{lightred}Наразі ви не можете оновлювати шкіри зброї", "wp_info_agent": "Введіть {lime}!agents{default}, щоб відкрити меню агентів",
"wp_command_refresh_done": "{lime}Оновлення шкірок зброї", "wp_info_music": "Введіть {lime}!music{default}, щоб відкрити меню музики",
"wp_knife_menu_select": "Ви вибрали {lime}{0}{default} як ваш ніж", "wp_command_cooldown": "{lightred}Ви не можете оновити фарби зброї зараз",
"wp_command_refresh_done": "{lime}Оновлення фарби зброї",
"wp_knife_menu_select": "Ви обрали {lime}{0}{default} як свій ніж",
"wp_knife_menu_kill": "", "wp_knife_menu_kill": "",
"wp_knife_menu_title": "Меню Ножів", "wp_knife_menu_title": "Меню Ножів",
"wp_glove_menu_select": "Ви вибрали {lime}{0}{default} як ваші рукавички", "wp_glove_menu_select": "Ви обрали {lime}{0}{default} як свої рукавички",
"wp_glove_menu_title": "Меню Рукавичок", "wp_glove_menu_title": "Меню Рукавичок",
"wp_agent_menu_select": "Ви обрали {lime}{0}{default} як свого агента",
"wp_agent_menu_title": "Меню Агентів",
"wp_music_menu_title": "Меню Музики",
"wp_music_menu_select": "Ви обрали {lime}{0}{default} як свій набір музики",
"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} як свою шкіру",
"None": "Немає"
} }

View File

@@ -1,17 +1,25 @@
{ {
"wp_prefix": "{lightblue}[WeaponPaints] {default}", "wp_prefix": "{lightblue}[WeaponPaints] {default}",
"wp_info_website": "访问 {lime}{0}{default},您可以更改皮肤", "wp_info_website": "访问 {lime}{0}{default},您可以更改皮肤",
"wp_info_refresh": "输入 {lime}!wp{default} 同步选择的皮肤", "wp_info_refresh": "输入 {lime}!wp{default} 同步选择的皮肤",
"wp_info_knife": "输入 {lime}!knife{default} 打开刀具菜单", "wp_info_knife": "输入 {lime}!knife{default} 打开刀具菜单",
"wp_info_glove": "输入 {lime}!gloves{default} 打开手套菜单", "wp_info_glove": "输入 {lime}!gloves{default} 打开手套菜单",
"wp_command_cooldown": "{lightred}您现在无法刷新武器皮肤", "wp_info_agent": "输入 {lime}!agents{default} 打开代理菜单",
"wp_command_refresh_done": "{lime}刷新武器皮肤", "wp_info_music": "输入 {lime}!music{default} 打开音乐菜单",
"wp_knife_menu_select": "您已选择 {lime}{0}{default} 作为您的刀具", "wp_command_cooldown": "{lightred}您现在无法刷新武器涂装",
"wp_command_refresh_done": "{lime}正在刷新武器涂装",
"wp_knife_menu_select": "您选择了 {lime}{0}{default} 作为您的刀具",
"wp_knife_menu_kill": "", "wp_knife_menu_kill": "",
"wp_knife_menu_title": "刀具菜单", "wp_knife_menu_title": "刀具菜单",
"wp_glove_menu_select": "您选择 {lime}{0}{default} 作为您的手套", "wp_glove_menu_select": "您选择 {lime}{0}{default} 作为您的手套",
"wp_glove_menu_title": "手套菜单", "wp_glove_menu_title": "手套菜单",
"wp_agent_menu_select": "您选择了 {lime}{0}{default} 作为您的代理",
"wp_agent_menu_title": "代理菜单",
"wp_music_menu_title": "音乐菜单",
"wp_music_menu_select": "您选择了 {lime}{0}{default} 作为您的音乐包",
"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} 作为您的皮肤",
}
"None": "无"
}

392
website/data/agents.json Normal file
View File

@@ -0,0 +1,392 @@
[
{
"team": 2,
"image": "",
"model": "null",
"agent_name": "Agent | Default"
},
{
"team": 3,
"image": "",
"model": "null",
"agent_name": "Agent | Default"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4619.png",
"model": "ctm_st6/ctm_st6_variantj",
"agent_name": "'Blueberries' Buckshot | NSWC SEAL"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4680.png",
"model": "ctm_st6/ctm_st6_variantl",
"agent_name": "'Two Times' McCoy | TACP Cavalry"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4711.png",
"model": "ctm_swat/ctm_swat_variante",
"agent_name": "Cmdr. Mae 'Dead Cold' Jamison | SWAT"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4712.png",
"model": "ctm_swat/ctm_swat_variantf",
"agent_name": "1st Lieutenant Farlow | SWAT"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4713.png",
"model": "ctm_swat/ctm_swat_variantg",
"agent_name": "John 'Van Healen' Kask | SWAT"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4714.png",
"model": "ctm_swat/ctm_swat_varianth",
"agent_name": "Bio-Haz Specialist | SWAT"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4715.png",
"model": "ctm_swat/ctm_swat_varianti",
"agent_name": "Sergeant Bombson | SWAT"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4716.png",
"model": "ctm_swat/ctm_swat_variantj",
"agent_name": "Chem-Haz Specialist | SWAT"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4718.png",
"model": "tm_balkan/tm_balkan_variantk",
"agent_name": "Rezan the Redshirt | Sabre"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4726.png",
"model": "tm_professional/tm_professional_varf",
"agent_name": "Sir Bloody Miami Darryl | The Professionals"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4727.png",
"model": "tm_professional/tm_professional_varg",
"agent_name": "Safecracker Voltzmann | The Professionals"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4728.png",
"model": "tm_professional/tm_professional_varh",
"agent_name": "Little Kev | The Professionals"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4730.png",
"model": "tm_professional/tm_professional_varj",
"agent_name": "Getaway Sally | The Professionals"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4732.png",
"model": "tm_professional/tm_professional_vari",
"agent_name": "Number K | The Professionals"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4733.png",
"model": "tm_professional/tm_professional_varf1",
"agent_name": "Sir Bloody Silent Darryl | The Professionals"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4734.png",
"model": "tm_professional/tm_professional_varf2",
"agent_name": "Sir Bloody Skullhead Darryl | The Professionals"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4735.png",
"model": "tm_professional/tm_professional_varf3",
"agent_name": "Sir Bloody Darryl Royale | The Professionals"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4736.png",
"model": "tm_professional/tm_professional_varf4",
"agent_name": "Sir Bloody Loudmouth Darryl | The Professionals"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4613.png",
"model": "tm_professional/tm_professional_varf5",
"agent_name": "Bloody Darryl The Strapped | The Professionals"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4749.png",
"model": "ctm_gendarmerie/ctm_gendarmerie_varianta",
"agent_name": "Sous-Lieutenant Medic | Gendarmerie Nationale"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4750.png",
"model": "ctm_gendarmerie/ctm_gendarmerie_variantb",
"agent_name": "Chem-Haz Capitaine | Gendarmerie Nationale"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4751.png",
"model": "ctm_gendarmerie/ctm_gendarmerie_variantc",
"agent_name": "Chef d'Escadron Rouchard | Gendarmerie Nationale"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4752.png",
"model": "ctm_gendarmerie/ctm_gendarmerie_variantd",
"agent_name": "Aspirant | Gendarmerie Nationale"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4753.png",
"model": "ctm_gendarmerie/ctm_gendarmerie_variante",
"agent_name": "Officer Jacques Beltram | Gendarmerie Nationale"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4756.png",
"model": "ctm_swat/ctm_swat_variantk",
"agent_name": "Lieutenant 'Tree Hugger' Farlow | SWAT"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4757.png",
"model": "ctm_diver/ctm_diver_varianta",
"agent_name": "Cmdr. Davida 'Goggles' Fernandez | SEAL Frogman"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4771.png",
"model": "ctm_diver/ctm_diver_variantb",
"agent_name": "Cmdr. Frank 'Wet Sox' Baroud | SEAL Frogman"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4772.png",
"model": "ctm_diver/ctm_diver_variantc",
"agent_name": "Lieutenant Rex Krikey | SEAL Frogman"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4773.png",
"model": "tm_jungle_raider/tm_jungle_raider_varianta",
"agent_name": "Elite Trapper Solman | Guerrilla Warfare"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4774.png",
"model": "tm_jungle_raider/tm_jungle_raider_variantb",
"agent_name": "Crasswater The Forgotten | Guerrilla Warfare"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4775.png",
"model": "tm_jungle_raider/tm_jungle_raider_variantc",
"agent_name": "Arno The Overgrown | Guerrilla Warfare"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4776.png",
"model": "tm_jungle_raider/tm_jungle_raider_variantd",
"agent_name": "Col. Mangos Dabisi | Guerrilla Warfare"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4777.png",
"model": "tm_jungle_raider/tm_jungle_raider_variante",
"agent_name": "Vypa Sista of the Revolution | Guerrilla Warfare"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4778.png",
"model": "tm_jungle_raider/tm_jungle_raider_variantf",
"agent_name": "Trapper Aggressor | Guerrilla Warfare"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4780.png",
"model": "tm_jungle_raider/tm_jungle_raider_variantb2",
"agent_name": "'Medium Rare' Crasswater | Guerrilla Warfare"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-4781.png",
"model": "tm_jungle_raider/tm_jungle_raider_variantf2",
"agent_name": "Trapper | Guerrilla Warfare"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5105.png",
"model": "tm_leet/tm_leet_variantg",
"agent_name": "Ground Rebel | Elite Crew"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5106.png",
"model": "tm_leet/tm_leet_varianth",
"agent_name": "Osiris | Elite Crew"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5107.png",
"model": "tm_leet/tm_leet_varianti",
"agent_name": "Prof. Shahmat | Elite Crew"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5108.png",
"model": "tm_leet/tm_leet_variantf",
"agent_name": "The Elite Mr. Muhlik | Elite Crew"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5109.png",
"model": "tm_leet/tm_leet_variantj",
"agent_name": "Jungle Rebel | Elite Crew"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5205.png",
"model": "tm_phoenix/tm_phoenix_varianth",
"agent_name": "Soldier | Phoenix"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5206.png",
"model": "tm_phoenix/tm_phoenix_variantf",
"agent_name": "Enforcer | Phoenix"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5207.png",
"model": "tm_phoenix/tm_phoenix_variantg",
"agent_name": "Slingshot | Phoenix"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5208.png",
"model": "tm_phoenix/tm_phoenix_varianti",
"agent_name": "Street Soldier | Phoenix"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5305.png",
"model": "ctm_fbi/ctm_fbi_variantf",
"agent_name": "Operator | FBI SWAT"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5306.png",
"model": "ctm_fbi/ctm_fbi_variantg",
"agent_name": "Markus Delrow | FBI HRT"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5307.png",
"model": "ctm_fbi/ctm_fbi_varianth",
"agent_name": "Michael Syfers | FBI Sniper"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5308.png",
"model": "ctm_fbi/ctm_fbi_variantb",
"agent_name": "Special Agent Ava | FBI"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5400.png",
"model": "ctm_st6/ctm_st6_variantk",
"agent_name": "3rd Commando Company | KSK"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5401.png",
"model": "ctm_st6/ctm_st6_variante",
"agent_name": "Seal Team 6 Soldier | NSWC SEAL"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5402.png",
"model": "ctm_st6/ctm_st6_variantg",
"agent_name": "Buckshot | NSWC SEAL"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5403.png",
"model": "ctm_st6/ctm_st6_variantm",
"agent_name": "'Two Times' McCoy | USAF TACP"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5404.png",
"model": "ctm_st6/ctm_st6_varianti",
"agent_name": "Lt. Commander Ricksaw | NSWC SEAL"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5405.png",
"model": "ctm_st6/ctm_st6_variantn",
"agent_name": "Primeiro Tenente | Brazilian 1st Battalion"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5500.png",
"model": "tm_balkan/tm_balkan_variantf",
"agent_name": "Dragomir | Sabre"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5501.png",
"model": "tm_balkan/tm_balkan_varianti",
"agent_name": "Maximus | Sabre"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5502.png",
"model": "tm_balkan/tm_balkan_variantg",
"agent_name": "Rezan The Ready | Sabre"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5503.png",
"model": "tm_balkan/tm_balkan_variantj",
"agent_name": "Blackwolf | Sabre"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5504.png",
"model": "tm_balkan/tm_balkan_varianth",
"agent_name": "'The Doctor' Romanov | Sabre"
},
{
"team": 2,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5505.png",
"model": "tm_balkan/tm_balkan_variantl",
"agent_name": "Dragomir | Sabre Footsoldier"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5601.png",
"model": "ctm_sas/ctm_sas_variantf",
"agent_name": "B Squadron Officer | SAS"
},
{
"team": 3,
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/agent-5602.png",
"model": "ctm_sas/ctm_sas_variantg",
"agent_name": "D Squadron Officer | NZSAS"
}
]

File diff suppressed because one or more lines are too long

367
website/data/music.json Normal file
View File

@@ -0,0 +1,367 @@
[
{
"id": "3",
"name": "Music Kit | Daniel Sadowski, Crimson Assault",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-3.png"
},
{
"id": "4",
"name": "Music Kit | Noisia, Sharpened",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-4.png"
},
{
"id": "5",
"name": "Music Kit | Robert Allaire, Insurgency",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-5.png"
},
{
"id": "6",
"name": "Music Kit | Sean Murray, A*D*8",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-6.png"
},
{
"id": "7",
"name": "Music Kit | Feed Me, High Noon",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-7.png"
},
{
"id": "8",
"name": "Music Kit | Dren, Death's Head Demolition",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-8.png"
},
{
"id": "9",
"name": "Music Kit | Austin Wintory, Desert Fire",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-9.png"
},
{
"id": "10",
"name": "Music Kit | Sasha, LNOE",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-10.png"
},
{
"id": "11",
"name": "Music Kit | Skog, Metal",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-11.png"
},
{
"id": "12",
"name": "Music Kit | Midnight Riders, All I Want for Christmas",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-12.png"
},
{
"id": "13",
"name": "Music Kit | Matt Lange, IsoRhythm",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-13.png"
},
{
"id": "14",
"name": "Music Kit | Mateo Messina, For No Mankind",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-14.png"
},
{
"id": "15",
"name": "Music Kit | Various Artists, Hotline Miami",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-15.png"
},
{
"id": "16",
"name": "Music Kit | Daniel Sadowski, Total Domination",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-16.png"
},
{
"id": "17",
"name": "Music Kit | Damjan Mravunac, The Talos Principle",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-17.png"
},
{
"id": "18",
"name": "Music Kit | Proxy, Battlepack",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-18.png"
},
{
"id": "19",
"name": "Music Kit | Ki:Theory, MOLOTOV",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-19.png"
},
{
"id": "20",
"name": "Music Kit | Troels Folmann, Uber Blasto Phone",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-20.png"
},
{
"id": "21",
"name": "Music Kit | Kelly Bailey, Hazardous Environments",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-21.png"
},
{
"id": "22",
"name": "Music Kit | Skog, II-Headshot",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-22.png"
},
{
"id": "23",
"name": "Music Kit | Daniel Sadowski, The 8-Bit Kit",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-23.png"
},
{
"id": "24",
"name": "Music Kit | AWOLNATION, I Am",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-24.png"
},
{
"id": "25",
"name": "Music Kit | Mord Fustang, Diamonds",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-25.png"
},
{
"id": "26",
"name": "Music Kit | Michael Bross, Invasion!",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-26.png"
},
{
"id": "27",
"name": "Music Kit | Ian Hultquist, Lion's Mouth",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-27.png"
},
{
"id": "28",
"name": "Music Kit | New Beat Fund, Sponge Fingerz",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-28.png"
},
{
"id": "29",
"name": "Music Kit | Beartooth, Disgusting",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-29.png"
},
{
"id": "30",
"name": "Music Kit | Lennie Moore, Java Havana Funkaloo",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-30.png"
},
{
"id": "31",
"name": "Music Kit | Darude, Moments CS:GO",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-31.png"
},
{
"id": "32",
"name": "Music Kit | Beartooth, Aggressive",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-32.png"
},
{
"id": "33",
"name": "Music Kit | Blitz Kids, The Good Youth",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-33.png"
},
{
"id": "34",
"name": "Music Kit | Hundredth, FREE",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-34.png"
},
{
"id": "35",
"name": "Music Kit | Neck Deep, Life's Not Out To Get You",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-35.png"
},
{
"id": "36",
"name": "Music Kit | Roam, Backbone",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-36.png"
},
{
"id": "37",
"name": "Music Kit | Twin Atlantic, GLA",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-37.png"
},
{
"id": "38",
"name": "Music Kit | Skog, III-Arena",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-38.png"
},
{
"id": "39",
"name": "Music Kit | The Verkkars, EZ4ENCE",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-39.png"
},
{
"id": "40",
"name": "Halo, The Master Chief Collection",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-40.png"
},
{
"id": "41",
"name": "Music Kit | Scarlxrd: King, Scar",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-41.png"
},
{
"id": "42",
"name": "Half-Life: Alyx, Anti-Citizen",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-42.png"
},
{
"id": "43",
"name": "Music Kit | Austin Wintory, Bachram",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-43.png"
},
{
"id": "44",
"name": "Music Kit | Dren, Gunman Taco Truck",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-44.png"
},
{
"id": "45",
"name": "Music Kit | Daniel Sadowski, Eye of the Dragon",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-45.png"
},
{
"id": "46",
"name": "Music Kit | Tree Adams and Ben Bromfield, M.U.D.D. FORCE",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-46.png"
},
{
"id": "47",
"name": "Music Kit | Tim Huling, Neo Noir",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-47.png"
},
{
"id": "48",
"name": "Music Kit | Sam Marshall, Bodacious",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-48.png"
},
{
"id": "49",
"name": "Music Kit | Matt Levine, Drifter",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-49.png"
},
{
"id": "50",
"name": "Music Kit | Amon Tobin, All for Dust",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-50.png"
},
{
"id": "51",
"name": "Darren Korb, Hades Music Kit",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-51.png"
},
{
"id": "52",
"name": "Music Kit | Neck Deep, The Lowlife Pack",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-52.png"
},
{
"id": "53",
"name": "Music Kit | Scarlxrd, CHAIN$AW.LXADXUT.",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-53.png"
},
{
"id": "54",
"name": "Music Kit | Austin Wintory, Mocha Petal",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-54.png"
},
{
"id": "55",
"name": "Music Kit | Chipzel, ~Yellow Magic~",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-55.png"
},
{
"id": "56",
"name": "Music Kit | Freaky DNA, Vici",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-56.png"
},
{
"id": "57",
"name": "Music Kit | Jesse Harlin, Astro Bellum",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-57.png"
},
{
"id": "58",
"name": "Music Kit | Laura Shigihara: Work Hard, Play Hard",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-58.png"
},
{
"id": "59",
"name": "Music Kit | Sarah Schachner, KOLIBRI",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-59.png"
},
{
"id": "60",
"name": "Music Kit | bbno$, u mad!",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-60.png"
},
{
"id": "61",
"name": "Music Kit | The Verkkars & n0thing, Flashbang Dance",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-61.png"
},
{
"id": "62",
"name": "Music Kit | 3kliksphilip, Heading for the Source",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-62.png"
},
{
"id": "63",
"name": "Music Kit | Humanity's Last Breath, Void",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-63.png"
},
{
"id": "64",
"name": "Music Kit | Juelz, Shooters",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-64.png"
},
{
"id": "65",
"name": "Music Kit | Knock2, dashstar*",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-65.png"
},
{
"id": "66",
"name": "Music Kit | Meechy Darko, Gothic Luxury",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-66.png"
},
{
"id": "67",
"name": "Music Kit | Sullivan King, Lock Me Up",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-67.png"
},
{
"id": "68",
"name": "Music Kit | Perfect World, 花脸 Hua Lian (Painted Face)",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-68.png"
},
{
"id": "69",
"name": "Music Kit | Denzel Curry, ULTIMATE",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-69.png"
},
{
"id": "71",
"name": "Music Kit | DRYDEN, Feel The Power",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-71.png"
},
{
"id": "72",
"name": "Music Kit | ISOxo, inhuman",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-72.png"
},
{
"id": "73",
"name": "Music Kit | KILL SCRIPT, All Night",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-73.png"
},
{
"id": "74",
"name": "Music Kit | Knock2, Make U SWEAT!",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-74.png"
},
{
"id": "75",
"name": "Music Kit | Rad Cat, Reason",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-75.png"
},
{
"id": "76",
"name": "Music Kit | TWERL, Ekko & Sidetrack, Under Bright Lights",
"image": "https://raw.githubusercontent.com/daffyyyy/cs2-WeaponPaints/main/website/img/skins/music_kit-76.png"
}
]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Some files were not shown because too many files have changed in this diff Show More