Compare commits

..

3 Commits

Author SHA1 Message Date
Nereziel
2abe48f71a 123 2024-03-02 19:15:50 +01:00
Nereziel
219c201fde testref 2024-02-24 22:44:17 +01:00
Nereziel
acf4a766ca Update Commands.cs 2024-02-24 21:15:45 +01:00
261 changed files with 13359 additions and 2786 deletions

14
.github/FUNDING.yml vendored
View File

@@ -1,14 +0,0 @@
# 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,10 +62,6 @@ 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,4 +2,3 @@
bin/ bin/
obj/ obj/
website/getskins.php website/getskins.php
.idea/

File diff suppressed because it is too large Load Diff

View File

@@ -5,18 +5,15 @@ 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;
@@ -29,15 +26,9 @@ 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";
@@ -56,13 +47,16 @@ 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; } = 6; public override int Version { get; set; } = 4;
[JsonPropertyName("DatabaseHost")] [JsonPropertyName("DatabaseHost")]
public string DatabaseHost { get; set; } = ""; public string DatabaseHost { get; set; } = "";
@@ -89,6 +83,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(); public Additional Additional { get; set; } = new Additional();
} }
} }

View File

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

360
Events.cs
View File

@@ -1,10 +1,6 @@
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
{ {
@@ -15,47 +11,36 @@ namespace WeaponPaints
{ {
CCSPlayerController? player = @event.Userid; CCSPlayerController? player = @event.Userid;
if (player is null || !player.IsValid || player.IsBot || if (player is null || !player.IsValid || player.IsBot || player.IsHLTV || player.SteamID.ToString().Length != 17 ||
weaponSync == null || _database == null) return HookResult.Continue; weaponSync == null || _database == null) return HookResult.Continue;
PlayerInfo playerInfo = new PlayerInfo PlayerInfo playerInfo = new(
{ (int)player.Index,
UserId = player.UserId, player.Slot,
Slot = player.Slot, player.UserId,
Index = (int)player.Index, player.SteamID.ToString(),
SteamId = player.SteamID.ToString(), player.PlayerName,
Name = player.PlayerName, player.IpAddress?.Split(":")[0]
IpAddress = player.IpAddress?.Split(":")[0] );
};
try try
{ {
_ = Task.Run(async () => await weaponSync.GetPlayerData(playerInfo));
/*
if (Config.Additional.SkinEnabled) if (Config.Additional.SkinEnabled)
{ {
_ = Task.Run(async () => await weaponSync.GetWeaponPaintsFromDatabase(playerInfo)); Task.Run(() => weaponSync.GetWeaponPaintsFromDatabase(playerInfo));
} }
if (Config.Additional.KnifeEnabled) if (Config.Additional.KnifeEnabled)
{ {
_ = Task.Run(async () => await weaponSync.GetKnifeFromDatabase(playerInfo)); Task.Run(() => weaponSync.GetKnifeFromDatabase(playerInfo));
} }
if (Config.Additional.GloveEnabled) if (Config.Additional.GloveEnabled)
{ {
_ = Task.Run(async () => await weaponSync.GetGloveFromDatabase(playerInfo)); Task.Run(() => 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 catch (Exception ex)
{ {
Console.WriteLine($"An error occurred: {ex.Message}");
} }
return HookResult.Continue; return HookResult.Continue;
@@ -64,61 +49,154 @@ 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) return HookResult.Continue; if (player is null || !player.IsValid || player.IsBot ||
player.IsHLTV || player.SteamID.ToString().Length != 17) return HookResult.Continue;
if (Config.Additional.SkinEnabled) PlayerInfo playerInfo = new(
(int)player.Index,
player.Slot,
player.UserId,
player.SteamID.ToString(),
player.PlayerName,
player.IpAddress?.Split(":")[0]
);
if (weaponSync != null)
{ {
gPlayerWeaponsInfo.TryRemove(player.Slot, out _); // Run weapon sync tasks asynchronously
} Task.Run(async () =>
if (Config.Additional.KnifeEnabled) {
{ await weaponSync.SyncWeaponPaintsToDatabase(playerInfo);
g_playersKnife.TryRemove(player.Slot, out _); });
}
if (Config.Additional.GloveEnabled) // Remove player data
{ if (Config.Additional.SkinEnabled)
g_playersGlove.TryRemove(player.Slot, out _); {
} gPlayerWeaponsInfo.TryRemove(player.Slot, out _);
if (Config.Additional.AgentEnabled) }
{ if (Config.Additional.KnifeEnabled)
g_playersAgent.TryRemove(player.Slot, out _); {
} g_playersKnife.TryRemove(player.Slot, out _);
if (Config.Additional.MusicEnabled) }
{ if (Config.Additional.GloveEnabled)
g_playersMusic.TryRemove(player.Slot, out _); {
g_playersGlove.TryRemove(player.Slot, out _);
}
} }
// Remove player's command cooldown
commandsCooldown.Remove(player.Slot); commandsCooldown.Remove(player.Slot);
return HookResult.Continue; return HookResult.Continue;
} }
private void OnEntityCreated(CEntityInstance entity)
{
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 is { KnifeEnabled: false, SkinEnabled: false, GloveEnabled: false }) return; if (!Config.Additional.KnifeEnabled && !Config.Additional.SkinEnabled && !Config.Additional.GloveEnabled) 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 is { KnifeEnabled: false, GloveEnabled: false }) if (player is null || !player.IsValid || !Config.Additional.KnifeEnabled && !Config.Additional.GloveEnabled)
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;
GivePlayerMusicKit(player); if (!PlayerHasKnife(player))
GivePlayerAgent(player); GiveKnifeToPlayer(player);
GivePlayerGloves(player);
Server.NextFrame(() =>
{
RefreshGloves(player);
});
return HookResult.Continue; return HookResult.Continue;
} }
@@ -132,120 +210,100 @@ 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 { IsValid: true, PlayerPawn.IsValid: true } && p is not null && p.IsValid &&
(LifeState_t)p.LifeState == LifeState_t.LIFE_ALIVE (LifeState_t)p.LifeState == LifeState_t.LIFE_ALIVE && p.SteamID.ToString().Length == 17
&& !p.IsBot && p is { Connected: PlayerConnectedState.PlayerConnected } && !p.IsBot && !p.IsHLTV && p.Connected == PlayerConnectedState.PlayerConnected && p.Team != CounterStrikeSharp.API.Modules.Utils.CsTeam.None
) )
) )
{ {
if (PlayerWeaponImage.TryGetValue(player.Slot, out var value) && !string.IsNullOrEmpty(value)) try
{
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); RegisterEventHandler<EventRoundStart>(OnRoundStart, HookMode.Pre);
RegisterEventHandler<EventRoundEnd>(OnRoundEnd); RegisterEventHandler<EventRoundEnd>(OnRoundEnd);
RegisterListener<Listeners.OnEntityCreated>(OnEntityCreated);
if (Config.Additional.ShowSkinImage) //RegisterEventHandler<EventItemPickup>(OnItemPickup);
RegisterListener<Listeners.OnTick>(OnTick);
if (!IsWindows) HookEntityOutput("weapon_knife", "OnPlayerPickup", OnPickup);
VirtualFunctions.GiveNamedItemFunc.Hook(OnGiveNamedItemPost, HookMode.Post);
} }
} }
} }

View File

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

View File

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

View File

@@ -9,14 +9,13 @@ 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, knives, gloves and agents - Changes only paint, seed and wear on weapons and knives
- 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
- Added command **`!gloves`** to show menu with gloves - Knife change is now limited to have these cvars empty **`mp_t_default_melee ""`** and **`mp_ct_default_melee ""`**
- 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
@@ -25,7 +24,6 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
- Run server with plugin, **it will generate config if installed correctly!** - Run server with plugin, **it will generate config if installed correctly!**
- Edit `addons/counterstrikesharp/configs/`**`plugins/WeaponPaints/WeaponPaints.json`** include database credentials - Edit `addons/counterstrikesharp/configs/`**`plugins/WeaponPaints/WeaponPaints.json`** include database credentials
- In `addons/counterstrikesharp/configs/`**`core.json`** set **FollowCS2ServerGuidelines** to **`false`** - In `addons/counterstrikesharp/configs/`**`core.json`** set **FollowCS2ServerGuidelines** to **`false`**
- Copy from plugins folder gamedata file **`weaponpaints.json`** to folder **`addons/counterstrikesharp/gamedata/`**
## Plugin Configuration ## Plugin Configuration
<details> <details>
@@ -54,6 +52,7 @@ 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
@@ -72,7 +71,7 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
</details> </details>
## Web install ## Web install
- Requires PHP >= 7.4 with curl and pdo_mysql ***(Tested on php ver **`8.2.3`** and nginx webserver)*** - Requires PHP >= 7.4 ***(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)
@@ -84,6 +83,11 @@ 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:**
@@ -92,6 +96,11 @@ 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

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

View File

@@ -1,9 +1,11 @@
using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Core.Translations; using CounterStrikeSharp.API.Modules.Utils;
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
{ {
@@ -11,6 +13,22 @@ 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;
@@ -23,47 +41,35 @@ namespace WeaponPaints
try try
{ {
string[] createTableQueries = string[] createTableQueries = new[]
[ {
""" @"CREATE TABLE IF NOT EXISTS `wp_player_skins` (
CREATE TABLE IF NOT EXISTS `wp_player_skins` ( `steamid` varchar(64) NOT NULL,
`steamid` varchar(18) NOT NULL, `weapon_defindex` int(6) NOT NULL,
`weapon_defindex` int(6) NOT NULL, `weapon_paint_id` int(6) NOT NULL,
`weapon_paint_id` int(6) NOT NULL, `weapon_wear` float NOT NULL DEFAULT 0.000001,
`weapon_wear` float NOT NULL DEFAULT 0.000001, `weapon_seed` int(16) NOT NULL DEFAULT 0
`weapon_seed` int(16) NOT NULL DEFAULT 0 ) ENGINE=InnoDB",
) ENGINE=InnoDB
""",
@"CREATE TABLE IF NOT EXISTS `wp_player_knife` ( @"CREATE TABLE IF NOT EXISTS `wp_player_knife` (
`steamid` varchar(18) NOT NULL, `steamid` varchar(64) NOT NULL,
`knife` varchar(64) NOT NULL, `knife` varchar(64) NOT NULL,
UNIQUE (`steamid`) UNIQUE (`steamid`)
) ENGINE = InnoDB", ) ENGINE = InnoDB",
""" @"CREATE TABLE IF NOT EXISTS `wp_player_gloves` (
CREATE TABLE IF NOT EXISTS `wp_player_gloves` ( `steamid` varchar(64) NOT NULL,
`steamid` varchar(18) NOT NULL, `weapon_defindex` int(11) NOT NULL,
`weapon_defindex` int(11) NOT NULL, UNIQUE (`steamid`)
UNIQUE (`steamid`) ) ENGINE=InnoDB"
) ENGINE=InnoDB };
""", /*string[] createTableQueries = new[]
""" {
CREATE TABLE IF NOT EXISTS `wp_player_agents` ( @"CREATE TABLE IF NOT EXISTS `wp_players` (`steamid` BIGINT UNSIGNED NOT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
`steamid` varchar(18) NOT NULL, @"CREATE TABLE IF NOT EXISTS `wp_players_skins` (`steamid` BIGINT UNSIGNED NOT NULL, `team` SMALLINT UNSIGNED NOT NULL, `weapon` SMALLINT UNSIGNED NOT NULL, `paint` SMALLINT UNSIGNED NOT NULL, `wear` FLOAT NOT NULL DEFAULT 0.00001, `seed` SMALLINT UNSIGNED NOT NULL DEFAULT 0, `nametag` VARCHAR(20) DEFAULT NULL, `stattrack` INT UNSIGNED NOT NULL DEFAULT 0, `stattrack_enabled` SMALLINT NOT NULL DEFAULT 0, PRIMARY KEY (`steamid`,`weapon`,`team`), FOREIGN KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
`agent_ct` varchar(64) DEFAULT NULL, @"CREATE TABLE IF NOT EXISTS `wp_players_knife` (`steamid` BIGINT UNSIGNED NOT NULL, `knife_t` SMALLINT UNSIGNED NOT NULL, `knife_ct` SMALLINT UNSIGNED NOT NULL, PRIMARY KEY (`steamid`), FOREIGN KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
`agent_t` varchar(64) DEFAULT NULL, @"CREATE TABLE IF NOT EXISTS `wp_players_music` (`steamid` BIGINT UNSIGNED NOT NULL, `music` SMALLINT UNSIGNED DEFAULT NULL, PRIMARY KEY (`steamid`), FOREIGN KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
UNIQUE (`steamid`) @"CREATE TABLE IF NOT EXISTS `wp_players_gloves` (`steamid` BIGINT UNSIGNED NOT NULL, `glove_t` SMALLINT UNSIGNED NOT NULL, `glove_ct` SMALLINT UNSIGNED NOT NULL, PRIMARY KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"
) ENGINE=InnoDB };*/
""", foreach (var query in createTableQueries)
"""
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)
{ {
await connection.ExecuteAsync(query, transaction: transaction); await connection.ExecuteAsync(query, transaction: transaction);
} }
@@ -84,64 +90,37 @@ namespace WeaponPaints
internal static bool IsPlayerValid(CCSPlayerController? player) internal static bool IsPlayerValid(CCSPlayerController? player)
{ {
if (player is null || WeaponPaints.weaponSync is null) return false; if (player is null) return false;
return player is { IsValid: true, IsBot: false, IsHLTV: false, UserId: not null }; return (player is not null && player.IsValid && !player.IsBot && !player.IsHLTV && player.UserId.HasValue
&& WeaponPaints.weaponSync != null && player.Connected == PlayerConnectedState.PlayerConnected && player.SteamID.ToString().Length == 17);
} }
internal static void LoadSkinsFromFile(string filePath, ILogger logger) internal static void LoadSkinsFromFile(string filePath)
{
var json = File.ReadAllText(filePath);
try
{
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
WeaponPaints.skinsList = deserializedSkins ?? [];
}
catch (FileNotFoundException)
{
logger?.LogError("Not found \"skins.json\" file");
}
}
internal static void LoadGlovesFromFile(string filePath, ILogger logger)
{ {
try try
{ {
var json = File.ReadAllText(filePath); string json = File.ReadAllText(filePath);
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json); var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
WeaponPaints.glovesList = deserializedSkins ?? []; WeaponPaints.skinsList = deserializedSkins ?? new List<JObject>();
} }
catch (FileNotFoundException) catch (FileNotFoundException)
{ {
logger?.LogError("Not found \"gloves.json\" file"); throw;
} }
} }
internal static void LoadAgentsFromFile(string filePath, ILogger logger) internal static void LoadGlovesFromFile(string filePath)
{ {
try try
{ {
var json = File.ReadAllText(filePath); string json = File.ReadAllText(filePath);
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json); var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
WeaponPaints.agentsList = deserializedSkins ?? []; WeaponPaints.glovesList = deserializedSkins ?? new List<JObject>();
} }
catch (FileNotFoundException) catch (FileNotFoundException)
{ {
logger?.LogError("Not found \"agents.json\" file"); throw;
}
}
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");
} }
} }
@@ -155,49 +134,68 @@ namespace WeaponPaints
internal static string ReplaceTags(string message) internal static string ReplaceTags(string message)
{ {
return message.ReplaceColorTags(); if (message.Contains('{'))
{
string modifiedValue = message;
if (Config != null)
{
modifiedValue = modifiedValue.Replace("{WEBSITE}", Config.Website);
}
foreach (FieldInfo field in typeof(ChatColors).GetFields())
{
string pattern = $"{{{field.Name}}}";
if (message.Contains(pattern, StringComparison.OrdinalIgnoreCase))
{
modifiedValue = modifiedValue.Replace(pattern, field.GetValue(null)!.ToString(), StringComparison.OrdinalIgnoreCase);
}
}
return modifiedValue;
}
return message;
} }
internal static async Task CheckVersion(string version, ILogger logger) internal static async Task CheckVersion(string version, ILogger logger)
{ {
using HttpClient client = new(); using (HttpClient client = new HttpClient())
try
{ {
var response = await client.GetAsync("https://raw.githubusercontent.com/Nereziel/cs2-WeaponPaints/main/VERSION").ConfigureAwait(false); try
if (response.IsSuccessStatusCode)
{ {
var remoteVersion = await response.Content.ReadAsStringAsync().ConfigureAwait(false); HttpResponseMessage response = await client.GetAsync("https://raw.githubusercontent.com/Nereziel/cs2-WeaponPaints/main/VERSION").ConfigureAwait(false);
remoteVersion = remoteVersion.Trim();
var comparisonResult = string.CompareOrdinal(version, remoteVersion); if (response.IsSuccessStatusCode)
switch (comparisonResult)
{ {
case < 0: string remoteVersion = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
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; }
case > 0: else if (comparisonResult > 0)
{
logger.LogInformation("Probably dev version detected"); logger.LogInformation("Probably dev version detected");
break; }
default: else
{
logger.LogInformation("Plugin is up to date"); logger.LogInformation("Plugin is up to date");
break; }
}
else
{
logger.LogWarning("Failed to check version");
} }
} }
else catch (HttpRequestException ex)
{ {
logger.LogWarning("Failed to check version"); logger.LogError(ex, "Failed to connect to the version server.");
}
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.");
} }
} }
@@ -215,5 +213,26 @@ 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 @@
2.5a 1.9b

View File

@@ -1,6 +1,5 @@
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;
@@ -11,69 +10,82 @@ namespace WeaponPaints
{ {
public partial class WeaponPaints public partial class WeaponPaints
{ {
private void GivePlayerWeaponSkin(CCSPlayerController player, CBasePlayerWeapon weapon) internal static void ChangeWeaponAttributes(CBasePlayerWeapon? weapon, CCSPlayerController? player, bool isKnife = false)
{ {
if (!Config.Additional.SkinEnabled) return; if (player is null || weapon is null || !weapon.IsValid || !Utility.IsPlayerValid(player)) return;
if (!gPlayerWeaponsInfo.TryGetValue(player.Slot, out _)) return; if (!gPlayerWeaponsInfo.TryGetValue(player.Slot, out _)) 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[] 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 }; int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
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.01f; weapon.FallbackWear = 0.000001f;
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;
if (isKnife) return; var foundSkin = skinsList.FirstOrDefault(skin =>
UpdatePlayerWeaponMeshGroupMask(player, weapon, !newPaints.Contains(fallbackPaintKit)); ((int?)skin?["weapon_defindex"] ?? 0) == weaponDefIndex &&
((int?)skin?["paint"] ?? 0) == fallbackPaintKit &&
skin?["paint_name"] != null
);
string skinName = foundSkin?["paint_name"]?.ToString() ?? "";
if (!string.IsNullOrEmpty(skinName))
new SchemaString<CEconItemView>(weapon.AttributeManager.Item, "m_szCustomName").Set(skinName);
if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null)
{
var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode);
int[] newPaints = { 1171, 1170, 1169, 1164, 1162, 1161, 1159, 1175, 1174, 1167, 1165, 1168, 1163, 1160, 1166, 1173 };
if (newPaints.Contains(fallbackPaintKit))
{
skeleton.ModelState.MeshGroupMask = 1;
}
else
{
if (skeleton.ModelState.MeshGroupMask != 2)
{
skeleton.ModelState.MeshGroupMask = 2;
}
}
}
var viewModels = GetPlayerViewModels(player);
if (viewModels == null || viewModels.Length == 0)
return;
var viewModel = viewModels[0];
if (viewModel == null || viewModel.Value == null || viewModel.Value.Weapon == null || viewModel.Value.Weapon.Value == null)
return;
Utilities.SetStateChanged(viewModel.Value, "CBaseEntity", "m_CBodyComponent");
return; return;
} }
if (!gPlayerWeaponsInfo[player.Slot].TryGetValue(weaponDefIndex, out var value) || value.Paint == 0) return; if (!gPlayerWeaponsInfo[player.Slot].ContainsKey(weaponDefIndex)) 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;
@@ -81,55 +93,113 @@ 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;
if (isKnife) return; var foundSkin1 = skinsList.FirstOrDefault(skin =>
UpdatePlayerWeaponMeshGroupMask(player, weapon, !newPaints.Contains(fallbackPaintKit)); ((int?)skin?["weapon_defindex"] ?? 0) == weaponDefIndex &&
((int?)skin?["paint"] ?? 0) == fallbackPaintKit &&
skin?["paint_name"] != null
);
var skinName1 = foundSkin1?["paint_name"]?.ToString() ?? "";
if (!string.IsNullOrEmpty(skinName1))
new SchemaString<CEconItemView>(weapon.AttributeManager.Item, "m_szCustomName").Set(skinName1);
if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null)
{
var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode);
int[] newPaints = { 1171, 1170, 1169, 1164, 1162, 1161, 1159, 1175, 1174, 1167, 1165, 1168, 1163, 1160, 1166, 1173 };
if (newPaints.Contains(fallbackPaintKit))
{
skeleton.ModelState.MeshGroupMask = 1;
}
else
{
if (skeleton.ModelState.MeshGroupMask != 2)
{
skeleton.ModelState.MeshGroupMask = 2;
}
}
}
var viewModels1 = GetPlayerViewModels(player);
if (viewModels1 == null || viewModels1.Length == 0)
return;
var viewModel1 = viewModels1[0];
if (viewModel1 == null || viewModel1.Value == null || viewModel1.Value.Weapon == null || viewModel1.Value.Weapon.Value == null)
return;
Utilities.SetStateChanged(viewModel1.Value, "CBaseEntity", "m_CBodyComponent");
} }
private static void GiveKnifeToPlayer(CCSPlayerController? player) internal static void GiveKnifeToPlayer(CCSPlayerController? player)
{ {
if (!_config.Additional.KnifeEnabled || player == null || !player.IsValid) return; if (!_config.Additional.KnifeEnabled || player == null || !player.IsValid) return;
if (PlayerHasKnife(player)) return; WeaponPaints.Instance.AddTimer(1.0f, () =>
{
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();
//string knifeToGive = (CsTeam)player.TeamNum == CsTeam.Terrorist ? "weapon_knife_t" : "weapon_knife"; if (knifeTypes.Count == 0)
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);
});
} }
private static bool PlayerHasKnife(CCSPlayerController? player) internal static bool PlayerHasKnife(CCSPlayerController? player)
{ {
if (!_config.Additional.KnifeEnabled) return false; if (!_config.Additional.KnifeEnabled) return false;
if (player == null || !player.IsValid || !player.PlayerPawn.IsValid) if (player == null || !player.IsValid || player.PlayerPawn == null || !player.PlayerPawn.IsValid)
{ {
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.IsValid || weapon.Value == null || !weapon.Value.IsValid) continue; if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid)
if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet"))
{ {
return true; if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet"))
{
return true;
}
} }
} }
return false; return false;
} }
private void RefreshWeapons(CCSPlayerController? player) internal void RefreshWeapons(CCSPlayerController? player)
{ {
if (!g_bCommandsAllowed) return;
if (player == null || !player.IsValid || player.PlayerPawn?.Value == null || (LifeState_t)player.LifeState != LifeState_t.LIFE_ALIVE) if (player == null || !player.IsValid || player.PlayerPawn?.Value == null || (LifeState_t)player.LifeState != LifeState_t.LIFE_ALIVE)
return; return;
if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null)
@@ -137,55 +207,56 @@ namespace WeaponPaints
var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons; var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons;
if (weapons.Count == 0) if (weapons == null || weapons.Count == 0)
return; return;
if (player.Team is CsTeam.None or CsTeam.Spectator) if (player.Team == CsTeam.None || player.Team == CsTeam.Spectator)
return; return;
int playerTeam = player.TeamNum; //Dictionary<string, (int, int)> weaponsWithAmmo = new Dictionary<string, (int, int)>();
Dictionary<string, 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.IsValid || weapon.Value == null || if (weapon == null || !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 var weaponByDefindex)) if (!WeaponDefindex.TryGetValue(weapon.Value.AttributeManager.Item.ItemDefinitionIndex, out 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.TryGetValue(weaponByDefindex, out var value)) if (!weaponsWithAmmo.ContainsKey(weaponByDefindex))
{ {
value = []; weaponsWithAmmo.Add(weaponByDefindex, new List<(int, int)>());
weaponsWithAmmo.Add(weaponByDefindex, value);
} }
value.Add((clip1, reservedAmmo)); weaponsWithAmmo[weaponByDefindex].Add((clip1, reservedAmmo));
if (gun.VData == null) return;
weapon.Value.Remove();
} }
//player.RemoveItemByDesignerName(weapon.Value.DesignerName, false);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -194,80 +265,106 @@ namespace WeaponPaints
} }
} }
try for (int i = 1; i <= 3; i++)
{ {
player.ExecuteClientCommand("slot 3"); player.ExecuteClientCommand($"slot {i}");
player.ExecuteClientCommand("slot 3"); player.ExecuteClientCommand($"slot {i}");
var weapon = player.PlayerPawn.Value.WeaponServices.ActiveWeapon; AddTimer(0.1f, () =>
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)
{ {
CCSWeaponBaseGun gun; var weapon = player.PlayerPawn.Value.WeaponServices.ActiveWeapon.Value;
CCSWeaponBaseGun? gun = weapon?.As<CCSWeaponBaseGun>();
AddTimer(0.3f, () => if (gun == null || gun.VData == null) return;
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 (player.TeamNum != playerTeam) return; if (gun != null && gun.IsValid && gun.State == CSWeaponState_t.WEAPON_NOT_CARRIED)
player.ExecuteClientCommand("slot 3");
gun = weapon.Value.As<CCSWeaponBaseGun>();
player.DropActiveWeapon();
AddTimer(0.7f, () =>
{ {
if (player.TeamNum != playerTeam) return; weapon?.Remove();
}
if (!gun.IsValid || gun.State != CSWeaponState_t.WEAPON_NOT_CARRIED) return;
gun.Remove();
});
GiveKnifeToPlayer(player);
}); });
} });
} }
catch (Exception ex)
AddTimer(1.2f, () =>
{ {
Logger.LogWarning($"Cannot remove knife: {ex.Message}"); GiveKnifeToPlayer(player);
}
AddTimer(0.6f, () => foreach (var entry in weaponsWithAmmo)
{
foreach (var ammo in entry.Value)
{ {
if (!g_bCommandsAllowed) return; var newWeapon = new CBasePlayerWeapon(player.GiveNamedItem(entry.Key));
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
{ {
newWeapon.Clip1 = ammo.Item1; if (newWeapon != null)
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); }, TimerFlags.STOP_ON_MAPCHANGE);
} }
private void GivePlayerGloves(CCSPlayerController player) 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)
{
Logger.LogWarning($"Cannot remove knife: {ex.Message}");
}
}
}
}
}
private static void RefreshGloves(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) if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE)
return; return;
var model = pawn.CBodyComponent?.SceneNode?.GetSkeletonInstance()?.ModelState.ModelName ?? string.Empty; string 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");
@@ -276,30 +373,35 @@ namespace WeaponPaints
Instance.AddTimer(0.06f, () => Instance.AddTimer(0.06f, () =>
{ {
CEconItemView item = pawn.EconGloves;
try try
{ {
if (!player.IsValid) if (player == null || !player.IsValid)
return; return;
if (!player.PawnIsAlive) if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE)
return; return;
if (!g_playersGlove.TryGetValue(player.Slot, out var gloveInfo) || gloveInfo == 0) return; if (g_playersGlove.TryGetValue(player.Slot, out var gloveInfo) && gloveInfo != 0)
{
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];
item.ItemDefinitionIndex = gloveInfo; CEconItemView item = pawn.EconGloves;
item.ItemIDLow = 16384 & 0xFFFFFFFF; item.ItemDefinitionIndex = gloveInfo;
item.ItemIDHigh = 16384; item.ItemIDLow = 16384 & 0xFFFFFFFF;
item.ItemIDHigh = 16384;
CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture prefab", weaponInfo.Paint); CAttributeList_SetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture prefab", weaponInfo.Paint);
CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture seed", weaponInfo.Seed); CAttributeList_SetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture seed", weaponInfo.Seed);
CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture wear", weaponInfo.Wear); CAttributeList_SetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture wear", weaponInfo.Wear);
item.Initialized = true; item.Initialized = true;
SetBodygroup(pawn.Handle, "default_gloves", 1); CBaseModelEntity_SetBodygroup.Invoke(pawn, "default_gloves", 1);
}
} }
catch (Exception) { } catch (Exception) { }
}, TimerFlags.STOP_ON_MAPCHANGE); }, TimerFlags.STOP_ON_MAPCHANGE);
@@ -307,7 +409,7 @@ namespace WeaponPaints
private static int GetRandomPaint(int defindex) private static int GetRandomPaint(int defindex)
{ {
if (skinsList.Count == 0) if (skinsList == null || skinsList.Count == 0)
return 0; return 0;
Random rnd = new Random(); Random rnd = new Random();
@@ -320,127 +422,32 @@ namespace WeaponPaints
var randomWeapon = filteredWeapons[rnd.Next(filteredWeapons.Count)]; var randomWeapon = filteredWeapons[rnd.Next(filteredWeapons.Count)];
return int.TryParse(randomWeapon["paint"]?.ToString(), out var paintValue) ? paintValue : 0; if (int.TryParse(randomWeapon["paint"]?.ToString(), out int paintValue))
return paintValue;
return 0;
} }
private static void SubclassChange(CBasePlayerWeapon weapon, ushort itemD) private static CSkeletonInstance GetSkeletonInstance(CGameSceneNode node)
{ {
var subclassChangeFunc = VirtualFunction.Create<nint, string, int>( Func<nint, nint> GetSkeletonInstance = VirtualFunction.Create<nint, nint>(node.Handle, 8);
GameData.GetSignature("ChangeSubclass") return new CSkeletonInstance(GetSkeletonInstance(node.Handle));
);
subclassChangeFunc(weapon.Handle, itemD.ToString());
} }
private static void UpdateWeaponMeshGroupMask(CBaseEntity weapon, bool isLegacy = false) private static unsafe CHandle<CBaseViewModel>[]? GetPlayerViewModels(CCSPlayerController player)
{
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(player.PlayerPawn.Value.ViewModelServices!.Handle); CCSPlayer_ViewModelServices viewModelServices = new CCSPlayer_ViewModelServices(player.PlayerPawn.Value.ViewModelServices!.Handle);
var ptr = viewModelServices.Handle + Schema.GetSchemaOffset("CCSPlayer_ViewModelServices", "m_hViewModel"); return GetFixedArray<CHandle<CBaseViewModel>>(viewModelServices.Handle, "CCSPlayer_ViewModelServices", "m_hViewModel", 3);
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>
{ {
var ptr = pointer + Schema.GetSchemaOffset(@class, member); nint ptr = pointer + Schema.GetSchemaOffset(@class, member);
var references = MemoryMarshal.CreateSpan(ref ptr, length); Span<nint> references = MemoryMarshal.CreateSpan<nint>(ref ptr, length);
var values = new T[length]; T[] values = new T[length];
for (var i = 0; i < length; i++) for (int 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

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

View File

@@ -7,16 +7,15 @@ 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(230)] [MinimumApiVersion(168)]
public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig> public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
{ {
internal static WeaponPaints Instance { get; private set; } = new(); internal static WeaponPaints Instance { get; private set; } = new();
internal static readonly int[] newPaints = { 1171, 1170, 1169, 1164, 1162, 1161, 1159, 1175, 1174, 1167, 1165, 1168, 1163, 1160, 1166, 1173 };
private static readonly Dictionary<string, string> WeaponList = new() internal static readonly Dictionary<string, string> weaponList = new()
{ {
{"weapon_deagle", "Desert Eagle"}, {"weapon_deagle", "Desert Eagle"},
{"weapon_elite", "Dual Berettas"}, {"weapon_elite", "Dual Berettas"},
@@ -76,33 +75,25 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
{ "weapon_knife_kukri", "Kukri Knife" } { "weapon_knife_kukri", "Kukri Knife" }
}; };
private static WeaponPaintsConfig _config = new(); internal static WeaponPaintsConfig _config = new WeaponPaintsConfig();
internal static IStringLocalizer? _localizer; internal static IStringLocalizer? _localizer;
private static Dictionary<int, int> g_knifePickupCount = new(); internal static Dictionary<int, int> g_knifePickupCount = new Dictionary<int, int>();
internal static ConcurrentDictionary<int, string> g_playersKnife = new(); internal static ConcurrentDictionary<int, string> g_playersKnife = new ConcurrentDictionary<int, string>();
internal static ConcurrentDictionary<int, ushort> g_playersGlove = new(); internal static ConcurrentDictionary<int, ushort> g_playersGlove = new ConcurrentDictionary<int, ushort>();
internal static ConcurrentDictionary<int, ushort> g_playersMusic = new(); internal static ConcurrentDictionary<int, ConcurrentDictionary<int, WeaponInfo>> gPlayerWeaponsInfo = new ConcurrentDictionary<int, ConcurrentDictionary<int, WeaponInfo>>();
internal static ConcurrentDictionary<int, (string? CT, string? T)> g_playersAgent = new(); internal static List<JObject> skinsList = new List<JObject>();
internal static ConcurrentDictionary<int, ConcurrentDictionary<int, WeaponInfo>> gPlayerWeaponsInfo = new(); internal static List<JObject> glovesList = new List<JObject>();
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;
private static bool g_bCommandsAllowed = true; public static bool g_bCommandsAllowed = true;
private Dictionary<int, string> PlayerWeaponImage = new(); internal Dictionary<int, string> PlayerWeaponImage = new();
private static Dictionary<int, DateTime> commandsCooldown = new(); internal static Dictionary<int, DateTime> commandsCooldown = new Dictionary<int, DateTime>();
internal static Database? _database; internal static Database? _database;
private static readonly MemoryFunctionVoid<nint, string, float> CAttributeListSetOrAddAttributeValueByName = new(GameData.GetSignature("CAttributeList_SetOrAddAttributeValueByName")); internal static MemoryFunctionVoid<nint, string, float> CAttributeList_SetOrAddAttributeValueByName = new(GameData.GetSignature("CAttributeList_SetOrAddAttributeValueByName"));
internal static MemoryFunctionVoid<CBaseModelEntity, string, UInt64> CBaseModelEntity_SetBodygroup = new(GameData.GetSignature("CBaseModelEntity_SetBodygroup"));
private static readonly MemoryFunctionWithReturn<nint, string, int, int> SetBodygroupFunc = new( public static Dictionary<int, string> WeaponDefindex { get; } = new Dictionary<int, string>
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" },
@@ -160,16 +151,17 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
{ 525, "weapon_knife_skeleton" }, { 525, "weapon_knife_skeleton" },
{ 526, "weapon_knife_kukri" } { 526, "weapon_knife_kukri" }
}; };
public static MemoryFunctionVoid<IntPtr, string, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr> GiveNamedItem2 = new(@"\x55\x48\x89\xE5\x41\x57\x41\x56\x41\x55\x41\x54\x53\x48\x83\xEC\x18\x48\x89\x7D\xC8\x48\x85\xF6\x74");
private const ulong MinimumCustomItemId = 65578; public WeaponPaintsConfig Config { get; set; } = new();
private ulong _nextItemId = MinimumCustomItemId;
public static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
public WeaponPaintsConfig Config { get; set; } = new();
public override string ModuleAuthor => "Nereziel & daffyy"; public override string ModuleAuthor => "Nereziel & daffyy";
public override string ModuleDescription => "Skin, gloves, agents and knife selector, standalone and web-based"; public override string ModuleDescription => "Skin, gloves and knife selector, standalone and web-based";
public override string ModuleName => "WeaponPaints"; public override string ModuleName => "WeaponPaints";
public override string ModuleVersion => "2.5a"; public override string ModuleVersion => "1.9b";
public static WeaponPaintsConfig GetWeaponPaintsConfig()
{
return _config;
}
public override void Load(bool hotReload) public override void Load(bool hotReload)
{ {
@@ -179,39 +171,41 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
{ {
OnMapStart(string.Empty); OnMapStart(string.Empty);
foreach (var player in Enumerable foreach (var player in Utilities.GetPlayers())
.OfType<CCSPlayerController>(Utilities.GetPlayers().TakeWhile(player => weaponSync != null))
.Where(player => player.IsValid &&
!string.IsNullOrEmpty(player.IpAddress) && player is
{ IsBot: false, Connected: PlayerConnectedState.PlayerConnected }))
{ {
g_knifePickupCount[player.Slot] = 0; if (weaponSync == null || player is null || !player.IsValid || player.SteamID.ToString().Length != 17 || !player.PawnIsAlive || player.IsBot ||
gPlayerWeaponsInfo.TryRemove(player.Slot, out _); player.IsHLTV || player.Connected != PlayerConnectedState.PlayerConnected)
g_playersKnife.TryRemove(player.Slot, out _); continue;
g_playersGlove.TryRemove(player.Slot, out _);
g_playersAgent.TryRemove(player.Slot, out _);
PlayerInfo? playerInfo = new PlayerInfo g_knifePickupCount[(int)player.Slot] = 0;
{ gPlayerWeaponsInfo.TryRemove((int)player.Slot, out _);
UserId = player.UserId, g_playersKnife.TryRemove((int)player.Slot, out _);
Slot = player.Slot,
Index = (int)player.Index,
SteamId = player?.SteamID.ToString(),
Name = player?.PlayerName,
IpAddress = player?.IpAddress?.Split(":")[0]
};
_ = Task.Run(async () => PlayerInfo playerInfo = new PlayerInfo(
(int)player.Slot,
player.Slot,
player.UserId,
player?.SteamID.ToString(),
player?.PlayerName,
player?.IpAddress?.Split(":")[0]);
if (Config.Additional.SkinEnabled)
{ {
if (weaponSync != null) await weaponSync.GetPlayerData(playerInfo); Task.Run(() => weaponSync.GetWeaponPaintsFromDatabase(playerInfo));
}); }
if (Config.Additional.KnifeEnabled)
{
Task.Run(() => weaponSync.GetKnifeFromDatabase(playerInfo));
}
if (Config.Additional.GloveEnabled)
{
Task.Run(() => weaponSync.GetGloveFromDatabase(playerInfo));
}
} }
} }
Utility.LoadSkinsFromFile(ModuleDirectory + "/skins.json", Logger); Utility.LoadSkinsFromFile(ModuleDirectory + "/skins.json");
Utility.LoadGlovesFromFile(ModuleDirectory + "/gloves.json", Logger); Utility.LoadGlovesFromFile(ModuleDirectory + "/gloves.json");
Utility.LoadAgentsFromFile(ModuleDirectory + "/agents.json", Logger);
Utility.LoadMusicFromFile(ModuleDirectory + "/music.json", Logger);
if (Config.Additional.KnifeEnabled) if (Config.Additional.KnifeEnabled)
SetupKnifeMenu(); SetupKnifeMenu();
@@ -219,29 +213,25 @@ 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();
} }
public static void PlayerGiveNamedItem(CCSPlayerController player, string item)
{
if (!player.PlayerPawn.IsValid) return;
if (player.PlayerPawn.Value == null) return;
if (!player.PlayerPawn.Value.IsValid) return;
if (player.PlayerPawn.Value.ItemServices == null) return;
public void OnConfigParsed(WeaponPaintsConfig config) GiveNamedItem2.Invoke(player.PlayerPawn.Value.ItemServices.Handle, item, 0, 0, 0, 0, 0, 0);
}
public void OnConfigParsed(WeaponPaintsConfig config)
{ {
if (config.DatabaseHost.Length < 1 || config.DatabaseName.Length < 1 || config.DatabaseUser.Length < 1) if (config.DatabaseHost.Length < 1 || config.DatabaseName.Length < 1 || config.DatabaseUser.Length < 1)
{ {
Logger.LogError("You need to setup Database credentials in \"configs/plugins/WeaponPaints/WeaponPaints.json\"!"); Logger.LogError("You need to setup Database credentials in config!");
Unload(false); throw new Exception("[WeaponPaints] You need to setup Database credentials in config!");
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
@@ -251,11 +241,10 @@ 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 Database(builder.ConnectionString); _database = new(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>net8.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
@@ -9,17 +9,13 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.233" /> <PackageReference Include="CounterStrikeSharp.API" Version="1.0.172" />
<PackageReference Include="Dapper" Version="2.1.35" /> <PackageReference Include="Dapper" Version="2.1.28" />
<PackageReference Include="MySqlConnector" Version="2.3.7" /> <PackageReference Include="MySqlConnector" Version="2.3.5" />
<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,5 +1,4 @@
using Dapper; using Dapper;
using MySqlConnector;
using System.Collections.Concurrent; using System.Collections.Concurrent;
namespace WeaponPaints namespace WeaponPaints
@@ -15,112 +14,71 @@ namespace WeaponPaints
_config = config; _config = config;
} }
internal async Task GetPlayerData(PlayerInfo? player) internal async Task GetKnifeFromDatabase(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";
if (_config.Additional.KnifeEnabled) string? playerKnife = await connection.QueryFirstOrDefaultAsync<string>(query, new { steamid = player.SteamId });
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 ex) catch (Exception e)
{ {
Utility.Log($"An error occurred in GetKnifeFromDatabase: {ex.Message}"); Utility.Log(e.Message);
return;
} }
} }
private void GetGloveFromDatabase(PlayerInfo? player, MySqlConnection connection) internal async Task GetGloveFromDatabase(PlayerInfo player)
{ {
if (!_config.Additional.GloveEnabled) return;
try try
{ {
if (!_config.Additional.GloveEnabled || string.IsNullOrEmpty(player?.SteamId)) // Ensure proper disposal of resources using "using" statement
return; await using var connection = await _database.GetConnectionAsync();
const string query = "SELECT `weapon_defindex` FROM `wp_player_gloves` WHERE `steamid` = @steamid"; // Construct the SQL query with specific columns for better performance
var gloveData = connection.QueryFirstOrDefault<ushort?>(query, new { steamid = player.SteamId }); string query = "SELECT `weapon_defindex` FROM `wp_player_gloves` WHERE `steamid` = @steamid";
// Execute the query and retrieve glove data
ushort? gloveData = await connection.QueryFirstOrDefaultAsync<ushort?>(query, new { steamid = player.SteamId });
// 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 ex) catch (Exception e)
{ {
Utility.Log($"An error occurred in GetGloveFromDatabase: {ex.Message}"); // Log any exceptions occurred during database operation
Utility.Log("An error occurred while fetching glove data: " + e.Message);
} }
} }
private void GetAgentFromDatabase(PlayerInfo? player, MySqlConnection connection) internal async Task GetWeaponPaintsFromDatabase(PlayerInfo player)
{ {
if (!_config.Additional.SkinEnabled || player == null || player.SteamId == null) return;
try try
{ {
if (!_config.Additional.AgentEnabled || string.IsNullOrEmpty(player?.SteamId)) await using var connection = await _database.GetConnectionAsync();
return; string query = "SELECT `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed` FROM `wp_player_skins` WHERE `steamid` = @steamid";
const string query = "SELECT `agent_ct`, `agent_t` FROM `wp_player_agents` WHERE `steamid` = @steamid"; var playerSkins = await connection.QueryAsync<dynamic>(query, new { steamid = player.SteamId });
var agentData = connection.QueryFirstOrDefault<(string, string)>(query, new { steamid = player.SteamId });
if (agentData == default) return; if (playerSkins == null) 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;
@@ -140,44 +98,20 @@ namespace WeaponPaints
WeaponPaints.gPlayerWeaponsInfo[player.Slot] = weaponInfos; WeaponPaints.gPlayerWeaponsInfo[player.Slot] = weaponInfos;
} }
catch (Exception ex) catch (Exception e)
{ {
Utility.Log($"An error occurred in GetWeaponPaintsFromDatabase: {ex.Message}"); Utility.Log($"Database error occurred: {e.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 || string.IsNullOrEmpty(player.SteamId) || string.IsNullOrEmpty(knife)) return; if (!_config.Additional.KnifeEnabled || player == null || 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)
@@ -186,14 +120,15 @@ namespace WeaponPaints
} }
} }
internal async Task SyncGloveToDatabase(PlayerInfo player, int defindex) internal async Task SyncGloveToDatabase(PlayerInfo player, int defindex)
{ {
if (!_config.Additional.GloveEnabled || string.IsNullOrEmpty(player.SteamId)) return; if (!_config.Additional.GloveEnabled || player == null || string.IsNullOrEmpty(player.SteamId)) return;
try try
{ {
await using var connection = await _database.GetConnectionAsync(); await using var connection = await _database.GetConnectionAsync();
const string query = "INSERT INTO `wp_player_gloves` (`steamid`, `weapon_defindex`) VALUES(@steamid, @weapon_defindex) ON DUPLICATE KEY UPDATE `weapon_defindex` = @weapon_defindex"; 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)
@@ -202,86 +137,44 @@ 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 (string.IsNullOrEmpty(player.SteamId) || !WeaponPaints.gPlayerWeaponsInfo.TryGetValue(player.Slot, out var weaponsInfo)) if (player == null || 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 (weaponDefIndex, weaponInfo) in weaponsInfo) foreach (var weaponInfoPair in weaponsInfo)
{ {
var paintId = weaponInfo.Paint; int weaponDefIndex = weaponInfoPair.Key;
var wear = weaponInfo.Wear; WeaponInfo weaponInfo = weaponInfoPair.Value;
var seed = weaponInfo.Seed;
const string queryCheckExistence = "SELECT COUNT(*) FROM `wp_player_skins` WHERE `steamid` = @steamid AND `weapon_defindex` = @weaponDefIndex"; int paintId = weaponInfo.Paint;
float wear = weaponInfo.Wear;
int seed = weaponInfo.Seed;
var existingRecordCount = await connection.ExecuteScalarAsync<int>(queryCheckExistence, new { steamid = player.SteamId, weaponDefIndex = weaponDefIndex }); string query = "INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed`) " +
"VALUES (@steamid, @weaponDefIndex, @paintId, @wear, @seed) " +
"ON DUPLICATE KEY UPDATE `weapon_paint_id` = @paintId, `weapon_wear` = @wear, `weapon_seed` = @seed";
string query; var parameters = new DynamicParameters();
object parameters; parameters.Add("@steamid", player.SteamId);
parameters.Add("@weaponDefIndex", weaponDefIndex);
parameters.Add("@paintId", paintId);
parameters.Add("@wear", wear);
parameters.Add("@seed", seed);
if (existingRecordCount > 0) await connection.ExecuteAsync(query, parameters);
{ }
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 };
}
await connection.ExecuteAsync(query, parameters); }
} catch (Exception e)
}
catch (Exception e)
{ {
Utility.Log($"Error syncing weapon paints to database: {e.Message}"); Utility.Log($"Error syncing weapon paints to database: {e.Message}");
} }
} }
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}");
}
}
} }
} }

16
gamedata/gloves.json Normal file
View File

@@ -0,0 +1,16 @@
{
"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

@@ -1,23 +0,0 @@
{
"ChangeSubclass": {
"signatures": {
"library": "server",
"windows": "48 89 6C 24 ? 56 48 83 EC ? 48 8B EA 48 8B F1 E8 ? ? ? ? 84 C0 0F 84",
"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,8 +4,6 @@
"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",
@@ -13,13 +11,7 @@
"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,25 +1,17 @@
{ {
"wp_prefix": "{lightblue}[WeaponPaints] {default}", "wp_prefix": "{lightblue}[WeaponPaints] {default}",
"wp_info_website": "Apmeklē {lime}{0}{default}, kur varat mainīt ādas", "wp_info_website": "Apmeklējiet {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_info_agent": "Ievadiet {lime}!agents{default}, lai atvērtu aģentu izvēlni", "wp_command_cooldown": "{lightred}Šobrīd jūs nevarat atjaunot ieroču ādas",
"wp_info_music": "Ievadiet {lime}!music{default}, lai atvērtu mūzikas izvēlni", "wp_command_refresh_done": "{lime}Atjauno ieroču ādas",
"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": "Nazi Izvēlne", "wp_knife_menu_title": "Nazis Izvēlne",
"wp_glove_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu cimdu", "wp_glove_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savus cimdus",
"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 priekš {lime}{0}{default}", "wp_skin_menu_skin_title": "Izvēlieties ādu {lime}{0}{default}",
"wp_skin_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu ādu", "wp_skin_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu ādu"
"None": "Nav"
} }

View File

@@ -3,23 +3,15 @@
"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ękawic", "wp_info_glove": "Wpisz {lime}!gloves{default}, aby otworzyć menu rękawiczek",
"wp_info_agent": "Wpisz {lime}!agents{default}, aby otworzyć menu agentów", "wp_command_cooldown": "{lightred}Nie możesz teraz odświeżyć skórek broni",
"wp_info_music": "Wpisz {lime}!music{default}, aby otworzyć menu muzyczne", "wp_command_refresh_done": "{lime}Odświeżanie skórek broni",
"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": "", "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_title": "Menu Noży", "wp_knife_menu_title": "Menu Noży",
"wp_glove_menu_select": "Wybrałeś {lime}{0}{default} jako swoją rękawiczkę", "wp_glove_menu_select": "Wybrałeś {lime}{0}{default} jako swoje rękawiczki",
"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,25 +1,17 @@
{ {
"wp_prefix": "{lightblue}[WeaponPaints] {default}", "wp_prefix": "{lightblue}[WeaponPaints] {default}",
"wp_info_website": "Visite {lime}{0}{default}, onde você pode alterar as skins", "wp_info_website": "Visite {lime}{0}{default}, onde você pode alterar skins",
"wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins escolhidas", "wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins selecionadas",
"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_info_agent": "Digite {lime}!agents{default} para abrir o menu de agentes", "wp_command_cooldown": "{lightred}Você não pode atualizar as skins de arma agora",
"wp_info_music": "Digite {lime}!music{default} para abrir o menu de música", "wp_command_refresh_done": "{lime}Atualizando as skins de arma",
"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 sua luva", "wp_glove_menu_select": "Você escolheu {lime}{0}{default} como suas luvas",
"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 a skin para {lime}{0}{default}", "wp_skin_menu_skin_title": "Selecione uma skin para {lime}{0}{default}",
"wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin", "wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin"
"None": "Nenhum"
} }

View File

@@ -1,25 +1,17 @@
{ {
"wp_prefix": "{lightblue}[WeaponPaints] {default}", "wp_prefix": "{lightblue}[WeaponPaints] {default}",
"wp_info_website": "Visite {lime}{0}{default}, onde pode alterar as skins", "wp_info_website": "Visite {lime}{0}{default}, onde pode alterar skins",
"wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins escolhidas", "wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins selecionadas",
"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_info_agent": "Digite {lime}!agents{default} para abrir o menu de agentes", "wp_command_cooldown": "{lightred}Você não pode atualizar as skins de arma agora",
"wp_info_music": "Digite {lime}!music{default} para abrir o menu de música", "wp_command_refresh_done": "{lime}Atualizando as skins de arma",
"wp_command_cooldown": "{lightred}Não pode atualizar as skins de armas de momento", "wp_knife_menu_select": "Você escolheu {lime}{0}{default} como sua faca",
"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": "Escolheu {lime}{0}{default} como a sua luva", "wp_glove_menu_select": "Você escolheu {lime}{0}{default} como suas luvas",
"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 a skin para {lime}{0}{default}", "wp_skin_menu_skin_title": "Selecione uma skin para {lime}{0}{default}",
"wp_skin_menu_select": "Escolheu {lime}{0}{default} como a sua skin", "wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin"
"None": "Nenhum"
} }

View File

@@ -4,22 +4,14 @@
"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_info_agent": "Введите {lime}!agents{default}, чтобы открыть меню агентов", "wp_command_cooldown": "{lightred}Вы не можете обновить скины оружия сейчас",
"wp_info_music": "Введите {lime}!music{default}, чтобы открыть меню музыки", "wp_command_refresh_done": "{lime}Обновление скинов оружия",
"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,25 +1,17 @@
{ {
"wp_prefix": "{lightblue}[WeaponPaints] {default}", "wp_prefix": "{lightblue}[WeaponPaints] {default}",
"wp_info_website": "Ziyaret edin {lime}{0}{default}, nerede derileri değiştirebilirsiniz", "wp_info_website": "Ziyaret edin {lime}{0}{default}, burada skinleri değiştirebilirsiniz",
"wp_info_refresh": "Senkronize etmek için {lime}!wp{default} yazın seçilen deriler", "wp_info_refresh": "Senkronize edilen skinleri görmek için {lime}!wp{default} yazın",
"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": "Handskar menüsünü açmak için {lime}!gloves{default} yazın", "wp_info_glove": "Eldiven menüsünü açmak için {lime}!gloves{default} yazın",
"wp_info_agent": "Ajan menüsünü açmak için {lime}!agents{default} yazın", "wp_command_cooldown": "{lightred}Şu anda silah skinlerini yenileyemezsiniz",
"wp_info_music": "Müzik menüsünü açmak için {lime}!music{default} yazın", "wp_command_refresh_done": "{lime}Silah skinleri yenileniyor",
"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 cilt seçin", "wp_skin_menu_skin_title": "{lime}{0}{default} için bir skin seçin",
"wp_skin_menu_select": "{lime}{0}{default} olarak cildinizi seçtiniz", "wp_skin_menu_select": "{lime}{0}{default} olarak bir skin seçtiniz"
"None": "Hiçbiri"
} }

View File

@@ -4,22 +4,14 @@
"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_info_agent": "Введіть {lime}!agents{default}, щоб відкрити меню агентів", "wp_command_cooldown": "{lightred}Наразі ви не можете оновлювати шкіри зброї",
"wp_info_music": "Введіть {lime}!music{default}, щоб відкрити меню музики", "wp_command_refresh_done": "{lime}Оновлення шкірок зброї",
"wp_command_cooldown": "{lightred}Ви не можете оновити фарби зброї зараз", "wp_knife_menu_select": "Ви вибрали {lime}{0}{default} як ваш ніж",
"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,25 +1,17 @@
{ {
"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_info_agent": "输入 {lime}!agents{default} 打开代理菜单", "wp_command_cooldown": "{lightred}您现在无法刷新武器皮肤",
"wp_info_music": "输入 {lime}!music{default} 打开音乐菜单", "wp_command_refresh_done": "{lime}刷新武器皮肤",
"wp_command_cooldown": "{lightred}您现在无法刷新武器涂装", "wp_knife_menu_select": "您已选择 {lime}{0}{default} 作为您的刀具",
"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,392 +0,0 @@
[
{
"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

View File

@@ -1,367 +0,0 @@
[
{
"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.

Before

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 KiB

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