mirror of
https://github.com/Nereziel/cs2-WeaponPaints.git
synced 2026-02-23 04:00:37 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8f0e8a0a63 | ||
|
|
eb6cdfc98d | ||
|
|
56537971ad |
14
.github/FUNDING.yml
vendored
14
.github/FUNDING.yml
vendored
@@ -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']
|
|
||||||
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
@@ -58,10 +58,14 @@ jobs:
|
|||||||
${{ env.OUTPUT_PATH }}/McMaster.NETCore.Plugins.dll \
|
${{ env.OUTPUT_PATH }}/McMaster.NETCore.Plugins.dll \
|
||||||
${{ env.OUTPUT_PATH }}/Microsoft.DotNet.PlatformAbstractions.dll \
|
${{ env.OUTPUT_PATH }}/Microsoft.DotNet.PlatformAbstractions.dll \
|
||||||
${{ env.OUTPUT_PATH }}/Microsoft.Extensions.DependencyModel.dll \
|
${{ env.OUTPUT_PATH }}/Microsoft.Extensions.DependencyModel.dll \
|
||||||
- name: Copy skins
|
- name: Copy skins.json
|
||||||
run: cp -R website/data ${{ env.OUTPUT_PATH }}
|
run: cp website/data/skins.json ${{ env.OUTPUT_PATH }}/skins.json
|
||||||
- name: Zip
|
- name: Zip
|
||||||
run: zip -r "${{ env.PROJECT_NAME }}.zip" "${{ env.OUTPUT_PATH }}" gamedata/
|
uses: thedoctor0/zip-release@0.7.5
|
||||||
|
with:
|
||||||
|
type: 'zip'
|
||||||
|
filename: '${{ env.PROJECT_NAME }}.zip'
|
||||||
|
path: ${{ env.OUTPUT_PATH }}
|
||||||
- name: Clean files Website
|
- name: Clean files Website
|
||||||
run: |
|
run: |
|
||||||
rm -rf website/img/
|
rm -rf website/img/
|
||||||
|
|||||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
.vs/
|
.vs/
|
||||||
bin/
|
bin/
|
||||||
obj/
|
obj/
|
||||||
website/getskins.php
|
.test/
|
||||||
.idea/
|
|
||||||
|
|||||||
BIN
3rd_party/MenuManagerApi.dll
vendored
BIN
3rd_party/MenuManagerApi.dll
vendored
Binary file not shown.
761
Commands.cs
761
Commands.cs
@@ -1,7 +1,6 @@
|
|||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Modules.Commands;
|
using CounterStrikeSharp.API.Modules.Commands;
|
||||||
using CounterStrikeSharp.API.Modules.Menu;
|
using CounterStrikeSharp.API.Modules.Menu;
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
namespace WeaponPaints
|
namespace WeaponPaints
|
||||||
{
|
{
|
||||||
@@ -9,53 +8,50 @@ namespace WeaponPaints
|
|||||||
{
|
{
|
||||||
private void OnCommandRefresh(CCSPlayerController? player, CommandInfo command)
|
private void OnCommandRefresh(CCSPlayerController? player, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (!Config.Additional.CommandWpEnabled || !Config.Additional.SkinEnabled || !_gBCommandsAllowed) return;
|
if (!Config.AdditionalSetting.CommandWpEnabled || !Config.AdditionalSetting.SkinEnabled || !g_bCommandsAllowed) return;
|
||||||
if (!Utility.IsPlayerValid(player)) return;
|
if (!Utility.IsPlayerValid(player)) return;
|
||||||
|
if (player == null || player.Index <= 0) return;
|
||||||
|
int playerIndex = (int)player!.Index;
|
||||||
|
|
||||||
if (player == null || !player.IsValid || player.UserId == null || player.IsBot) return;
|
PlayerInfo playerInfo = new PlayerInfo
|
||||||
|
|
||||||
PlayerInfo? playerInfo = new PlayerInfo
|
|
||||||
{
|
{
|
||||||
UserId = player.UserId,
|
UserId = player.UserId,
|
||||||
Slot = player.Slot,
|
|
||||||
Index = (int)player.Index,
|
Index = (int)player.Index,
|
||||||
SteamId = player?.SteamID.ToString(),
|
SteamId = player?.AuthorizedSteamID?.SteamId64,
|
||||||
Name = player?.PlayerName,
|
Name = player?.PlayerName,
|
||||||
IpAddress = player?.IpAddress?.Split(":")[0]
|
IpAddress = player?.IpAddress?.Split(":")[0]
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
if (player == null || player.UserId == null) return;
|
||||||
|
|
||||||
|
if (!commandsCooldown.TryGetValue((int)player.UserId, out DateTime cooldownEndTime) ||
|
||||||
|
DateTime.UtcNow >= (commandsCooldown.TryGetValue((int)player.UserId, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
|
||||||
{
|
{
|
||||||
if (player != null && !CommandsCooldown.TryGetValue(player.Slot, out var cooldownEndTime) ||
|
commandsCooldown[(int)player.UserId] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
|
||||||
player != null && DateTime.UtcNow >= (CommandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
|
if (weaponSync != null)
|
||||||
|
Task.Run(async () => await weaponSync.GetWeaponPaintsFromDatabase(playerInfo));
|
||||||
|
if (Config.AdditionalSetting.KnifeEnabled)
|
||||||
{
|
{
|
||||||
CommandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
|
if (weaponSync != null)
|
||||||
|
Task.Run(async () => await weaponSync.GetKnifeFromDatabase(playerInfo));
|
||||||
|
|
||||||
if (WeaponSync != null)
|
RefreshWeapons(player);
|
||||||
{
|
|
||||||
_ = Task.Run(async () => await WeaponSync.GetPlayerData(playerInfo));
|
|
||||||
|
|
||||||
GivePlayerGloves(player);
|
|
||||||
RefreshWeapons(player);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_command_refresh_done"]))
|
|
||||||
{
|
|
||||||
player.Print(Localizer["wp_command_refresh_done"]);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"]))
|
if (!string.IsNullOrEmpty(Localizer["wp_command_refresh_done"]))
|
||||||
{
|
{
|
||||||
player!.Print(Localizer["wp_command_cooldown"]);
|
player!.Print(Localizer["wp_command_refresh_done"]);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"]))
|
||||||
|
{
|
||||||
|
player!.Print(Localizer["wp_command_cooldown"]);
|
||||||
}
|
}
|
||||||
catch (Exception) { }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnCommandWS(CCSPlayerController? player, CommandInfo command)
|
private void OnCommandWS(CCSPlayerController? player, CommandInfo command)
|
||||||
{
|
{
|
||||||
if (!Config.Additional.SkinEnabled) return;
|
if (!Config.AdditionalSetting.SkinEnabled) return;
|
||||||
if (!Utility.IsPlayerValid(player)) return;
|
if (!Utility.IsPlayerValid(player)) return;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_info_website"]))
|
if (!string.IsNullOrEmpty(Localizer["wp_info_website"]))
|
||||||
@@ -66,26 +62,7 @@ namespace WeaponPaints
|
|||||||
{
|
{
|
||||||
player!.Print(Localizer["wp_info_refresh"]);
|
player!.Print(Localizer["wp_info_refresh"]);
|
||||||
}
|
}
|
||||||
|
if (!Config.AdditionalSetting.KnifeEnabled) return;
|
||||||
if (Config.Additional.GloveEnabled)
|
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_info_glove"]))
|
|
||||||
{
|
|
||||||
player!.Print(Localizer["wp_info_glove"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.Additional.AgentEnabled)
|
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_info_agent"]))
|
|
||||||
{
|
|
||||||
player!.Print(Localizer["wp_info_agent"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Config.Additional.MusicEnabled)
|
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_info_music"]))
|
|
||||||
{
|
|
||||||
player!.Print(Localizer["wp_info_music"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Config.Additional.KnifeEnabled) return;
|
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_info_knife"]))
|
if (!string.IsNullOrEmpty(Localizer["wp_info_knife"]))
|
||||||
{
|
{
|
||||||
player!.Print(Localizer["wp_info_knife"]);
|
player!.Print(Localizer["wp_info_knife"]);
|
||||||
@@ -94,603 +71,225 @@ namespace WeaponPaints
|
|||||||
|
|
||||||
private void RegisterCommands()
|
private void RegisterCommands()
|
||||||
{
|
{
|
||||||
_config.Additional.CommandSkin.ForEach(c =>
|
AddCommand($"css_{Config.AdditionalSetting.CommandSkin}", "Skins info", (player, info) =>
|
||||||
{
|
{
|
||||||
AddCommand($"css_{c}", "Skins info", (player, info) =>
|
if (!Utility.IsPlayerValid(player)) return;
|
||||||
{
|
OnCommandWS(player, info);
|
||||||
if (!Utility.IsPlayerValid(player)) return;
|
|
||||||
OnCommandWS(player, info);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
AddCommand($"css_{Config.AdditionalSetting.CommandRefresh}", "Skins refresh", (player, info) =>
|
||||||
_config.Additional.CommandRefresh.ForEach(c =>
|
|
||||||
{
|
{
|
||||||
AddCommand($"css_{c}", "Skins refresh", (player, info) =>
|
if (!Utility.IsPlayerValid(player) || !g_bCommandsAllowed) return;
|
||||||
{
|
OnCommandRefresh(player, info);
|
||||||
if (!Utility.IsPlayerValid(player)) return;
|
|
||||||
OnCommandRefresh(player, info);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
if (Config.AdditionalSetting.CommandKillEnabled)
|
||||||
if (Config.Additional.CommandKillEnabled)
|
|
||||||
{
|
{
|
||||||
_config.Additional.CommandKill.ForEach(c =>
|
AddCommand($"css_{Config.AdditionalSetting.CommandKill}", "kill yourself", (player, info) =>
|
||||||
{
|
{
|
||||||
AddCommand($"css_{c}", "kill yourself", (player, _) =>
|
if (player == null || !Utility.IsPlayerValid(player) || player.PlayerPawn.Value == null || !player!.PlayerPawn.IsValid) return;
|
||||||
{
|
|
||||||
if (player == null || !Utility.IsPlayerValid(player) || player.PlayerPawn.Value == null || !player.PlayerPawn.IsValid) return;
|
|
||||||
|
|
||||||
player.PlayerPawn.Value.CommitSuicide(true, false);
|
player.PlayerPawn.Value.CommitSuicide(true, false);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetupKnifeMenu()
|
private void SetupKnifeMenu()
|
||||||
{
|
{
|
||||||
if (!Config.Additional.KnifeEnabled || !_gBCommandsAllowed) return;
|
if (!Config.AdditionalSetting.KnifeEnabled || !g_bCommandsAllowed) return;
|
||||||
|
|
||||||
var knivesOnly = WeaponList
|
var knivesOnly = weaponList
|
||||||
.Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet"))
|
.Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet"))
|
||||||
.ToDictionary(pair => pair.Key, pair => pair.Value);
|
.ToDictionary(pair => pair.Key, pair => pair.Value);
|
||||||
|
|
||||||
var giveItemMenu = MenuApi?.NewMenu(Localizer["wp_knife_menu_title"]);
|
var giveItemMenu = new ChatMenu(Localizer["wp_knife_menu_title"]);
|
||||||
|
var handleGive = (CCSPlayerController? player, ChatMenuOption option) =>
|
||||||
var handleGive = (CCSPlayerController player, ChatMenuOption option) =>
|
|
||||||
{
|
{
|
||||||
if (!Utility.IsPlayerValid(player)) return;
|
if (Utility.IsPlayerValid(player))
|
||||||
|
|
||||||
var knifeName = option.Text;
|
|
||||||
var knifeKey = knivesOnly.FirstOrDefault(x => x.Value == knifeName).Key;
|
|
||||||
if (string.IsNullOrEmpty(knifeKey)) return;
|
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_knife_menu_select"]))
|
|
||||||
{
|
{
|
||||||
player.Print(Localizer["wp_knife_menu_select", knifeName]);
|
if (player == null) return;
|
||||||
|
var knifeName = option.Text;
|
||||||
|
var knifeKey = knivesOnly.FirstOrDefault(x => x.Value == knifeName).Key;
|
||||||
|
if (!string.IsNullOrEmpty(knifeKey))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(Localizer["wp_knife_menu_select"]))
|
||||||
|
{
|
||||||
|
player!.Print(Localizer["wp_knife_menu_select", knifeName]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(Localizer["wp_knife_menu_kill"]) && Config.AdditionalSetting.CommandKillEnabled)
|
||||||
|
{
|
||||||
|
player!.Print(Localizer["wp_knife_menu_kill"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerInfo playerInfo = new PlayerInfo
|
||||||
|
{
|
||||||
|
UserId = player.UserId,
|
||||||
|
Index = (int)player.Index,
|
||||||
|
SteamId = player?.AuthorizedSteamID?.SteamId64,
|
||||||
|
Name = player?.PlayerName,
|
||||||
|
IpAddress = player?.IpAddress?.Split(":")[0]
|
||||||
|
};
|
||||||
|
|
||||||
|
g_playersKnife[(int)player!.Index] = knifeKey;
|
||||||
|
|
||||||
|
if (player!.PawnIsAlive && g_bCommandsAllowed)
|
||||||
|
{
|
||||||
|
RefreshWeapons(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weaponSync != null)
|
||||||
|
Task.Run(async () => await weaponSync.SyncKnifeToDatabase(playerInfo, knifeKey));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_knife_menu_kill"]) && Config.Additional.CommandKillEnabled)
|
|
||||||
{
|
|
||||||
player.Print(Localizer["wp_knife_menu_kill"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayerInfo playerInfo = new PlayerInfo
|
|
||||||
{
|
|
||||||
UserId = player.UserId,
|
|
||||||
Slot = player.Slot,
|
|
||||||
Index = (int)player.Index,
|
|
||||||
SteamId = player.SteamID.ToString(),
|
|
||||||
Name = player.PlayerName,
|
|
||||||
IpAddress = player.IpAddress?.Split(":")[0]
|
|
||||||
};
|
|
||||||
|
|
||||||
GPlayersKnife[player.Slot] = knifeKey;
|
|
||||||
|
|
||||||
if (_gBCommandsAllowed && (LifeState_t)player.LifeState == LifeState_t.LIFE_ALIVE)
|
|
||||||
RefreshWeapons(player);
|
|
||||||
|
|
||||||
if (WeaponSync != null)
|
|
||||||
_ = Task.Run(async () => await WeaponSync.SyncKnifeToDatabase(playerInfo, knifeKey));
|
|
||||||
};
|
};
|
||||||
foreach (var knifePair in knivesOnly)
|
foreach (var knifePair in knivesOnly)
|
||||||
{
|
{
|
||||||
giveItemMenu?.AddMenuOption(knifePair.Value, handleGive);
|
giveItemMenu.AddMenuOption(knifePair.Value, handleGive);
|
||||||
}
|
}
|
||||||
_config.Additional.CommandKnife.ForEach(c =>
|
AddCommand($"css_{Config.AdditionalSetting.CommandKnife}", "Knife Menu", (player, info) =>
|
||||||
{
|
{
|
||||||
AddCommand($"css_{c}", "Knife Menu", (player, _) =>
|
if (!Utility.IsPlayerValid(player) || !g_bCommandsAllowed) return;
|
||||||
|
|
||||||
|
if (player == null || player.UserId == null) return;
|
||||||
|
|
||||||
|
if (!commandsCooldown.TryGetValue((int)player.UserId, out DateTime cooldownEndTime) ||
|
||||||
|
DateTime.UtcNow >= (commandsCooldown.TryGetValue((int)player.UserId, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
|
||||||
{
|
{
|
||||||
if (giveItemMenu == null) return;
|
commandsCooldown[(int)player.UserId] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
|
||||||
if (!Utility.IsPlayerValid(player) || !_gBCommandsAllowed) return;
|
ChatMenus.OpenMenu(player, giveItemMenu);
|
||||||
|
return;
|
||||||
if (player == null || player.UserId == null) return;
|
}
|
||||||
|
if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"]))
|
||||||
if (!CommandsCooldown.TryGetValue(player.Slot, out var cooldownEndTime) ||
|
{
|
||||||
DateTime.UtcNow >= (CommandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
|
player!.Print(Localizer["wp_command_cooldown"]);
|
||||||
{
|
}
|
||||||
CommandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
|
|
||||||
giveItemMenu.PostSelectAction = PostSelectAction.Close;
|
|
||||||
|
|
||||||
giveItemMenu.Open(player);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"]))
|
|
||||||
{
|
|
||||||
player.Print(Localizer["wp_command_cooldown"]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetupSkinsMenu()
|
private void SetupSkinsMenu()
|
||||||
{
|
{
|
||||||
// var classNamesByWeapon = WeaponList.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
|
var classNamesByWeapon = weaponList.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
|
||||||
var classNamesByWeapon = WeaponList
|
var weaponSelectionMenu = new ChatMenu(Localizer["wp_skin_menu_weapon_title"]);
|
||||||
.Except([new KeyValuePair<string, string>("weapon_knife", "Default Knife")])
|
|
||||||
.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);
|
|
||||||
|
|
||||||
var weaponSelectionMenu = MenuApi?.NewMenu(Localizer["wp_skin_menu_weapon_title"]);
|
|
||||||
|
|
||||||
// Function to handle skin selection for a specific weapon
|
// Function to handle skin selection for a specific weapon
|
||||||
var handleWeaponSelection = (CCSPlayerController? player, ChatMenuOption option) =>
|
var handleWeaponSelection = (CCSPlayerController? player, ChatMenuOption option) =>
|
||||||
{
|
{
|
||||||
if (!Utility.IsPlayerValid(player)) return;
|
if (!Utility.IsPlayerValid(player)) return;
|
||||||
|
|
||||||
var selectedWeapon = option.Text;
|
int playerIndex = (int)player!.Index;
|
||||||
|
string selectedWeapon = option.Text;
|
||||||
if (!classNamesByWeapon.TryGetValue(selectedWeapon, out var selectedWeaponClassname)) return;
|
if (classNamesByWeapon.TryGetValue(selectedWeapon, out string? selectedWeaponClassname))
|
||||||
var skinsForSelectedWeapon = SkinsList?.Where(skin =>
|
{
|
||||||
|
if (selectedWeaponClassname == null) return;
|
||||||
|
var skinsForSelectedWeapon = skinsList?.Where(skin =>
|
||||||
|
skin != null &&
|
||||||
skin.TryGetValue("weapon_name", out var weaponName) &&
|
skin.TryGetValue("weapon_name", out var weaponName) &&
|
||||||
weaponName?.ToString() == selectedWeaponClassname
|
weaponName?.ToString() == selectedWeaponClassname
|
||||||
)?.ToList();
|
)?.ToList();
|
||||||
|
|
||||||
var skinSubMenu = MenuApi?.NewMenu(Localizer["wp_skin_menu_skin_title", selectedWeapon]);
|
var skinSubMenu = new ChatMenu(Localizer["wp_skin_menu_skin_title", selectedWeapon]);
|
||||||
|
|
||||||
// Function to handle skin selection for the chosen weapon
|
// Function to handle skin selection for the chosen weapon
|
||||||
var handleSkinSelection = (CCSPlayerController p, ChatMenuOption opt) =>
|
var handleSkinSelection = (CCSPlayerController? p, ChatMenuOption opt) =>
|
||||||
{
|
|
||||||
if (!Utility.IsPlayerValid(p)) return;
|
|
||||||
|
|
||||||
var steamId = p.SteamID.ToString();
|
|
||||||
var firstSkin = SkinsList?.FirstOrDefault(skin =>
|
|
||||||
{
|
{
|
||||||
if (skin.TryGetValue("weapon_name", out var weaponName))
|
if (p == null || !p.IsValid || p.Index <= 0) return;
|
||||||
|
|
||||||
|
playerIndex = (int)p.Index;
|
||||||
|
|
||||||
|
if (p.AuthorizedSteamID == null) return;
|
||||||
|
|
||||||
|
string steamId = p.AuthorizedSteamID.SteamId64.ToString();
|
||||||
|
var firstSkin = skinsList?.FirstOrDefault(skin =>
|
||||||
{
|
{
|
||||||
return weaponName?.ToString() == selectedWeaponClassname;
|
if (skin != null && skin.TryGetValue("weapon_name", out var weaponName))
|
||||||
|
{
|
||||||
|
return weaponName?.ToString() == selectedWeaponClassname;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
string selectedSkin = opt.Text;
|
||||||
|
string selectedPaintID = selectedSkin.Split('(')[1].Trim(')').Trim();
|
||||||
|
|
||||||
|
if (firstSkin != null &&
|
||||||
|
firstSkin.TryGetValue("weapon_defindex", out var weaponDefIndexObj) &&
|
||||||
|
weaponDefIndexObj != null &&
|
||||||
|
ushort.TryParse(weaponDefIndexObj.ToString(), out var weaponDefIndex) &&
|
||||||
|
ushort.TryParse(selectedPaintID, out var paintID))
|
||||||
|
{
|
||||||
|
p!.Print(Localizer["wp_skin_menu_select", selectedSkin]);
|
||||||
|
|
||||||
|
if (!gPlayerWeaponsInfo[playerIndex].TryGetValue(weaponDefIndex, out _))
|
||||||
|
{
|
||||||
|
gPlayerWeaponsInfo[playerIndex][weaponDefIndex] = new WeaponInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Paint = paintID;
|
||||||
|
gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Wear = 0.00001f;
|
||||||
|
gPlayerWeaponsInfo[playerIndex][weaponDefIndex].Seed = 0;
|
||||||
|
|
||||||
|
PlayerInfo playerInfo = new PlayerInfo
|
||||||
|
{
|
||||||
|
UserId = player.UserId,
|
||||||
|
Index = (int)player.Index,
|
||||||
|
SteamId = player?.AuthorizedSteamID?.SteamId64,
|
||||||
|
Name = player?.PlayerName,
|
||||||
|
IpAddress = player?.IpAddress?.Split(":")[0]
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!Config.GlobalShare)
|
||||||
|
{
|
||||||
|
if (weaponSync != null)
|
||||||
|
Task.Run(async () => await weaponSync.SyncWeaponPaintToDatabase(playerInfo, weaponDefIndex));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
};
|
||||||
});
|
|
||||||
|
|
||||||
var selectedSkin = opt.Text;
|
// Add skin options to the submenu for the selected weapon
|
||||||
var selectedPaintId = selectedSkin[(selectedSkin.LastIndexOf('(') + 1)..].Trim(')');
|
if (skinsForSelectedWeapon != null)
|
||||||
|
|
||||||
if (firstSkin == null ||
|
|
||||||
!firstSkin.TryGetValue("weapon_defindex", out var weaponDefIndexObj) ||
|
|
||||||
!int.TryParse(weaponDefIndexObj.ToString(), out var weaponDefIndex) ||
|
|
||||||
!int.TryParse(selectedPaintId, out var paintId)) return;
|
|
||||||
{
|
{
|
||||||
if (Config.Additional.ShowSkinImage && SkinsList != null)
|
foreach (var skin in skinsForSelectedWeapon.Where(s => s != null))
|
||||||
{
|
{
|
||||||
var foundSkin = SkinsList.FirstOrDefault(skin =>
|
if (skin.TryGetValue("paint_name", out var paintNameObj) && skin.TryGetValue("paint", out var paintObj))
|
||||||
((int?)skin?["weapon_defindex"] ?? 0) == weaponDefIndex &&
|
{
|
||||||
((int?)skin?["paint"] ?? 0) == paintId &&
|
var paintName = paintNameObj?.ToString();
|
||||||
skin?["image"] != null
|
var paint = paintObj?.ToString();
|
||||||
);
|
|
||||||
var image = foundSkin?["image"]?.ToString() ?? "";
|
|
||||||
_playerWeaponImage[p.Slot] = image;
|
|
||||||
AddTimer(2.0f, () => _playerWeaponImage.Remove(p.Slot), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
p.Print(Localizer["wp_skin_menu_select", selectedSkin]);
|
if (!string.IsNullOrEmpty(paintName) && !string.IsNullOrEmpty(paint))
|
||||||
|
{
|
||||||
if (!GPlayerWeaponsInfo[p.Slot].TryGetValue(weaponDefIndex, out var value))
|
skinSubMenu.AddMenuOption($"{paintName} ({paint})", handleSkinSelection);
|
||||||
{
|
}
|
||||||
value = new WeaponInfo();
|
}
|
||||||
GPlayerWeaponsInfo[p.Slot][weaponDefIndex] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
value.Paint = paintId;
|
|
||||||
value.Wear = 0.01f;
|
|
||||||
value.Seed = 0;
|
|
||||||
|
|
||||||
PlayerInfo playerInfo = new PlayerInfo
|
|
||||||
{
|
|
||||||
UserId = p.UserId,
|
|
||||||
Slot = p.Slot,
|
|
||||||
Index = (int)p.Index,
|
|
||||||
SteamId = p.SteamID.ToString(),
|
|
||||||
Name = p.PlayerName,
|
|
||||||
IpAddress = p.IpAddress?.Split(":")[0]
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!_gBCommandsAllowed || (LifeState_t)p.LifeState != LifeState_t.LIFE_ALIVE ||
|
|
||||||
WeaponSync == null) return;
|
|
||||||
RefreshWeapons(player);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_ = Task.Run(async () => await WeaponSync.SyncWeaponPaintsToDatabase(playerInfo));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Utility.Log($"Error syncing weapon paints: {ex.Message}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// Add skin options to the submenu for the selected weapon
|
// Open the submenu for skin selection of the chosen weapon
|
||||||
if (skinsForSelectedWeapon != null)
|
ChatMenus.OpenMenu(player, skinSubMenu);
|
||||||
{
|
|
||||||
foreach (var skin in skinsForSelectedWeapon)
|
|
||||||
{
|
|
||||||
if (!skin.TryGetValue("paint_name", out var paintNameObj) ||
|
|
||||||
!skin.TryGetValue("paint", out var paintObj)) continue;
|
|
||||||
var paintName = paintNameObj?.ToString();
|
|
||||||
var paint = paintObj?.ToString();
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(paintName) && !string.IsNullOrEmpty(paint))
|
|
||||||
{
|
|
||||||
skinSubMenu?.AddMenuOption($"{paintName} ({paint})", handleSkinSelection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (player != null && Utility.IsPlayerValid(player))
|
|
||||||
skinSubMenu?.Open(player);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add weapon options to the weapon selection menu
|
|
||||||
foreach (var weaponName in WeaponList
|
|
||||||
.Where(kvp => kvp.Key != "weapon_knife")
|
|
||||||
.Select(kvp => kvp.Value))
|
|
||||||
{
|
|
||||||
weaponSelectionMenu?.AddMenuOption(weaponName, handleWeaponSelection);
|
|
||||||
}
|
|
||||||
// Command to open the weapon selection menu for players
|
|
||||||
|
|
||||||
_config.Additional.CommandSkinSelection.ForEach(c =>
|
|
||||||
{
|
|
||||||
AddCommand($"css_{c}", "Skins selection menu", (player, _) =>
|
|
||||||
{
|
|
||||||
if (!Utility.IsPlayerValid(player)) return;
|
|
||||||
|
|
||||||
if (player == null || player.UserId == null) return;
|
|
||||||
|
|
||||||
if (!CommandsCooldown.TryGetValue(player.Slot, out var cooldownEndTime) ||
|
|
||||||
DateTime.UtcNow >= (CommandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
|
|
||||||
{
|
|
||||||
CommandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
|
|
||||||
weaponSelectionMenu?.Open(player);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"]))
|
|
||||||
{
|
|
||||||
player.Print(Localizer["wp_command_cooldown"]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetupGlovesMenu()
|
|
||||||
{
|
|
||||||
var glovesSelectionMenu = MenuApi?.NewMenu(Localizer["wp_glove_menu_title"]);
|
|
||||||
if (glovesSelectionMenu == null) return;
|
|
||||||
glovesSelectionMenu.PostSelectAction = PostSelectAction.Close;
|
|
||||||
|
|
||||||
var handleGloveSelection = (CCSPlayerController? player, ChatMenuOption option) =>
|
|
||||||
{
|
|
||||||
if (!Utility.IsPlayerValid(player) || player is null) return;
|
|
||||||
|
|
||||||
var selectedPaintName = option.Text;
|
|
||||||
|
|
||||||
var selectedGlove = GlovesList.FirstOrDefault(g => g.ContainsKey("paint_name") && g["paint_name"]?.ToString() == selectedPaintName);
|
|
||||||
var image = selectedGlove?["image"]?.ToString() ?? "";
|
|
||||||
if (selectedGlove == null ||
|
|
||||||
!selectedGlove.ContainsKey("weapon_defindex") ||
|
|
||||||
!selectedGlove.ContainsKey("paint") ||
|
|
||||||
!int.TryParse(selectedGlove["weapon_defindex"]?.ToString(), out var weaponDefindex) ||
|
|
||||||
!int.TryParse(selectedGlove["paint"]?.ToString(), out var paint)) return;
|
|
||||||
if (Config.Additional.ShowSkinImage)
|
|
||||||
{
|
|
||||||
_playerWeaponImage[player.Slot] = image;
|
|
||||||
AddTimer(2.0f, () => _playerWeaponImage.Remove(player.Slot), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayerInfo playerInfo = new PlayerInfo
|
|
||||||
{
|
|
||||||
UserId = player.UserId,
|
|
||||||
Slot = player.Slot,
|
|
||||||
Index = (int)player.Index,
|
|
||||||
SteamId = player.SteamID.ToString(),
|
|
||||||
Name = player.PlayerName,
|
|
||||||
IpAddress = player.IpAddress?.Split(":")[0]
|
|
||||||
};
|
|
||||||
|
|
||||||
if (paint != 0)
|
|
||||||
{
|
|
||||||
GPlayersGlove[player.Slot] = (ushort)weaponDefindex;
|
|
||||||
|
|
||||||
if (!GPlayerWeaponsInfo[player.Slot].ContainsKey(weaponDefindex))
|
|
||||||
{
|
|
||||||
WeaponInfo weaponInfo = new()
|
|
||||||
{
|
|
||||||
Paint = paint
|
|
||||||
};
|
|
||||||
GPlayerWeaponsInfo[player.Slot][weaponDefindex] = weaponInfo;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GPlayersGlove.TryRemove(player.Slot, out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_glove_menu_select"]))
|
|
||||||
{
|
|
||||||
player.Print(Localizer["wp_glove_menu_select", selectedPaintName]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WeaponSync == null) return;
|
|
||||||
|
|
||||||
_ = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await WeaponSync.SyncGloveToDatabase(playerInfo, weaponDefindex);
|
|
||||||
|
|
||||||
if (!GPlayerWeaponsInfo[playerInfo.Slot].TryGetValue(weaponDefindex, out var value))
|
|
||||||
{
|
|
||||||
value = new WeaponInfo();
|
|
||||||
GPlayerWeaponsInfo[playerInfo.Slot][weaponDefindex] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
value.Paint = paint;
|
|
||||||
value.Wear = 0.00f;
|
|
||||||
value.Seed = 0;
|
|
||||||
|
|
||||||
await WeaponSync.SyncWeaponPaintsToDatabase(playerInfo);
|
|
||||||
});
|
|
||||||
|
|
||||||
AddTimer(0.1f, () => GivePlayerGloves(player));
|
|
||||||
AddTimer(0.25f, () => GivePlayerGloves(player));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add weapon options to the weapon selection menu
|
// Add weapon options to the weapon selection menu
|
||||||
foreach (var paintName in GlovesList.Select(gloveObject => gloveObject["paint_name"]?.ToString() ?? "").Where(paintName => paintName.Length > 0))
|
foreach (var weaponClass in weaponList.Keys)
|
||||||
{
|
{
|
||||||
glovesSelectionMenu.AddMenuOption(paintName, handleGloveSelection);
|
string weaponName = weaponList[weaponClass];
|
||||||
|
weaponSelectionMenu.AddMenuOption(weaponName, handleWeaponSelection);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Command to open the weapon selection menu for players
|
// Command to open the weapon selection menu for players
|
||||||
_config.Additional.CommandGlove.ForEach(c =>
|
AddCommand($"css_{Config.AdditionalSetting.CommandSkinSelection}", "Skins selection menu", (player, info) =>
|
||||||
{
|
{
|
||||||
AddCommand($"css_{c}", "Gloves selection menu", (player, info) =>
|
if (!Utility.IsPlayerValid(player)) return;
|
||||||
|
|
||||||
|
if (player == null || player.UserId == null) return;
|
||||||
|
|
||||||
|
if (!commandsCooldown.TryGetValue((int)player.UserId, out DateTime cooldownEndTime) ||
|
||||||
|
DateTime.UtcNow >= (commandsCooldown.TryGetValue((int)player.UserId, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
|
||||||
{
|
{
|
||||||
if (!Utility.IsPlayerValid(player) || !_gBCommandsAllowed) return;
|
commandsCooldown[(int)player.UserId] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
|
||||||
|
ChatMenus.OpenMenu(player, weaponSelectionMenu);
|
||||||
if (player == null || player.UserId == null) return;
|
return;
|
||||||
|
|
||||||
if (!CommandsCooldown.TryGetValue(player.Slot, out var cooldownEndTime) ||
|
|
||||||
DateTime.UtcNow >= (CommandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
|
|
||||||
{
|
|
||||||
CommandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
|
|
||||||
glovesSelectionMenu?.Open(player);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"]))
|
|
||||||
{
|
|
||||||
player.Print(Localizer["wp_command_cooldown"]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetupAgentsMenu()
|
|
||||||
{
|
|
||||||
var handleAgentSelection = (CCSPlayerController? player, ChatMenuOption option) =>
|
|
||||||
{
|
|
||||||
if (!Utility.IsPlayerValid(player) || player is null) return;
|
|
||||||
|
|
||||||
var selectedPaintName = option.Text;
|
|
||||||
var selectedAgent = AgentsList.FirstOrDefault(g =>
|
|
||||||
g.ContainsKey("agent_name") &&
|
|
||||||
g["agent_name"] != null && g["agent_name"]!.ToString() == selectedPaintName &&
|
|
||||||
g["team"] != null && (int)(g["team"]!) == player.TeamNum);
|
|
||||||
|
|
||||||
if (selectedAgent == null) return;
|
|
||||||
|
|
||||||
if (
|
|
||||||
selectedAgent.ContainsKey("model")
|
|
||||||
)
|
|
||||||
{
|
|
||||||
PlayerInfo playerInfo = new PlayerInfo
|
|
||||||
{
|
|
||||||
UserId = player.UserId,
|
|
||||||
Slot = player.Slot,
|
|
||||||
Index = (int)player.Index,
|
|
||||||
SteamId = player.SteamID.ToString(),
|
|
||||||
Name = player.PlayerName,
|
|
||||||
IpAddress = player.IpAddress?.Split(":")[0]
|
|
||||||
};
|
|
||||||
|
|
||||||
if (Config.Additional.ShowSkinImage)
|
|
||||||
{
|
|
||||||
var image = selectedAgent["image"]?.ToString() ?? "";
|
|
||||||
_playerWeaponImage[player.Slot] = image;
|
|
||||||
AddTimer(2.0f, () => _playerWeaponImage.Remove(player.Slot), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_agent_menu_select"]))
|
|
||||||
{
|
|
||||||
player.Print(Localizer["wp_agent_menu_select", selectedPaintName]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (player.TeamNum == 3)
|
|
||||||
{
|
|
||||||
GPlayersAgent.AddOrUpdate(player.Slot,
|
|
||||||
key => (selectedAgent["model"]!.ToString().Equals("null") ? null : selectedAgent["model"]!.ToString(), null),
|
|
||||||
(key, oldValue) => (selectedAgent["model"]!.ToString().Equals("null") ? null : selectedAgent["model"]!.ToString(), oldValue.T));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GPlayersAgent.AddOrUpdate(player.Slot,
|
|
||||||
key => (null, selectedAgent["model"]!.ToString().Equals("null") ? null : selectedAgent["model"]!.ToString()),
|
|
||||||
(key, oldValue) => (oldValue.CT, selectedAgent["model"]!.ToString().Equals("null") ? null : selectedAgent["model"]!.ToString())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WeaponSync != null)
|
|
||||||
{
|
|
||||||
_ = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await WeaponSync.SyncAgentToDatabase(playerInfo);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Command to open the weapon selection menu for players
|
|
||||||
_config.Additional.CommandAgent.ForEach(c =>
|
|
||||||
{
|
|
||||||
AddCommand($"css_{c}", "Agents selection menu", (player, info) =>
|
|
||||||
{
|
|
||||||
if (!Utility.IsPlayerValid(player) || !_gBCommandsAllowed) return;
|
|
||||||
|
|
||||||
if (player == null || player.UserId == null) return;
|
|
||||||
|
|
||||||
if (!CommandsCooldown.TryGetValue(player.Slot, out DateTime cooldownEndTime) ||
|
|
||||||
DateTime.UtcNow >= (CommandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
|
|
||||||
{
|
|
||||||
var agentsSelectionMenu = MenuApi?.NewMenu(Localizer["wp_agent_menu_title"]);
|
|
||||||
if (agentsSelectionMenu == null) return;
|
|
||||||
agentsSelectionMenu.PostSelectAction = PostSelectAction.Close;
|
|
||||||
|
|
||||||
var filteredAgents = AgentsList.Where(agentObject =>
|
|
||||||
{
|
|
||||||
if (agentObject["team"]?.Value<int>() is { } teamNum)
|
|
||||||
{
|
|
||||||
return teamNum == player.TeamNum;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add weapon options to the weapon selection menu
|
|
||||||
|
|
||||||
foreach (var agentObject in filteredAgents)
|
|
||||||
{
|
|
||||||
var paintName = agentObject["agent_name"]?.ToString() ?? "";
|
|
||||||
|
|
||||||
if (paintName.Length > 0)
|
|
||||||
agentsSelectionMenu.AddMenuOption(paintName, handleAgentSelection);
|
|
||||||
}
|
|
||||||
|
|
||||||
CommandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
|
|
||||||
agentsSelectionMenu.Open(player);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"]))
|
|
||||||
{
|
|
||||||
player.Print(Localizer["wp_command_cooldown"]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetupMusicMenu()
|
|
||||||
{
|
|
||||||
var musicSelectionMenu = MenuApi?.NewMenu(Localizer["wp_music_menu_title"]);
|
|
||||||
if (musicSelectionMenu == null) return;
|
|
||||||
musicSelectionMenu.PostSelectAction = PostSelectAction.Close;
|
|
||||||
|
|
||||||
var handleMusicSelection = (CCSPlayerController? player, ChatMenuOption option) =>
|
|
||||||
{
|
|
||||||
if (!Utility.IsPlayerValid(player) || player is null) return;
|
|
||||||
|
|
||||||
var selectedPaintName = option.Text;
|
|
||||||
|
|
||||||
var selectedMusic = MusicList.FirstOrDefault(g => g.ContainsKey("name") && g["name"]?.ToString() == selectedPaintName);
|
|
||||||
if (selectedMusic != null)
|
|
||||||
{
|
|
||||||
if (!selectedMusic.ContainsKey("id") ||
|
|
||||||
!selectedMusic.ContainsKey("name") ||
|
|
||||||
!int.TryParse(selectedMusic["id"]?.ToString(), out var paint)) return;
|
|
||||||
var image = selectedMusic["image"]?.ToString() ?? "";
|
|
||||||
if (Config.Additional.ShowSkinImage)
|
|
||||||
{
|
|
||||||
_playerWeaponImage[player.Slot] = image;
|
|
||||||
AddTimer(2.0f, () => _playerWeaponImage.Remove(player.Slot), CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
PlayerInfo playerInfo = new PlayerInfo
|
|
||||||
{
|
|
||||||
UserId = player.UserId,
|
|
||||||
Slot = player.Slot,
|
|
||||||
Index = (int)player.Index,
|
|
||||||
SteamId = player.SteamID.ToString(),
|
|
||||||
Name = player.PlayerName,
|
|
||||||
IpAddress = player.IpAddress?.Split(":")[0]
|
|
||||||
};
|
|
||||||
|
|
||||||
if (paint != 0)
|
|
||||||
{
|
|
||||||
GPlayersMusic[player.Slot] = (ushort)paint;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GPlayersMusic[player.Slot] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_music_menu_select"]))
|
|
||||||
{
|
|
||||||
player.Print(Localizer["wp_music_menu_select", selectedPaintName]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WeaponSync != null)
|
|
||||||
{
|
|
||||||
_ = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await WeaponSync.SyncMusicToDatabase(playerInfo, (ushort)paint);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
//RefreshGloves(player);
|
|
||||||
}
|
}
|
||||||
else
|
if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"]))
|
||||||
{
|
{
|
||||||
PlayerInfo playerInfo = new PlayerInfo
|
player!.Print(Localizer["wp_command_cooldown"]);
|
||||||
{
|
|
||||||
UserId = player.UserId,
|
|
||||||
Slot = player.Slot,
|
|
||||||
Index = (int)player.Index,
|
|
||||||
SteamId = player.SteamID.ToString(),
|
|
||||||
Name = player.PlayerName,
|
|
||||||
IpAddress = player.IpAddress?.Split(":")[0]
|
|
||||||
};
|
|
||||||
|
|
||||||
GPlayersMusic[player.Slot] = 0;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_music_menu_select"]))
|
|
||||||
{
|
|
||||||
player.Print(Localizer["wp_music_menu_select", Localizer["None"]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WeaponSync != null)
|
|
||||||
{
|
|
||||||
_ = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await WeaponSync.SyncMusicToDatabase(playerInfo, 0);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
musicSelectionMenu.AddMenuOption(Localizer["None"], handleMusicSelection);
|
|
||||||
// Add weapon options to the weapon selection menu
|
|
||||||
foreach (var paintName in MusicList.Select(musicObject => musicObject["name"]?.ToString() ?? "").Where(paintName => paintName.Length > 0))
|
|
||||||
{
|
|
||||||
musicSelectionMenu.AddMenuOption(paintName, handleMusicSelection);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command to open the weapon selection menu for players
|
|
||||||
_config.Additional.CommandMusic.ForEach(c =>
|
|
||||||
{
|
|
||||||
AddCommand($"css_{c}", "Music selection menu", (player, info) =>
|
|
||||||
{
|
|
||||||
if (!Utility.IsPlayerValid(player) || !_gBCommandsAllowed) return;
|
|
||||||
|
|
||||||
if (player == null || player.UserId == null) return;
|
|
||||||
|
|
||||||
if (!CommandsCooldown.TryGetValue(player.Slot, out var cooldownEndTime) ||
|
|
||||||
DateTime.UtcNow >= (CommandsCooldown.TryGetValue(player.Slot, out cooldownEndTime) ? cooldownEndTime : DateTime.UtcNow))
|
|
||||||
{
|
|
||||||
CommandsCooldown[player.Slot] = DateTime.UtcNow.AddSeconds(Config.CmdRefreshCooldownSeconds);
|
|
||||||
musicSelectionMenu.Open(player);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!string.IsNullOrEmpty(Localizer["wp_command_cooldown"]))
|
|
||||||
{
|
|
||||||
player.Print(Localizer["wp_command_cooldown"]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
70
Config.cs
70
Config.cs
@@ -3,69 +3,60 @@ using System.Text.Json.Serialization;
|
|||||||
|
|
||||||
namespace WeaponPaints
|
namespace WeaponPaints
|
||||||
{
|
{
|
||||||
public class Additional
|
public class AdditionalSetting
|
||||||
{
|
{
|
||||||
|
|
||||||
|
[JsonPropertyName("UseMetamodAlwaysLegacyModel")]
|
||||||
|
public bool UseMetamodAlwaysLegacyModel { get; set; } = false;
|
||||||
|
|
||||||
|
[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")]
|
|
||||||
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;
|
||||||
|
|
||||||
[JsonPropertyName("CommandWpEnabled")]
|
[JsonPropertyName("MusicKitEnabled")]
|
||||||
|
public bool MusicKitEnabled { get; set; } = true;
|
||||||
|
|
||||||
|
[JsonPropertyName("NameTagEnabled")]
|
||||||
|
public bool NameTagEnabled { get; set; } = true;
|
||||||
|
|
||||||
|
[JsonPropertyName("CommandWpEnabled")]
|
||||||
public bool CommandWpEnabled { get; set; } = true;
|
public bool CommandWpEnabled { get; set; } = true;
|
||||||
|
|
||||||
[JsonPropertyName("CommandKillEnabled")]
|
[JsonPropertyName("CommandKillEnabled")]
|
||||||
public bool CommandKillEnabled { get; set; } = true;
|
public bool CommandKillEnabled { get; set; } = true;
|
||||||
|
|
||||||
[JsonPropertyName("CommandKnife")]
|
[JsonPropertyName("CommandKnife")]
|
||||||
public List<string> CommandKnife { get; set; } = ["knife"];
|
public string CommandKnife { get; set; } = "knife";
|
||||||
|
|
||||||
[JsonPropertyName("CommandMusic")]
|
|
||||||
public List<string> CommandMusic { get; set; } = ["music"];
|
|
||||||
|
|
||||||
[JsonPropertyName("CommandGlove")]
|
|
||||||
public List<string> CommandGlove { get; set; } = ["gloves"];
|
|
||||||
|
|
||||||
[JsonPropertyName("CommandAgent")]
|
|
||||||
public List<string> CommandAgent { get; set; } = ["agents"];
|
|
||||||
|
|
||||||
[JsonPropertyName("CommandSkin")]
|
[JsonPropertyName("CommandSkin")]
|
||||||
public List<string> CommandSkin { get; set; } = ["ws"];
|
public string CommandSkin { get; set; } = "ws";
|
||||||
|
|
||||||
[JsonPropertyName("CommandSkinSelection")]
|
[JsonPropertyName("CommandSkinSelection")]
|
||||||
public List<string> CommandSkinSelection { get; set; } = ["skins"];
|
public string CommandSkinSelection { get; set; } = "skins";
|
||||||
|
|
||||||
[JsonPropertyName("CommandRefresh")]
|
[JsonPropertyName("CommandRefresh")]
|
||||||
public List<string> CommandRefresh { get; set; } = ["wp"];
|
public string CommandRefresh { get; set; } = "wp";
|
||||||
|
|
||||||
[JsonPropertyName("CommandKill")]
|
[JsonPropertyName("CommandKill")]
|
||||||
public List<string> CommandKill { get; set; } = ["kill"];
|
public string CommandKill { get; set; } = "kill";
|
||||||
|
|
||||||
[JsonPropertyName("GiveRandomKnife")]
|
[JsonPropertyName("GiveRandomKnife")]
|
||||||
public bool GiveRandomKnife { get; set; } = false;
|
public bool GiveRandomKnife { get; set; } = false;
|
||||||
|
|
||||||
[JsonPropertyName("GiveRandomSkin")]
|
[JsonPropertyName("GiveRandomSkin")]
|
||||||
public bool GiveRandomSkin { get; set; } = false;
|
public bool GiveRandomSkin { get; set; } = false;
|
||||||
|
[JsonPropertyName("GiveKnifeAfterRemove")]
|
||||||
[JsonPropertyName("ShowSkinImage")]
|
public bool GiveKnifeAfterRemove { get; set; } = false;
|
||||||
public bool ShowSkinImage { get; set; } = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class WeaponPaintsConfig : BasePluginConfig
|
public class WeaponPaintsConfig : BasePluginConfig
|
||||||
{
|
{
|
||||||
[JsonPropertyName("ConfigVersion")] public override int Version { get; set; } = 7;
|
public override int Version { get; set; } = 5;
|
||||||
|
|
||||||
[JsonPropertyName("SkinsLanguage")]
|
|
||||||
public string SkinsLanguage { get; set; } = "en";
|
|
||||||
|
|
||||||
[JsonPropertyName("DatabaseHost")]
|
[JsonPropertyName("DatabaseHost")]
|
||||||
public string DatabaseHost { get; set; } = "";
|
public string DatabaseHost { get; set; } = "";
|
||||||
@@ -82,13 +73,20 @@ namespace WeaponPaints
|
|||||||
[JsonPropertyName("DatabaseName")]
|
[JsonPropertyName("DatabaseName")]
|
||||||
public string DatabaseName { get; set; } = "";
|
public string DatabaseName { get; set; } = "";
|
||||||
|
|
||||||
|
[JsonPropertyName("GlobalShare")]
|
||||||
|
public bool GlobalShare { get; set; } = false;
|
||||||
|
|
||||||
[JsonPropertyName("CmdRefreshCooldownSeconds")]
|
[JsonPropertyName("CmdRefreshCooldownSeconds")]
|
||||||
public int CmdRefreshCooldownSeconds { get; set; } = 10;
|
public int CmdRefreshCooldownSeconds { get; set; } = 60;
|
||||||
|
|
||||||
|
[JsonPropertyName("Prefix")]
|
||||||
|
public string Prefix { get; set; } = "[WeaponPaints]";
|
||||||
|
|
||||||
[JsonPropertyName("Website")]
|
[JsonPropertyName("Website")]
|
||||||
public string Website { get; set; } = "example.com/skins";
|
public string Website { get; set; } = "example.com/skins";
|
||||||
|
|
||||||
[JsonPropertyName("Additionalss")]
|
[JsonPropertyName("AdditionalSetting")]
|
||||||
public Additional Additional { get; set; } = new();
|
public AdditionalSetting AdditionalSetting { get; set; } = new AdditionalSetting();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|||||||
23
Database.cs
23
Database.cs
@@ -1,23 +0,0 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using MySqlConnector;
|
|
||||||
|
|
||||||
namespace WeaponPaints
|
|
||||||
{
|
|
||||||
public class Database(string dbConnectionString)
|
|
||||||
{
|
|
||||||
public async Task<MySqlConnection> GetConnectionAsync()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
470
Events.cs
470
Events.cs
@@ -1,250 +1,352 @@
|
|||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Core.Attributes.Registration;
|
|
||||||
using CounterStrikeSharp.API.Modules.Entities;
|
using CounterStrikeSharp.API.Modules.Entities;
|
||||||
using CounterStrikeSharp.API.Modules.Memory;
|
using System.Numerics;
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace WeaponPaints
|
namespace WeaponPaints
|
||||||
{
|
{
|
||||||
public partial class WeaponPaints
|
public partial class WeaponPaints
|
||||||
{
|
{
|
||||||
[GameEventHandler]
|
private void OnClientPutInServer(int playerSlot)
|
||||||
public HookResult OnClientFullConnect(EventPlayerConnectFull @event, GameEventInfo info)
|
|
||||||
{
|
{
|
||||||
CCSPlayerController? player = @event.Userid;
|
CCSPlayerController? player = Utilities.GetPlayerFromSlot(playerSlot);
|
||||||
|
|
||||||
if (player is null || !player.IsValid || player.IsBot ||
|
PlayerInfo playerInfo = new PlayerInfo
|
||||||
WeaponSync == null || Database == null) return HookResult.Continue;
|
|
||||||
|
|
||||||
var playerInfo = new PlayerInfo
|
|
||||||
{
|
{
|
||||||
UserId = player.UserId,
|
UserId = player.UserId,
|
||||||
Slot = player.Slot,
|
|
||||||
Index = (int)player.Index,
|
Index = (int)player.Index,
|
||||||
SteamId = player.SteamID.ToString(),
|
SteamId = player.SteamID,
|
||||||
Name = player.PlayerName,
|
Name = player?.PlayerName,
|
||||||
IpAddress = player.IpAddress?.Split(":")[0]
|
IpAddress = player?.IpAddress?.Split(":")[0]
|
||||||
};
|
};
|
||||||
|
|
||||||
try
|
if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || weaponSync == null) return;
|
||||||
{
|
|
||||||
_ = Task.Run(async () => await WeaponSync.GetPlayerData(playerInfo));
|
|
||||||
/*
|
|
||||||
if (Config.Additional.SkinEnabled)
|
|
||||||
{
|
|
||||||
_ = Task.Run(async () => await weaponSync.GetWeaponPaintsFromDatabase(playerInfo));
|
|
||||||
}
|
|
||||||
if (Config.Additional.KnifeEnabled)
|
|
||||||
{
|
|
||||||
_ = Task.Run(async () => await weaponSync.GetKnifeFromDatabase(playerInfo));
|
|
||||||
}
|
|
||||||
if (Config.Additional.GloveEnabled)
|
|
||||||
{
|
|
||||||
_ = Task.Run(async () => await weaponSync.GetGloveFromDatabase(playerInfo));
|
|
||||||
}
|
|
||||||
if (Config.Additional.AgentEnabled)
|
|
||||||
{
|
|
||||||
_ = Task.Run(async () => await weaponSync.GetAgentFromDatabase(playerInfo));
|
|
||||||
}
|
|
||||||
if (Config.Additional.MusicEnabled)
|
|
||||||
{
|
|
||||||
_ = Task.Run(async () => await weaponSync.GetMusicFromDatabase(playerInfo));
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
return HookResult.Continue;
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await weaponSync.GetPlayerDatabaseIndex(playerInfo);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnClientDisconnect(int playerSlot)
|
||||||
|
{
|
||||||
|
CCSPlayerController player = Utilities.GetPlayerFromSlot(playerSlot);
|
||||||
|
|
||||||
|
if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || player.UserId == null) return;
|
||||||
|
|
||||||
|
g_playersDatabaseIndex.TryRemove((int)player.Index, out _);
|
||||||
|
if (Config.AdditionalSetting.KnifeEnabled)
|
||||||
|
g_playersKnife.TryRemove((int)player.Index, out _);
|
||||||
|
if (Config.AdditionalSetting.SkinEnabled)
|
||||||
|
{
|
||||||
|
if (gPlayerWeaponsInfo.TryRemove((int)player.Index, out var innerDictionary))
|
||||||
|
{
|
||||||
|
innerDictionary.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Config.AdditionalSetting.MusicKitEnabled)
|
||||||
|
g_playersMusicKit.TryRemove((int)player.Index, out _);
|
||||||
|
if (commandsCooldown.ContainsKey((int)player.UserId))
|
||||||
|
{
|
||||||
|
commandsCooldown.Remove((int)player.UserId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[GameEventHandler]
|
private void OnEntityCreated(CEntityInstance entity)
|
||||||
public HookResult OnPlayerDisconnect(EventPlayerDisconnect @event, GameEventInfo info)
|
{
|
||||||
|
if (!Config.AdditionalSetting.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;
|
||||||
|
var pawn = new CBasePlayerPawn(NativeAPI.GetEntityFromIndex(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) { }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private HookResult OnEventItemPurchasePost(EventItemPurchase @event, GameEventInfo info)
|
||||||
{
|
{
|
||||||
CCSPlayerController? player = @event.Userid;
|
CCSPlayerController? player = @event.Userid;
|
||||||
|
|
||||||
if (player is null || !player.IsValid || player.IsBot) return HookResult.Continue;
|
if (player == null || !player.IsValid) return HookResult.Continue;
|
||||||
|
|
||||||
if (Config.Additional.SkinEnabled)
|
/*
|
||||||
{
|
if (Config.AdditionalSetting.SkinVisibilityFix)
|
||||||
GPlayerWeaponsInfo.TryRemove(player.Slot, out _);
|
AddTimer(0.2f, () => RefreshSkins(player));
|
||||||
}
|
*/
|
||||||
if (Config.Additional.KnifeEnabled)
|
|
||||||
{
|
|
||||||
GPlayersKnife.TryRemove(player.Slot, out _);
|
|
||||||
}
|
|
||||||
if (Config.Additional.GloveEnabled)
|
|
||||||
{
|
|
||||||
GPlayersGlove.TryRemove(player.Slot, out _);
|
|
||||||
}
|
|
||||||
if (Config.Additional.AgentEnabled)
|
|
||||||
{
|
|
||||||
GPlayersAgent.TryRemove(player.Slot, out _);
|
|
||||||
}
|
|
||||||
if (Config.Additional.MusicEnabled)
|
|
||||||
{
|
|
||||||
GPlayersMusic.TryRemove(player.Slot, out _);
|
|
||||||
}
|
|
||||||
|
|
||||||
_temporaryPlayerWeaponWear.TryRemove(player.Slot, out _);
|
|
||||||
|
|
||||||
CommandsCooldown.Remove(player.Slot);
|
|
||||||
|
|
||||||
return HookResult.Continue;
|
return HookResult.Continue;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
private void OnMapStart(string mapName)
|
private HookResult OnItemPickup(EventItemPickup @event, GameEventInfo info)
|
||||||
{
|
{
|
||||||
if (Config.Additional is { KnifeEnabled: false, SkinEnabled: false, GloveEnabled: false }) return;
|
if (@event.Defindex == 42 || @event.Defindex == 59)
|
||||||
|
{
|
||||||
|
CCSPlayerController? player = @event.Userid;
|
||||||
|
if (player == null || !player.IsValid || !g_knifePickupCount.ContainsKey((int)player.Index) || player.IsBot || !g_playersKnife.ContainsKey((int)player.Index))
|
||||||
|
return HookResult.Continue;
|
||||||
|
|
||||||
if (Database != null)
|
|
||||||
WeaponSync = new WeaponSynchronization(Database, Config);
|
if (g_knifePickupCount[(int)player.Index] >= 2) return HookResult.Continue;
|
||||||
|
|
||||||
|
if (g_playersKnife.ContainsKey((int)player.Index)
|
||||||
|
&&
|
||||||
|
g_playersKnife[(int)player.Index] != "weapon_knife")
|
||||||
|
{
|
||||||
|
g_knifePickupCount[(int)player.Index]++;
|
||||||
|
|
||||||
|
RemovePlayerKnife(player, true);
|
||||||
|
|
||||||
|
if (!PlayerHasKnife(player) && Config.AdditionalSetting.GiveKnifeAfterRemove)
|
||||||
|
AddTimer(0.3f, () => GiveKnifeToPlayer(player));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return HookResult.Continue;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
public HookResult OnPickup(CEntityIOOutput output, string name, CEntityInstance activator, CEntityInstance caller, CVariant value, float delay)
|
||||||
|
{
|
||||||
|
CCSPlayerController? player = Utilities.GetEntityFromIndex<CCSPlayerPawn>((int)activator.Index).OriginalController.Value;
|
||||||
|
|
||||||
|
if (player == null || player.IsBot || player.IsHLTV)
|
||||||
|
return HookResult.Continue;
|
||||||
|
|
||||||
|
if (player == null || !player.IsValid || player.SteamID.ToString() == "" ||
|
||||||
|
!g_knifePickupCount.ContainsKey((int)player.Index) || !g_playersKnife.ContainsKey((int)player.Index))
|
||||||
|
return HookResult.Continue;
|
||||||
|
|
||||||
|
CBasePlayerWeapon weapon = new(caller.Handle);
|
||||||
|
|
||||||
|
if (weapon.AttributeManager.Item.ItemDefinitionIndex != 42 && weapon.AttributeManager.Item.ItemDefinitionIndex != 59)
|
||||||
|
return HookResult.Continue;
|
||||||
|
|
||||||
|
if (g_knifePickupCount[(int)player.Index] >= 2) return HookResult.Continue;
|
||||||
|
|
||||||
|
if (g_playersKnife[(int)player.Index] != "weapon_knife")
|
||||||
|
{
|
||||||
|
g_knifePickupCount[(int)player.Index]++;
|
||||||
|
player.RemoveItemByDesignerName(weapon.DesignerName);
|
||||||
|
if (Config.AdditionalSetting.GiveKnifeAfterRemove)
|
||||||
|
AddTimer(0.2f, () => GiveKnifeToPlayer(player));
|
||||||
|
}
|
||||||
|
|
||||||
|
return HookResult.Continue;
|
||||||
|
}
|
||||||
|
private void OnMapStart(string mapName)
|
||||||
|
{
|
||||||
|
if (!Config.AdditionalSetting.KnifeEnabled) return;
|
||||||
|
// 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");
|
||||||
|
|
||||||
|
if (Config.GlobalShare)
|
||||||
|
GlobalShareConnect();
|
||||||
|
|
||||||
|
weaponSync = new WeaponSynchronization(DatabaseConnectionString, Config, GlobalShareApi, GlobalShareServerId);
|
||||||
|
});
|
||||||
|
|
||||||
|
g_hTimerCheckSkinsData = AddTimer(10.0f, () =>
|
||||||
|
{
|
||||||
|
List<CCSPlayerController> players = Utilities.GetPlayers();
|
||||||
|
HashSet<int> playerIndexes = new HashSet<int>(gPlayerWeaponsInfo.Keys);
|
||||||
|
foreach (CCSPlayerController player in players)
|
||||||
|
{
|
||||||
|
if (player.IsBot || player.IsHLTV || player.SteamID.ToString() == "") continue;
|
||||||
|
//if (gPlayerWeaponsInfo.ContainsKey((int)player.Index)) continue;
|
||||||
|
if (playerIndexes.Contains((int)player.Index)) continue;
|
||||||
|
|
||||||
|
PlayerInfo playerInfo = new PlayerInfo
|
||||||
|
{
|
||||||
|
UserId = player.UserId,
|
||||||
|
Index = (int)player.Index,
|
||||||
|
SteamId = player?.SteamID,
|
||||||
|
Name = player?.PlayerName,
|
||||||
|
IpAddress = player?.IpAddress?.Split(":")[0]
|
||||||
|
};
|
||||||
|
if (weaponSync != null)
|
||||||
|
_ = weaponSync.GetKnifeFromDatabase(playerInfo);
|
||||||
|
}
|
||||||
|
}, CounterStrikeSharp.API.Modules.Timers.TimerFlags.STOP_ON_MAPCHANGE | CounterStrikeSharp.API.Modules.Timers.TimerFlags.REPEAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info)
|
||||||
|
{
|
||||||
|
CCSPlayerController? player = @event.Userid;
|
||||||
|
|
||||||
|
if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || weaponSync == null) return HookResult.Continue;
|
||||||
|
|
||||||
|
PlayerInfo playerInfo = new PlayerInfo
|
||||||
|
{
|
||||||
|
UserId = player.UserId,
|
||||||
|
Index = (int)player.Index,
|
||||||
|
SteamId = player?.SteamID,
|
||||||
|
Name = player?.PlayerName,
|
||||||
|
IpAddress = player?.IpAddress?.Split(":")[0]
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!g_playersDatabaseIndex.ContainsKey((int)player!.Index))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"[WeaponPaints] Retrying to retrieve player {player.PlayerName} skins");
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await weaponSync.GetPlayerDatabaseIndex(playerInfo);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return HookResult.Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info)
|
private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info)
|
||||||
{
|
{
|
||||||
CCSPlayerController? player = @event.Userid;
|
CCSPlayerController? player = @event.Userid;
|
||||||
|
if (player == null || !player.IsValid)
|
||||||
if (player is null || !player.IsValid || Config.Additional is { KnifeEnabled: false, GloveEnabled: false })
|
{
|
||||||
return HookResult.Continue;
|
return HookResult.Continue;
|
||||||
|
}
|
||||||
|
|
||||||
CCSPlayerPawn? pawn = player.PlayerPawn.Value;
|
if (Config.AdditionalSetting.KnifeEnabled && !PlayerHasKnife(player))
|
||||||
|
{
|
||||||
|
g_knifePickupCount[(int)player.Index] = 0;
|
||||||
|
GiveKnifeToPlayer(player);
|
||||||
|
//AddTimer(0.1f, () => GiveKnifeToPlayer(player));
|
||||||
|
}
|
||||||
|
|
||||||
if (pawn == null || !pawn.IsValid)
|
/*
|
||||||
return HookResult.Continue;
|
if (Config.AdditionalSetting.SkinVisibilityFix)
|
||||||
|
{
|
||||||
GivePlayerMusicKit(player);
|
AddTimer(0.3f, () => RefreshSkins(player));
|
||||||
GivePlayerAgent(player);
|
}
|
||||||
GivePlayerGloves(player);
|
*/
|
||||||
|
|
||||||
return HookResult.Continue;
|
return HookResult.Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info)
|
private HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info)
|
||||||
{
|
{
|
||||||
_gBCommandsAllowed = false;
|
g_bCommandsAllowed = false;
|
||||||
|
|
||||||
return HookResult.Continue;
|
return HookResult.Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info)
|
private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info)
|
||||||
{
|
{
|
||||||
_gBCommandsAllowed = true;
|
NativeAPI.IssueServerCommand("mp_t_default_melee \"\"");
|
||||||
return HookResult.Continue;
|
NativeAPI.IssueServerCommand("mp_ct_default_melee \"\"");
|
||||||
}
|
NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0");
|
||||||
|
|
||||||
public HookResult OnGiveNamedItemPost(DynamicHook hook)
|
g_bCommandsAllowed = true;
|
||||||
{
|
|
||||||
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 SteamID(weapon.OriginalOwnerXuidLow);
|
|
||||||
|
|
||||||
CCSPlayerController? player;
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnTick()
|
private void OnTick()
|
||||||
{
|
{
|
||||||
if (!Config.Additional.ShowSkinImage) return;
|
foreach (var player in Utilities.GetPlayers())
|
||||||
|
|
||||||
foreach (var player in Utilities.GetPlayers().Where(p =>
|
|
||||||
p is { IsValid: true, PlayerPawn.IsValid: true } &&
|
|
||||||
(LifeState_t)p.LifeState == LifeState_t.LIFE_ALIVE
|
|
||||||
&& !p.IsBot && p is { Connected: PlayerConnectedState.PlayerConnected }
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
if (_playerWeaponImage.TryGetValue(player.Slot, out var value) && !string.IsNullOrEmpty(value))
|
try
|
||||||
{
|
{
|
||||||
player.PrintToCenterHtml("<img src='{PATH}'</img>".Replace("{PATH}", value));
|
if (player == null || !player.IsValid || !player.PawnIsAlive || player.IsBot || player.IsHLTV) continue;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[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;
|
var viewModels = GetPlayerViewModels(player);
|
||||||
|
|
||||||
|
if (viewModels == null) continue;
|
||||||
|
|
||||||
|
var viewModel = viewModels[0];
|
||||||
|
if (viewModel == null || viewModel.Value == null || viewModel.Value.Weapon == null || viewModel.Value.Weapon.Value == null) continue;
|
||||||
|
CBasePlayerWeapon weapon = viewModel.Value.Weapon.Value;
|
||||||
|
|
||||||
|
if (weapon == null || !weapon.IsValid) continue;
|
||||||
|
|
||||||
|
var isKnife = viewModel.Value.VMName.Contains("knife");
|
||||||
|
|
||||||
|
if (!isKnife)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
viewModel.Value.CBodyComponent != null
|
||||||
|
&& viewModel.Value.CBodyComponent.SceneNode != null
|
||||||
|
&& weapon.CBodyComponent!.SceneNode!.GetSkeletonInstance().ModelState.MeshGroupMask != 2
|
||||||
|
)
|
||||||
|
{
|
||||||
|
weapon.CBodyComponent!.SceneNode!.GetSkeletonInstance().ModelState.MeshGroupMask = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Utilities.SetStateChanged(viewModel.Value, "CBaseEntity", "m_CBodyComponent");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RegisterListeners()
|
private void RegisterListeners()
|
||||||
{
|
{
|
||||||
|
RegisterListener<Listeners.OnEntityCreated>(OnEntityCreated);
|
||||||
|
RegisterListener<Listeners.OnClientPutInServer>(OnClientPutInServer);
|
||||||
|
RegisterListener<Listeners.OnClientDisconnect>(OnClientDisconnect);
|
||||||
RegisterListener<Listeners.OnMapStart>(OnMapStart);
|
RegisterListener<Listeners.OnMapStart>(OnMapStart);
|
||||||
|
|
||||||
RegisterEventHandler<EventPlayerSpawn>(OnPlayerSpawn);
|
if (!Config.AdditionalSetting.UseMetamodAlwaysLegacyModel)
|
||||||
RegisterEventHandler<EventRoundStart>(OnRoundStart);
|
|
||||||
RegisterEventHandler<EventRoundEnd>(OnRoundEnd);
|
|
||||||
RegisterListener<Listeners.OnEntityCreated>(OnEntityCreated);
|
|
||||||
|
|
||||||
if (Config.Additional.ShowSkinImage)
|
|
||||||
RegisterListener<Listeners.OnTick>(OnTick);
|
RegisterListener<Listeners.OnTick>(OnTick);
|
||||||
|
|
||||||
if (!IsWindows)
|
RegisterEventHandler<EventPlayerConnectFull>(OnPlayerConnectFull);
|
||||||
VirtualFunctions.GiveNamedItemFunc.Hook(OnGiveNamedItemPost, HookMode.Post);
|
RegisterEventHandler<EventPlayerSpawn>(OnPlayerSpawn);
|
||||||
|
RegisterEventHandler<EventRoundStart>(OnRoundStart, HookMode.Pre);
|
||||||
|
RegisterEventHandler<EventRoundEnd>(OnRoundEnd);
|
||||||
|
RegisterEventHandler<EventItemPurchase>(OnEventItemPurchasePost);
|
||||||
|
//RegisterEventHandler<EventItemPickup>(OnItemPickup);
|
||||||
|
HookEntityOutput("weapon_knife", "OnPlayerPickup", OnPickup, HookMode.Pre);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* WORKAROUND FOR CLIENTS WITHOUT STEAMID ON AUTHORIZATION */
|
||||||
|
/*private HookResult OnPlayerConnectFull(EventPlayerConnectFull @event, GameEventInfo info)
|
||||||
|
{
|
||||||
|
CCSPlayerController? player = @event.Userid;
|
||||||
|
|
||||||
|
if (player == null || !player.IsValid || !player.EntityIndex.HasValue || player.IsHLTV) return HookResult.Continue;
|
||||||
|
|
||||||
|
int playerIndex = (int)player.EntityIndex.Value.Value;
|
||||||
|
if (Config.AdditionalSetting.SkinEnabled && weaponSync != null)
|
||||||
|
_ = weaponSync.GetWeaponPaintsFromDatabase(playerIndex);
|
||||||
|
if (Config.AdditionalSetting.KnifeEnabled && weaponSync != null)
|
||||||
|
_ = weaponSync.GetKnifeFromDatabase(playerIndex);
|
||||||
|
|
||||||
|
Task.Run(async () =>
|
||||||
|
{
|
||||||
|
if (Config.AdditionalSetting.SkinEnabled && weaponSync != null)
|
||||||
|
if (Config.AdditionalSetting.KnifeEnabled && weaponSync != null)
|
||||||
|
});
|
||||||
|
|
||||||
|
return HookResult.Continue;
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,6 @@ public static class PlayerExtensions
|
|||||||
public static void Print(this CCSPlayerController controller, string message)
|
public static void Print(this CCSPlayerController controller, string message)
|
||||||
{
|
{
|
||||||
if (WeaponPaints._localizer == null) return;
|
if (WeaponPaints._localizer == null) return;
|
||||||
|
|
||||||
StringBuilder _message = new(WeaponPaints._localizer["wp_prefix"]);
|
StringBuilder _message = new(WeaponPaints._localizer["wp_prefix"]);
|
||||||
_message.Append(message);
|
_message.Append(message);
|
||||||
controller.PrintToChat(_message.ToString());
|
controller.PrintToChat(_message.ToString());
|
||||||
|
|||||||
@@ -3,10 +3,9 @@
|
|||||||
public class PlayerInfo
|
public class PlayerInfo
|
||||||
{
|
{
|
||||||
public int Index { get; set; }
|
public int Index { get; set; }
|
||||||
public int Slot { get; init; }
|
|
||||||
public int? UserId { get; set; }
|
public int? UserId { get; set; }
|
||||||
public string? SteamId { get; init; }
|
public ulong? SteamId { get; set; }
|
||||||
public string? Name { get; set; }
|
public string? Name { get; set; }
|
||||||
public string? IpAddress { get; set; }
|
public string? IpAddress { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
44
README.md
44
README.md
@@ -9,42 +9,34 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
|
|||||||
[](https://ko-fi.com/E1E2G0P2O) or [](https://steamcommunity.com/tradeoffer/new/?partner=41515647&token=gW2W-nXE)
|
[](https://ko-fi.com/E1E2G0P2O) or [](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 or global website, so you dont need MySQL/Website
|
||||||
- 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
|
||||||
|
|
||||||
## ⚙️ Requirements
|
**GlobalShare** - global website accessible at [weaponpaints.fun](https://weaponpaints.fun/)
|
||||||
**Ensure all the following dependencies are installed before proceeding**
|
|
||||||
- [CounterStrikeSharp](https://github.com/roflmuffin/CounterStrikeSharp)
|
|
||||||
- [PlayerSettings](https://github.com/NickFox007/PlayerSettingsCS2) - Required by MenuManagerCS2
|
|
||||||
- [AnyBaseLibCS2](https://github.com/NickFox007/AnyBaseLibCS2) - Required by PlayerSettings
|
|
||||||
- [MenuManagerCS2](https://github.com/NickFox007/MenuManagerCS2)
|
|
||||||
- MySQL database
|
|
||||||
|
|
||||||
## CS2 Server
|
## CS2 Server
|
||||||
- Have working CounterStrikeSharp (**with RUNTIME!**)
|
- Have working CounterStrikeSharp (**with RUNTIME!**)
|
||||||
- Download from Release and copy plugin to plugins
|
- Download from Release and copy plugin to plugins
|
||||||
- Run server with plugin, **it will generate config if installed correctly!**
|
- Setup `addons/counterstrikesharp/configs/`**`plugins/WeaponPaints/WeaponPaints.json`** set **`GlobalShare`** to **`true`** for global, or 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>
|
||||||
<summary>Click to expand</summary>
|
<summary>Click to expand</summary>
|
||||||
<code><pre>{
|
<code><pre>{
|
||||||
"Version": 4, // Don't touch
|
"Version": 4, // Don't touch
|
||||||
"DatabaseHost": "", // MySQL host
|
"DatabaseHost": "", // MySQL host (required if GlobalShare = false)
|
||||||
"DatabasePort": 3306, // MySQL port
|
"DatabasePort": 3306, // MySQL port (required if GlobalShare = false)
|
||||||
"DatabaseUser": "", // MySQL username
|
"DatabaseUser": "", // MySQL username (required if GlobalShare = false)
|
||||||
"DatabasePassword": "", // MySQL user password
|
"DatabasePassword": "", // MySQL user password (required if GlobalShare = false)
|
||||||
"DatabaseName": "", // MySQL database name
|
"DatabaseName": "", // MySQL database name (required if GlobalShare = false)
|
||||||
|
"GlobalShare": false, // Enable or disable GlobalShare, plugin can work without mysql credentials but with shared website at weaponpaints.fun
|
||||||
"CmdRefreshCooldownSeconds": 60, // Cooldown time in refreshing skins (!wp command)
|
"CmdRefreshCooldownSeconds": 60, // Cooldown time in refreshing skins (!wp command)
|
||||||
"Prefix": "[WeaponPaints]", // Prefix every chat message
|
"Prefix": "[WeaponPaints]", // Prefix every chat message
|
||||||
"Website": "example.com/skins", // Website used in WebsiteMessageCommand (!ws command)
|
"Website": "example.com/skins", // Website used in WebsiteMessageCommand (!ws command)
|
||||||
@@ -62,6 +54,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
|
||||||
@@ -74,11 +67,14 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
|
|||||||
"GiveRandomKnife": false, // Give random knife to players if they didn't choose
|
"GiveRandomKnife": false, // Give random knife to players if they didn't choose
|
||||||
"GiveRandomSkins": false // Give random skins to players if they didn't choose
|
"GiveRandomSkins": false // Give random skins to players if they didn't choose
|
||||||
},
|
},
|
||||||
</pre></code>
|
|
||||||
|
"ConfigVersion": 4 // Don't touch
|
||||||
|
}</pre></code>
|
||||||
</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)***
|
Disregard if the config is **`GlobalShare = true`**
|
||||||
|
- 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)
|
||||||
@@ -90,6 +86,10 @@ 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.
|
||||||
|
- Can cause incompatibility with plugins/maps which manipulates weapons and knives
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
<details>
|
<details>
|
||||||
**Skins are not changing:**
|
**Skins are not changing:**
|
||||||
@@ -98,6 +98,8 @@ Set FollowCSGOGuidelines to false in cssharp’s 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
|
||||||
</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/)
|
||||||
|
|||||||
247
Utility.cs
247
Utility.cs
@@ -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 MySqlConnector;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace WeaponPaints
|
namespace WeaponPaints
|
||||||
{
|
{
|
||||||
@@ -11,71 +13,45 @@ namespace WeaponPaints
|
|||||||
{
|
{
|
||||||
internal static WeaponPaintsConfig? Config { get; set; }
|
internal static WeaponPaintsConfig? Config { get; set; }
|
||||||
|
|
||||||
internal static async Task CheckDatabaseTables()
|
internal static string BuildDatabaseConnectionString()
|
||||||
{
|
{
|
||||||
if (WeaponPaints.Database is null) return;
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
return builder.ConnectionString;
|
||||||
|
}
|
||||||
|
internal static async void CheckDatabaseTables()
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await using var connection = await WeaponPaints.Database.GetConnectionAsync();
|
using var connection = new MySqlConnection(BuildDatabaseConnectionString());
|
||||||
|
await connection.OpenAsync();
|
||||||
|
|
||||||
await using var transaction = await connection.BeginTransactionAsync();
|
using var transaction = await connection.BeginTransactionAsync();
|
||||||
|
|
||||||
try
|
// Minimal version for MySQL 5.6.5
|
||||||
|
string[] sqlCommands = new string[]
|
||||||
|
{
|
||||||
|
"CREATE TABLE IF NOT EXISTS `wp_users` (`id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `steamid` BIGINT UNSIGNED NOT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `unique_steamid` (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
|
||||||
|
"CREATE TABLE IF NOT EXISTS `wp_users_items` (`user_id` INT 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, `quality` SMALLINT UNSIGNED NOT NULL DEFAULT 0, PRIMARY KEY (`user_id`,`weapon`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
|
||||||
|
"CREATE TABLE IF NOT EXISTS `wp_users_knife` (`user_id` INT UNSIGNED NOT NULL, `knife` VARCHAR(32) DEFAULT NULL, PRIMARY KEY (`user_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
|
||||||
|
"CREATE TABLE IF NOT EXISTS `wp_users_music` (`user_id` INT UNSIGNED NOT NULL, `music` SMALLINT UNSIGNED DEFAULT NULL, PRIMARY KEY (`user_id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"
|
||||||
|
};
|
||||||
|
try
|
||||||
{
|
{
|
||||||
string[] createTableQueries =
|
foreach (string command in sqlCommands)
|
||||||
[
|
{
|
||||||
"""
|
await connection.ExecuteAsync(command, transaction: transaction);
|
||||||
CREATE TABLE IF NOT EXISTS `wp_player_skins` (
|
}
|
||||||
`steamid` varchar(18) NOT NULL,
|
|
||||||
`weapon_defindex` int(6) NOT NULL,
|
|
||||||
`weapon_paint_id` int(6) NOT NULL,
|
|
||||||
`weapon_wear` float NOT NULL DEFAULT 0.000001,
|
|
||||||
`weapon_seed` int(16) NOT NULL DEFAULT 0,
|
|
||||||
`weapon_nametag` VARCHAR(128) DEFAULT NULL,
|
|
||||||
`weapon_sticker_0` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation',
|
|
||||||
`weapon_sticker_1` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation',
|
|
||||||
`weapon_sticker_2` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation',
|
|
||||||
`weapon_sticker_3` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation',
|
|
||||||
`weapon_sticker_4` VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0;0;0' COMMENT 'id;schema;x;y;wear;scale;rotation',
|
|
||||||
`weapon_keychain`VARCHAR(128) NOT NULL DEFAULT '0;0;0;0;0' COMMENT 'id;x;y;z;seed'
|
|
||||||
) ENGINE=InnoDB
|
|
||||||
""",
|
|
||||||
@"CREATE TABLE IF NOT EXISTS `wp_player_knife` (
|
|
||||||
`steamid` varchar(18) NOT NULL,
|
|
||||||
`knife` varchar(64) NOT NULL,
|
|
||||||
UNIQUE (`steamid`)
|
|
||||||
) ENGINE = InnoDB",
|
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS `wp_player_gloves` (
|
|
||||||
`steamid` varchar(18) NOT NULL,
|
|
||||||
`weapon_defindex` int(11) NOT NULL,
|
|
||||||
UNIQUE (`steamid`)
|
|
||||||
) ENGINE=InnoDB
|
|
||||||
""",
|
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS `wp_player_agents` (
|
|
||||||
`steamid` varchar(18) NOT NULL,
|
|
||||||
`agent_ct` varchar(64) DEFAULT NULL,
|
|
||||||
`agent_t` varchar(64) DEFAULT NULL,
|
|
||||||
UNIQUE (`steamid`)
|
|
||||||
) ENGINE=InnoDB
|
|
||||||
""",
|
|
||||||
"""
|
|
||||||
CREATE TABLE IF NOT EXISTS `wp_player_music` (
|
|
||||||
`steamid` varchar(64) NOT NULL,
|
|
||||||
`music_id` int(11) NOT NULL,
|
|
||||||
UNIQUE (`steamid`)
|
|
||||||
) ENGINE=InnoDB
|
|
||||||
""",
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach (var query in createTableQueries)
|
await transaction.CommitAsync();
|
||||||
{
|
|
||||||
await connection.ExecuteAsync(query, transaction: transaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
await transaction.CommitAsync();
|
|
||||||
}
|
}
|
||||||
catch (Exception)
|
catch (Exception)
|
||||||
{
|
{
|
||||||
@@ -85,70 +61,25 @@ namespace WeaponPaints
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
throw new Exception("[WeaponPaints] Unknown MySQL exception! " + ex.Message);
|
throw new Exception("[WeaponPaints] Unknown mysql exception! " + ex.Message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsPlayerValid(CCSPlayerController? player)
|
internal static bool IsPlayerValid(CCSPlayerController? player)
|
||||||
{
|
{
|
||||||
if (player is null || WeaponPaints.WeaponSync is null) return false;
|
return (player != null && player.IsValid && !player.IsBot && !player.IsHLTV && player.AuthorizedSteamID != null);
|
||||||
|
|
||||||
return player is { IsValid: true, IsBot: false, IsHLTV: false, UserId: not null };
|
|
||||||
}
|
}
|
||||||
|
internal static void LoadSkinsFromFile(string filePath)
|
||||||
internal static void LoadSkinsFromFile(string filePath, ILogger logger)
|
|
||||||
{
|
{
|
||||||
var json = File.ReadAllText(filePath);
|
if (File.Exists(filePath))
|
||||||
try
|
|
||||||
{
|
{
|
||||||
|
string json = File.ReadAllText(filePath);
|
||||||
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
|
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
|
||||||
WeaponPaints.SkinsList = deserializedSkins ?? [];
|
WeaponPaints.skinsList = deserializedSkins ?? new List<JObject>();
|
||||||
}
|
}
|
||||||
catch (FileNotFoundException)
|
else
|
||||||
{
|
{
|
||||||
logger?.LogError("Not found \"skins.json\" file");
|
throw new FileNotFoundException("File not found.", filePath);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void LoadGlovesFromFile(string filePath, ILogger logger)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var json = File.ReadAllText(filePath);
|
|
||||||
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
|
|
||||||
WeaponPaints.GlovesList = deserializedSkins ?? [];
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException)
|
|
||||||
{
|
|
||||||
logger?.LogError("Not found \"gloves.json\" file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void LoadAgentsFromFile(string filePath, ILogger logger)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var json = File.ReadAllText(filePath);
|
|
||||||
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
|
|
||||||
WeaponPaints.AgentsList = deserializedSkins ?? [];
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException)
|
|
||||||
{
|
|
||||||
logger?.LogError("Not found \"agents.json\" file");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void LoadMusicFromFile(string filePath, ILogger logger)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var json = File.ReadAllText(filePath);
|
|
||||||
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
|
|
||||||
WeaponPaints.MusicList = deserializedSkins ?? [];
|
|
||||||
}
|
|
||||||
catch (FileNotFoundException)
|
|
||||||
{
|
|
||||||
logger?.LogError("Not found \"music.json\" file");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,50 +93,65 @@ 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");
|
||||||
remoteVersion = remoteVersion.Trim();
|
|
||||||
|
|
||||||
var comparisonResult = string.CompareOrdinal(version, remoteVersion);
|
if (response.IsSuccessStatusCode)
|
||||||
|
|
||||||
switch (comparisonResult)
|
|
||||||
{
|
{
|
||||||
case < 0:
|
string remoteVersion = await response.Content.ReadAsStringAsync();
|
||||||
|
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 (Exception ex)
|
||||||
{
|
{
|
||||||
logger.LogWarning("Failed to check version");
|
Console.WriteLine(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (HttpRequestException ex)
|
|
||||||
{
|
|
||||||
logger.LogError(ex, "Failed to connect to the version server.");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
logger.LogError(ex, "An error occurred while checking version.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void ShowAd(string moduleVersion)
|
internal static void ShowAd(string moduleVersion)
|
||||||
@@ -222,5 +168,24 @@ 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
165
Variables.cs
165
Variables.cs
@@ -1,165 +0,0 @@
|
|||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using CounterStrikeSharp.API.Core;
|
|
||||||
using CounterStrikeSharp.API.Core.Capabilities;
|
|
||||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
|
||||||
using MenuManager;
|
|
||||||
using Microsoft.Extensions.Localization;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
|
|
||||||
namespace WeaponPaints;
|
|
||||||
|
|
||||||
public partial class WeaponPaints
|
|
||||||
{
|
|
||||||
private static readonly Dictionary<string, string> WeaponList = new()
|
|
||||||
{
|
|
||||||
{"weapon_deagle", "Desert Eagle"},
|
|
||||||
{"weapon_elite", "Dual Berettas"},
|
|
||||||
{"weapon_fiveseven", "Five-SeveN"},
|
|
||||||
{"weapon_glock", "Glock-18"},
|
|
||||||
{"weapon_ak47", "AK-47"},
|
|
||||||
{"weapon_aug", "AUG"},
|
|
||||||
{"weapon_awp", "AWP"},
|
|
||||||
{"weapon_famas", "FAMAS"},
|
|
||||||
{"weapon_g3sg1", "G3SG1"},
|
|
||||||
{"weapon_galilar", "Galil AR"},
|
|
||||||
{"weapon_m249", "M249"},
|
|
||||||
{"weapon_m4a1", "M4A1"},
|
|
||||||
{"weapon_mac10", "MAC-10"},
|
|
||||||
{"weapon_p90", "P90"},
|
|
||||||
{"weapon_mp5sd", "MP5-SD"},
|
|
||||||
{"weapon_ump45", "UMP-45"},
|
|
||||||
{"weapon_xm1014", "XM1014"},
|
|
||||||
{"weapon_bizon", "PP-Bizon"},
|
|
||||||
{"weapon_mag7", "MAG-7"},
|
|
||||||
{"weapon_negev", "Negev"},
|
|
||||||
{"weapon_sawedoff", "Sawed-Off"},
|
|
||||||
{"weapon_tec9", "Tec-9"},
|
|
||||||
{"weapon_taser", "Zeus x27"},
|
|
||||||
{"weapon_hkp2000", "P2000"},
|
|
||||||
{"weapon_mp7", "MP7"},
|
|
||||||
{"weapon_mp9", "MP9"},
|
|
||||||
{"weapon_nova", "Nova"},
|
|
||||||
{"weapon_p250", "P250"},
|
|
||||||
{"weapon_scar20", "SCAR-20"},
|
|
||||||
{"weapon_sg556", "SG 553"},
|
|
||||||
{"weapon_ssg08", "SSG 08"},
|
|
||||||
{"weapon_m4a1_silencer", "M4A1-S"},
|
|
||||||
{"weapon_usp_silencer", "USP-S"},
|
|
||||||
{"weapon_cz75a", "CZ75-Auto"},
|
|
||||||
{"weapon_revolver", "R8 Revolver"},
|
|
||||||
{ "weapon_knife", "Default Knife" },
|
|
||||||
{ "weapon_knife_m9_bayonet", "M9 Bayonet" },
|
|
||||||
{ "weapon_knife_karambit", "Karambit" },
|
|
||||||
{ "weapon_bayonet", "Bayonet" },
|
|
||||||
{ "weapon_knife_survival_bowie", "Bowie Knife" },
|
|
||||||
{ "weapon_knife_butterfly", "Butterfly Knife" },
|
|
||||||
{ "weapon_knife_falchion", "Falchion Knife" },
|
|
||||||
{ "weapon_knife_flip", "Flip Knife" },
|
|
||||||
{ "weapon_knife_gut", "Gut Knife" },
|
|
||||||
{ "weapon_knife_tactical", "Huntsman Knife" },
|
|
||||||
{ "weapon_knife_push", "Shadow Daggers" },
|
|
||||||
{ "weapon_knife_gypsy_jackknife", "Navaja Knife" },
|
|
||||||
{ "weapon_knife_stiletto", "Stiletto Knife" },
|
|
||||||
{ "weapon_knife_widowmaker", "Talon Knife" },
|
|
||||||
{ "weapon_knife_ursus", "Ursus Knife" },
|
|
||||||
{ "weapon_knife_css", "Classic Knife" },
|
|
||||||
{ "weapon_knife_cord", "Paracord Knife" },
|
|
||||||
{ "weapon_knife_canis", "Survival Knife" },
|
|
||||||
{ "weapon_knife_outdoor", "Nomad Knife" },
|
|
||||||
{ "weapon_knife_skeleton", "Skeleton Knife" },
|
|
||||||
{ "weapon_knife_kukri", "Kukri Knife" }
|
|
||||||
};
|
|
||||||
|
|
||||||
public static IStringLocalizer? _localizer;
|
|
||||||
internal static readonly ConcurrentDictionary<int, string> GPlayersKnife = new();
|
|
||||||
internal static readonly ConcurrentDictionary<int, ushort> GPlayersGlove = new();
|
|
||||||
internal static readonly ConcurrentDictionary<int, ushort> GPlayersMusic = new();
|
|
||||||
public static readonly ConcurrentDictionary<int, (string? CT, string? T)> GPlayersAgent = new();
|
|
||||||
internal static readonly ConcurrentDictionary<int, ConcurrentDictionary<int, WeaponInfo>> GPlayerWeaponsInfo = new();
|
|
||||||
internal static List<JObject> SkinsList = [];
|
|
||||||
internal static List<JObject> GlovesList = [];
|
|
||||||
internal static List<JObject> AgentsList = [];
|
|
||||||
internal static List<JObject> MusicList = [];
|
|
||||||
internal static WeaponSynchronization? WeaponSync;
|
|
||||||
private static bool _gBCommandsAllowed = true;
|
|
||||||
private readonly Dictionary<int, string> _playerWeaponImage = new();
|
|
||||||
|
|
||||||
private static readonly Dictionary<int, DateTime> CommandsCooldown = new();
|
|
||||||
internal static Database? Database;
|
|
||||||
|
|
||||||
private static readonly MemoryFunctionVoid<nint, string, float> CAttributeListSetOrAddAttributeValueByName = new(GameData.GetSignature("CAttributeList_SetOrAddAttributeValueByName"));
|
|
||||||
|
|
||||||
private static readonly MemoryFunctionWithReturn<nint, string, int, int> SetBodygroupFunc = new(
|
|
||||||
GameData.GetSignature("CBaseModelEntity_SetBodygroup"));
|
|
||||||
|
|
||||||
private static readonly Func<nint, string, int, int> SetBodygroup = SetBodygroupFunc.Invoke;
|
|
||||||
|
|
||||||
private static Dictionary<int, string> WeaponDefindex { get; } = new()
|
|
||||||
{
|
|
||||||
{ 1, "weapon_deagle" },
|
|
||||||
{ 2, "weapon_elite" },
|
|
||||||
{ 3, "weapon_fiveseven" },
|
|
||||||
{ 4, "weapon_glock" },
|
|
||||||
{ 7, "weapon_ak47" },
|
|
||||||
{ 8, "weapon_aug" },
|
|
||||||
{ 9, "weapon_awp" },
|
|
||||||
{ 10, "weapon_famas" },
|
|
||||||
{ 11, "weapon_g3sg1" },
|
|
||||||
{ 13, "weapon_galilar" },
|
|
||||||
{ 14, "weapon_m249" },
|
|
||||||
{ 16, "weapon_m4a1" },
|
|
||||||
{ 17, "weapon_mac10" },
|
|
||||||
{ 19, "weapon_p90" },
|
|
||||||
{ 23, "weapon_mp5sd" },
|
|
||||||
{ 24, "weapon_ump45" },
|
|
||||||
{ 25, "weapon_xm1014" },
|
|
||||||
{ 26, "weapon_bizon" },
|
|
||||||
{ 27, "weapon_mag7" },
|
|
||||||
{ 28, "weapon_negev" },
|
|
||||||
{ 29, "weapon_sawedoff" },
|
|
||||||
{ 30, "weapon_tec9" },
|
|
||||||
{ 31, "weapon_taser" },
|
|
||||||
{ 32, "weapon_hkp2000" },
|
|
||||||
{ 33, "weapon_mp7" },
|
|
||||||
{ 34, "weapon_mp9" },
|
|
||||||
{ 35, "weapon_nova" },
|
|
||||||
{ 36, "weapon_p250" },
|
|
||||||
{ 38, "weapon_scar20" },
|
|
||||||
{ 39, "weapon_sg556" },
|
|
||||||
{ 40, "weapon_ssg08" },
|
|
||||||
{ 60, "weapon_m4a1_silencer" },
|
|
||||||
{ 61, "weapon_usp_silencer" },
|
|
||||||
{ 63, "weapon_cz75a" },
|
|
||||||
{ 64, "weapon_revolver" },
|
|
||||||
{ 500, "weapon_bayonet" },
|
|
||||||
{ 503, "weapon_knife_css" },
|
|
||||||
{ 505, "weapon_knife_flip" },
|
|
||||||
{ 506, "weapon_knife_gut" },
|
|
||||||
{ 507, "weapon_knife_karambit" },
|
|
||||||
{ 508, "weapon_knife_m9_bayonet" },
|
|
||||||
{ 509, "weapon_knife_tactical" },
|
|
||||||
{ 512, "weapon_knife_falchion" },
|
|
||||||
{ 514, "weapon_knife_survival_bowie" },
|
|
||||||
{ 515, "weapon_knife_butterfly" },
|
|
||||||
{ 516, "weapon_knife_push" },
|
|
||||||
{ 517, "weapon_knife_cord" },
|
|
||||||
{ 518, "weapon_knife_canis" },
|
|
||||||
{ 519, "weapon_knife_ursus" },
|
|
||||||
{ 520, "weapon_knife_gypsy_jackknife" },
|
|
||||||
{ 521, "weapon_knife_outdoor" },
|
|
||||||
{ 522, "weapon_knife_stiletto" },
|
|
||||||
{ 523, "weapon_knife_widowmaker" },
|
|
||||||
{ 525, "weapon_knife_skeleton" },
|
|
||||||
{ 526, "weapon_knife_kukri" }
|
|
||||||
};
|
|
||||||
|
|
||||||
private const ulong MinimumCustomItemId = 65578;
|
|
||||||
private ulong _nextItemId = MinimumCustomItemId;
|
|
||||||
private static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<int, ConcurrentDictionary<int, float>> _temporaryPlayerWeaponWear = new();
|
|
||||||
|
|
||||||
internal static IMenuApi? MenuApi;
|
|
||||||
private static readonly PluginCapability<IMenuApi> MenuCapability = new("menu:nfcore");
|
|
||||||
}
|
|
||||||
631
WeaponAction.cs
631
WeaponAction.cs
@@ -1,518 +1,343 @@
|
|||||||
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.Utils;
|
using CounterStrikeSharp.API.Modules.Utils;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace WeaponPaints
|
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 == null || weapon == null || !weapon.IsValid || !Utility.IsPlayerValid(player)) return;
|
||||||
if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out _)) return;
|
|
||||||
|
|
||||||
bool isKnife = weapon.DesignerName.Contains("knife") || weapon.DesignerName.Contains("bayonet");
|
int playerIndex = (int)player.Index;
|
||||||
|
|
||||||
if (isKnife && !GPlayersKnife.ContainsKey(player.Slot) || isKnife && GPlayersKnife[player.Slot] == "weapon_knife") return;
|
if (!gPlayerWeaponsInfo.ContainsKey(playerIndex)) 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 };
|
if (isKnife && !g_playersKnife.ContainsKey(playerIndex) || isKnife && g_playersKnife[playerIndex] == "weapon_knife") return;
|
||||||
|
|
||||||
|
ushort weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
|
||||||
|
|
||||||
if (isKnife)
|
if (isKnife)
|
||||||
{
|
{
|
||||||
var newDefIndex = WeaponDefindex.FirstOrDefault(x => x.Value == GPlayersKnife[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 weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
|
if (_config.AdditionalSetting.GiveRandomSkin &&
|
||||||
int fallbackPaintKit = 0;
|
!gPlayerWeaponsInfo[playerIndex].ContainsKey(weaponDefIndex))
|
||||||
|
|
||||||
weapon.AttributeManager.Item.AccountID = (uint)player.SteamID;
|
|
||||||
|
|
||||||
if (_config.Additional.GiveRandomSkin &&
|
|
||||||
!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.00001f;
|
||||||
|
if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null)
|
||||||
weapon.AttributeManager.Item.NetworkedDynamicAttributes.Attributes.RemoveAll();
|
{
|
||||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture prefab", GetRandomPaint(weaponDefIndex));
|
if (weapon.CBodyComponent!.SceneNode!.GetSkeletonInstance().ModelState.MeshGroupMask != 2)
|
||||||
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.CBodyComponent!.SceneNode!.GetSkeletonInstance().ModelState.MeshGroupMask = 2;
|
||||||
|
}
|
||||||
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;
|
|
||||||
|
|
||||||
if (fallbackPaintKit == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (isKnife) return;
|
|
||||||
UpdatePlayerWeaponMeshGroupMask(player, weapon, !newPaints.Contains(fallbackPaintKit));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!GPlayerWeaponsInfo[player.Slot].TryGetValue(weaponDefIndex, out var value) || value.Paint == 0) return;
|
if (!gPlayerWeaponsInfo[playerIndex].ContainsKey(weaponDefIndex)) return;
|
||||||
|
WeaponInfo weaponInfo = gPlayerWeaponsInfo[playerIndex][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;
|
||||||
weapon.AttributeManager.Item.ItemIDHigh = weapon.AttributeManager.Item.ItemIDLow >> 32;
|
weapon.AttributeManager.Item.ItemIDHigh = weapon.AttributeManager.Item.ItemIDLow >> 32;
|
||||||
weapon.AttributeManager.Item.CustomName = weaponInfo.Nametag;
|
|
||||||
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;
|
if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null)
|
||||||
|
{
|
||||||
|
if (weapon.CBodyComponent!.SceneNode!.GetSkeletonInstance().ModelState.MeshGroupMask != 2)
|
||||||
|
{
|
||||||
|
weapon.CBodyComponent!.SceneNode!.GetSkeletonInstance().ModelState.MeshGroupMask = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if (fallbackPaintKit == 0)
|
internal static void GiveKnifeToPlayer(CCSPlayerController? player)
|
||||||
return;
|
|
||||||
|
|
||||||
if (isKnife) return;
|
|
||||||
|
|
||||||
if (weaponInfo.Stickers.Count > 0) SetStickers(player, weapon);
|
|
||||||
if (weaponInfo.KeyChain != null) SetKeychain(player, weapon);
|
|
||||||
|
|
||||||
UpdatePlayerWeaponMeshGroupMask(player, weapon, !newPaints.Contains(fallbackPaintKit));
|
|
||||||
}
|
|
||||||
|
|
||||||
// silly method to update sticker when call RefreshWeapons()
|
|
||||||
private void IncrementWearForWeaponWithStickers(CCSPlayerController player, CBasePlayerWeapon weapon)
|
|
||||||
{
|
{
|
||||||
int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
|
if (!_config.AdditionalSetting.KnifeEnabled || player == null || !player.IsValid) return;
|
||||||
if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out var playerWeapons) ||
|
|
||||||
!playerWeapons.TryGetValue(weaponDefIndex, out var weaponInfo) ||
|
|
||||||
weaponInfo.Stickers.Count <= 0) return;
|
|
||||||
|
|
||||||
float wearIncrement = 0.001f;
|
|
||||||
float currentWear = weaponInfo.Wear;
|
|
||||||
|
|
||||||
var playerWear = _temporaryPlayerWeaponWear.GetOrAdd(player.Slot, _ => new ConcurrentDictionary<int, float>());
|
if (g_playersKnife.TryGetValue((int)player.Index, out var knife))
|
||||||
|
|
||||||
float incrementedWear = playerWear.AddOrUpdate(
|
|
||||||
weaponDefIndex,
|
|
||||||
currentWear + wearIncrement,
|
|
||||||
(_, oldWear) => Math.Min(oldWear + wearIncrement, 1.0f)
|
|
||||||
);
|
|
||||||
|
|
||||||
weapon.FallbackWear = incrementedWear;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetStickers(CCSPlayerController? player, CBasePlayerWeapon weapon)
|
|
||||||
{
|
|
||||||
if (player == null || !player.IsValid) return;
|
|
||||||
|
|
||||||
int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
|
|
||||||
|
|
||||||
if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out var playerWeapons) ||
|
|
||||||
!playerWeapons.TryGetValue(weaponDefIndex, out var weaponInfo))
|
|
||||||
{
|
{
|
||||||
return;
|
player.GiveNamedItem(knife);
|
||||||
}
|
}
|
||||||
|
else if (_config.AdditionalSetting.GiveRandomKnife)
|
||||||
foreach (var sticker in weaponInfo.Stickers)
|
|
||||||
{
|
{
|
||||||
int stickerSlot = weaponInfo.Stickers.IndexOf(sticker);
|
var knifeTypes = weaponList.Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")).ToDictionary(pair => pair.Key, pair => pair.Value);
|
||||||
|
|
||||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
Random random = new();
|
||||||
$"sticker slot {stickerSlot} id", ViewAsFloat(sticker.Id));
|
int index = random.Next(knifeTypes.Count);
|
||||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
var randomKnifeClass = knifeTypes.Keys.ElementAt(index);
|
||||||
$"sticker slot {stickerSlot} schema", sticker.Schema);
|
|
||||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
player.GiveNamedItem(randomKnifeClass);
|
||||||
$"sticker slot {stickerSlot} offset x", sticker.OffsetX);
|
|
||||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
|
||||||
$"sticker slot {stickerSlot} offset y", sticker.OffsetY);
|
|
||||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
|
||||||
$"sticker slot {stickerSlot} wear", sticker.Wear);
|
|
||||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
|
||||||
$"sticker slot {stickerSlot} scale", sticker.Scale);
|
|
||||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
|
||||||
$"sticker slot {stickerSlot} rotation", sticker.Rotation);
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (_temporaryPlayerWeaponWear.TryGetValue(player.Slot, out var playerWear) &&
|
|
||||||
playerWear.TryGetValue(weaponDefIndex, out float storedWear))
|
|
||||||
{
|
{
|
||||||
weapon.FallbackWear = storedWear;
|
var defaultKnife = (CsTeam)player.TeamNum == CsTeam.Terrorist ? "weapon_knife_t" : "weapon_knife";
|
||||||
|
player.GiveNamedItem(defaultKnife);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetKeychain(CCSPlayerController? player, CBasePlayerWeapon weapon)
|
internal static bool PlayerHasKnife(CCSPlayerController? player)
|
||||||
{
|
{
|
||||||
if (player == null || !player.IsValid) return;
|
if (!_config.AdditionalSetting.KnifeEnabled) return false;
|
||||||
|
|
||||||
int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
|
if (player == null || !player.IsValid || player.PlayerPawn == null || !player.PlayerPawn.IsValid || !player.PawnIsAlive)
|
||||||
|
|
||||||
if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out var playerWeaponsInfo) ||
|
|
||||||
!playerWeaponsInfo.TryGetValue(weaponDefIndex, out var value) ||
|
|
||||||
value.KeyChain == null) return;
|
|
||||||
var keyChain = value.KeyChain;
|
|
||||||
|
|
||||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
|
||||||
"keychain slot 0 id", ViewAsFloat(keyChain.Id));
|
|
||||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
|
||||||
"keychain slot 0 offset x", keyChain.OffsetX);
|
|
||||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
|
||||||
"keychain slot 0 offset y", keyChain.OffsetY);
|
|
||||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
|
||||||
"keychain slot 0 offset z", keyChain.OffsetZ);
|
|
||||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
|
||||||
"keychain slot 0 seed", keyChain.Seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void GiveKnifeToPlayer(CCSPlayerController? player)
|
|
||||||
{
|
|
||||||
if (!_config.Additional.KnifeEnabled || player == null || !player.IsValid) return;
|
|
||||||
|
|
||||||
if (PlayerHasKnife(player)) return;
|
|
||||||
|
|
||||||
//string knifeToGive = (CsTeam)player.TeamNum == CsTeam.Terrorist ? "weapon_knife_t" : "weapon_knife";
|
|
||||||
player.GiveNamedItem(CsItem.Knife);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool PlayerHasKnife(CCSPlayerController? player)
|
|
||||||
{
|
|
||||||
if (!_config.Additional.KnifeEnabled) return false;
|
|
||||||
|
|
||||||
if (player == null || !player.IsValid || !player.PlayerPawn.IsValid)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (player.PlayerPawn.Value == null || player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null)
|
if (player.PlayerPawn?.Value == null || player.PlayerPawn?.Value.WeaponServices == null || player.PlayerPawn?.Value.ItemServices == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var weapons = player.PlayerPawn.Value.WeaponServices?.MyWeapons;
|
var weapons = player.PlayerPawn.Value.WeaponServices?.MyWeapons;
|
||||||
if (weapons == null) return false;
|
if (weapons == null) return false;
|
||||||
foreach (var weapon in weapons)
|
foreach (var weapon in weapons)
|
||||||
{
|
{
|
||||||
if (!weapon.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 RefreshPlayerKnife(CCSPlayerController? player)
|
||||||
{
|
{
|
||||||
if (!_gBCommandsAllowed) return;
|
if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PawnIsAlive) return;
|
||||||
if (player == null || !player.IsValid || player.PlayerPawn.Value == null || (LifeState_t)player.LifeState != LifeState_t.LIFE_ALIVE)
|
if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return;
|
||||||
return;
|
|
||||||
if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons;
|
var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons;
|
||||||
|
if (weapons != null && weapons.Count > 0)
|
||||||
if (weapons.Count == 0)
|
|
||||||
return;
|
|
||||||
if (player.Team is CsTeam.None or CsTeam.Spectator)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int playerTeam = player.TeamNum;
|
|
||||||
|
|
||||||
Dictionary<string, List<(int, int)>> weaponsWithAmmo = [];
|
|
||||||
|
|
||||||
foreach (var weapon in weapons)
|
|
||||||
{
|
{
|
||||||
if (!weapon.IsValid || weapon.Value == null ||
|
CCSPlayer_ItemServices service = new(player.PlayerPawn.Value.ItemServices.Handle);
|
||||||
!weapon.Value.IsValid || !weapon.Value.DesignerName.Contains("weapon_"))
|
//var dropWeapon = VirtualFunction.CreateVoid<nint, nint>(service.Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon"));
|
||||||
continue;
|
|
||||||
|
|
||||||
CCSWeaponBaseGun gun = weapon.Value.As<CCSWeaponBaseGun>();
|
foreach (var weapon in weapons)
|
||||||
|
|
||||||
if (weapon.Value.Entity == null) continue;
|
|
||||||
if (!weapon.Value.OwnerEntity.IsValid) continue;
|
|
||||||
if (gun.Entity == null) continue;
|
|
||||||
if (!gun.IsValid) continue;
|
|
||||||
if (!gun.VisibleinPVS) continue;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
CCSWeaponBaseVData? weaponData = weapon.Value.As<CCSWeaponBase>().VData;
|
if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid)
|
||||||
|
|
||||||
if (weaponData == null) continue;
|
|
||||||
|
|
||||||
if (weaponData.GearSlot is gear_slot_t.GEAR_SLOT_RIFLE or gear_slot_t.GEAR_SLOT_PISTOL)
|
|
||||||
{
|
{
|
||||||
if (!WeaponDefindex.TryGetValue(weapon.Value.AttributeManager.Item.ItemDefinitionIndex, out var weaponByDefindex))
|
//if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59)
|
||||||
continue;
|
if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet"))
|
||||||
|
|
||||||
int clip1 = weapon.Value.Clip1;
|
|
||||||
int reservedAmmo = weapon.Value.ReserveAmmo[0];
|
|
||||||
|
|
||||||
if (!weaponsWithAmmo.TryGetValue(weaponByDefindex, out var value))
|
|
||||||
{
|
{
|
||||||
value = [];
|
if (weapon.Index <= 0) return;
|
||||||
weaponsWithAmmo.Add(weaponByDefindex, value);
|
int weaponEntityIndex = (int)weapon.Index;
|
||||||
|
NativeAPI.IssueClientCommand((int)player.Index - 1, "slot3");
|
||||||
|
AddTimer(0.22f, () =>
|
||||||
|
{
|
||||||
|
if (player.PlayerPawn.Value.WeaponServices.ActiveWeapon.Value!.DesignerName.Contains("knife")
|
||||||
|
||
|
||||||
|
player.PlayerPawn.Value.WeaponServices.ActiveWeapon.Value!.DesignerName.Contains("bayonet")
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (player.PawnIsAlive)
|
||||||
|
{
|
||||||
|
NativeAPI.IssueClientCommand((int)player.Index - 1, "slot3");
|
||||||
|
service.DropActivePlayerWeapon(weapon.Value);
|
||||||
|
GiveKnifeToPlayer(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Task.Delay(TimeSpan.FromSeconds(3.5)).ContinueWith(_ =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
CEntityInstance? knife = Utilities.GetEntityFromIndex<CEntityInstance>(weaponEntityIndex);
|
||||||
|
|
||||||
|
if (knife != null && knife.IsValid && knife.Handle != -1 && knife.Index > 0)
|
||||||
|
{
|
||||||
|
knife.Remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception) { }
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
value.Add((clip1, reservedAmmo));
|
|
||||||
|
|
||||||
if (gun.VData == null) return;
|
|
||||||
|
|
||||||
weapon.Value?.AddEntityIOEvent("Kill", weapon.Value, null, "", 0.1f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (weaponData.GearSlot == gear_slot_t.GEAR_SLOT_KNIFE)
|
|
||||||
{
|
|
||||||
weapon.Value?.AddEntityIOEvent("Kill", weapon.Value, null, "", 0.1f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogWarning(ex.Message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AddTimer(0.23f, () =>
|
|
||||||
{
|
|
||||||
if (!_gBCommandsAllowed) return;
|
|
||||||
|
|
||||||
if (!PlayerHasKnife(player))
|
|
||||||
GiveKnifeToPlayer(player);
|
|
||||||
|
|
||||||
foreach (var entry in weaponsWithAmmo)
|
|
||||||
{
|
|
||||||
foreach (var ammo in entry.Value)
|
|
||||||
{
|
|
||||||
var newWeapon = new CBasePlayerWeapon(player.GiveNamedItem(entry.Key));
|
|
||||||
Server.NextFrame(() =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
newWeapon.Clip1 = ammo.Item1;
|
|
||||||
newWeapon.ReserveAmmo[0] = ammo.Item2;
|
|
||||||
|
|
||||||
IncrementWearForWeaponWithStickers(player, newWeapon);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogWarning("Error setting weapon properties: " + ex.Message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, TimerFlags.STOP_ON_MAPCHANGE);
|
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
private void GivePlayerGloves(CCSPlayerController player)
|
internal void RefreshSkins(CCSPlayerController? player)
|
||||||
{
|
{
|
||||||
if (!Utility.IsPlayerValid(player) || (LifeState_t)player.LifeState != LifeState_t.LIFE_ALIVE) return;
|
return;
|
||||||
|
if (!Utility.IsPlayerValid(player) || !player!.PawnIsAlive) return;
|
||||||
|
|
||||||
CCSPlayerPawn? pawn = player.PlayerPawn.Value;
|
AddTimer(0.18f, () => NativeAPI.IssueClientCommand((int)player.Index - 1, "slot3"));
|
||||||
if (pawn == null || !pawn.IsValid)
|
AddTimer(0.25f, () => NativeAPI.IssueClientCommand((int)player.Index - 1, "slot2"));
|
||||||
return;
|
AddTimer(0.38f, () => NativeAPI.IssueClientCommand((int)player.Index - 1, "slot1"));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
internal void RefreshWeapons(CCSPlayerController? player)
|
||||||
|
{
|
||||||
|
if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PawnIsAlive) return;
|
||||||
|
if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return;
|
||||||
|
|
||||||
var model = pawn.CBodyComponent?.SceneNode?.GetSkeletonInstance()?.ModelState.ModelName ?? string.Empty;
|
var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons;
|
||||||
if (!string.IsNullOrEmpty(model))
|
if (weapons != null && weapons.Count > 0)
|
||||||
{
|
{
|
||||||
pawn.SetModel("characters/models/tm_jumpsuit/tm_jumpsuit_varianta.vmdl");
|
CCSPlayer_ItemServices service = new(player.PlayerPawn.Value.ItemServices.Handle);
|
||||||
pawn.SetModel(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
Instance.AddTimer(0.08f, () =>
|
foreach (var weapon in weapons)
|
||||||
{
|
|
||||||
CEconItemView item = pawn.EconGloves;
|
|
||||||
try
|
|
||||||
{
|
{
|
||||||
if (!player.IsValid)
|
if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid)
|
||||||
return;
|
{
|
||||||
|
if (weapon.Index <= 0 || !weapon.Value.DesignerName.Contains("weapon_")) continue;
|
||||||
|
//if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet"))
|
||||||
|
{
|
||||||
|
player.RemoveItemByDesignerName(weapon.Value.DesignerName, true);
|
||||||
|
GiveKnifeToPlayer(player);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!WeaponDefindex.ContainsKey(weapon.Value.AttributeManager.Item.ItemDefinitionIndex)) continue;
|
||||||
|
int clip1, reservedAmmo;
|
||||||
|
|
||||||
if (!player.PawnIsAlive)
|
clip1 = weapon.Value.Clip1;
|
||||||
return;
|
reservedAmmo = weapon.Value.ReserveAmmo[0];
|
||||||
|
|
||||||
if (!GPlayersGlove.TryGetValue(player.Slot, out var gloveInfo) || gloveInfo == 0) return;
|
string weaponByDefindex = WeaponDefindex[weapon.Value.AttributeManager.Item.ItemDefinitionIndex];
|
||||||
|
player.RemoveItemByDesignerName(weapon.Value.DesignerName, true);
|
||||||
|
CBasePlayerWeapon newWeapon = new(player.GiveNamedItem(weaponByDefindex));
|
||||||
|
|
||||||
WeaponInfo weaponInfo = GPlayerWeaponsInfo[player.Slot][gloveInfo];
|
Server.NextFrame(() =>
|
||||||
|
{
|
||||||
item.ItemDefinitionIndex = gloveInfo;
|
if (newWeapon == null) return;
|
||||||
item.ItemIDLow = 16384 & 0xFFFFFFFF;
|
try
|
||||||
item.ItemIDHigh = 16384;
|
{
|
||||||
|
newWeapon.Clip1 = clip1;
|
||||||
CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture prefab", weaponInfo.Paint);
|
newWeapon.ReserveAmmo[0] = reservedAmmo;
|
||||||
CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture seed", weaponInfo.Seed);
|
}
|
||||||
CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture wear", weaponInfo.Wear);
|
catch (Exception)
|
||||||
|
{ }
|
||||||
item.Initialized = true;
|
});
|
||||||
|
}
|
||||||
SetBodygroup(pawn.Handle, "default_gloves", 1);
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogWarning("Refreshing weapons exception");
|
||||||
|
Console.WriteLine("[WeaponPaints] Refreshing weapons exception");
|
||||||
|
Console.WriteLine(ex.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception) { }
|
|
||||||
}, TimerFlags.STOP_ON_MAPCHANGE);
|
/*
|
||||||
|
if (Config.AdditionalSetting.SkinVisibilityFix)
|
||||||
|
RefreshSkins(player);
|
||||||
|
*/
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal void RemovePlayerKnife(CCSPlayerController? player, bool force = false)
|
||||||
|
{
|
||||||
|
if (player == null || !player.IsValid || player.PlayerPawn.Value == null || !player.PawnIsAlive) return;
|
||||||
|
if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null) return;
|
||||||
|
|
||||||
|
var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons;
|
||||||
|
if (weapons != null && weapons.Count > 0)
|
||||||
|
{
|
||||||
|
CCSPlayer_ItemServices service = new CCSPlayer_ItemServices(player.PlayerPawn.Value.ItemServices.Handle);
|
||||||
|
//var dropWeapon = VirtualFunction.CreateVoid<nint, nint>(service.Handle, GameData.GetOffset("CCSPlayer_ItemServices_DropActivePlayerWeapon"));
|
||||||
|
|
||||||
|
foreach (var weapon in weapons)
|
||||||
|
{
|
||||||
|
if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid)
|
||||||
|
{
|
||||||
|
//if (weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 42 || weapon.Value.AttributeManager.Item.ItemDefinitionIndex == 59)
|
||||||
|
if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet"))
|
||||||
|
{
|
||||||
|
if (!force)
|
||||||
|
{
|
||||||
|
if ((int)weapon.Index <= 0) return;
|
||||||
|
int weaponEntityIndex = (int)weapon.Index;
|
||||||
|
NativeAPI.IssueClientCommand((int)player.Index - 1, "slot3");
|
||||||
|
AddTimer(0.35f, () => service.DropActivePlayerWeapon(weapon.Value));
|
||||||
|
|
||||||
|
AddTimer(1.0f, () =>
|
||||||
|
{
|
||||||
|
CEntityInstance? knife = Utilities.GetEntityFromIndex<CEntityInstance>(weaponEntityIndex);
|
||||||
|
if (knife != null && knife.IsValid)
|
||||||
|
{
|
||||||
|
knife.Remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
weapon.Value.Remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
private static int GetRandomPaint(int defindex)
|
private static int GetRandomPaint(int defindex)
|
||||||
{
|
{
|
||||||
if (SkinsList.Count == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
Random rnd = new Random();
|
if (skinsList != null)
|
||||||
|
|
||||||
// Filter weapons by the provided defindex
|
|
||||||
var filteredWeapons = SkinsList.Where(w => w["weapon_defindex"]?.ToString() == defindex.ToString()).ToList();
|
|
||||||
|
|
||||||
if (filteredWeapons.Count == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
var randomWeapon = filteredWeapons[rnd.Next(filteredWeapons.Count)];
|
|
||||||
|
|
||||||
return int.TryParse(randomWeapon["paint"]?.ToString(), out var paintValue) ? paintValue : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SubclassChange(CBasePlayerWeapon weapon, ushort itemD)
|
|
||||||
{
|
|
||||||
var subclassChangeFunc = VirtualFunction.Create<nint, string, int>(
|
|
||||||
GameData.GetSignature("ChangeSubclass")
|
|
||||||
);
|
|
||||||
|
|
||||||
subclassChangeFunc(weapon.Handle, itemD.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdateWeaponMeshGroupMask(CBaseEntity weapon, bool isLegacy = false)
|
|
||||||
{
|
|
||||||
if (weapon.CBodyComponent?.SceneNode == null) return;
|
|
||||||
var skeleton = weapon.CBodyComponent.SceneNode.GetSkeletonInstance();
|
|
||||||
var value = (ulong)(isLegacy ? 2 : 1);
|
|
||||||
|
|
||||||
if (skeleton.ModelState.MeshGroupMask != value)
|
|
||||||
{
|
{
|
||||||
skeleton.ModelState.MeshGroupMask = value;
|
Random rnd = new Random();
|
||||||
}
|
// Filter weapons by the provided defindex
|
||||||
}
|
var filteredWeapons = skinsList.FindAll(w => w["weapon_defindex"]?.ToString() == defindex.ToString());
|
||||||
|
|
||||||
private static void UpdatePlayerWeaponMeshGroupMask(CCSPlayerController player, CBasePlayerWeapon weapon, bool isLegacy)
|
if (filteredWeapons.Count > 0)
|
||||||
{
|
|
||||||
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 (!GPlayersAgent.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(
|
var randomWeapon = filteredWeapons[rnd.Next(filteredWeapons.Count)];
|
||||||
$"characters/models/{model}.vmdl"
|
if (int.TryParse(randomWeapon["paint"]?.ToString(), out int paintValue))
|
||||||
);
|
{
|
||||||
});
|
return paintValue;
|
||||||
}
|
}
|
||||||
catch (Exception)
|
else
|
||||||
{
|
{
|
||||||
}
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void GivePlayerMusicKit(CCSPlayerController player)
|
|
||||||
{
|
|
||||||
if (!GPlayersMusic.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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
private static unsafe CHandle<CBaseViewModel>[]? GetPlayerViewModels(CCSPlayerController player)
|
||||||
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])!;
|
||||||
}
|
}
|
||||||
|
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float ViewAsFloat(uint value)
|
|
||||||
{
|
|
||||||
return BitConverter.Int32BitsToSingle((int)value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,31 +2,12 @@
|
|||||||
{
|
{
|
||||||
public class WeaponInfo
|
public class WeaponInfo
|
||||||
{
|
{
|
||||||
public int Paint { get; set; }
|
public ushort Paint { get; set; }
|
||||||
public int Seed { get; set; } = 0;
|
public ushort Seed { get; set; }
|
||||||
public float Wear { get; set; } = 0f;
|
|
||||||
public string Nametag { get; set; } = "";
|
|
||||||
public KeyChainInfo? KeyChain { get; set; }
|
|
||||||
public List<StickerInfo> Stickers { get; set; } = new List<StickerInfo>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class StickerInfo
|
|
||||||
{
|
|
||||||
public uint Id { get; set; }
|
|
||||||
public uint Schema { get; set; }
|
|
||||||
public float OffsetX { get; set; }
|
|
||||||
public float OffsetY { get; set; }
|
|
||||||
public float Wear { get; set; }
|
public float Wear { get; set; }
|
||||||
public float Scale { get; set; }
|
public string? NameTag { get; set; }
|
||||||
public float Rotation { get; set; }
|
public ushort Quality { get; set; }
|
||||||
}
|
public uint StatTrack { get; set; }
|
||||||
|
public bool StatTrackEnabled { get; set; }
|
||||||
public class KeyChainInfo
|
}
|
||||||
{
|
|
||||||
public uint Id { get; set; }
|
|
||||||
public float OffsetX { get; set; }
|
|
||||||
public float OffsetY { get; set; }
|
|
||||||
public float OffsetZ { get; set; }
|
|
||||||
public uint Seed { get; set; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
314
WeaponPaints.cs
314
WeaponPaints.cs
@@ -1,130 +1,288 @@
|
|||||||
using CounterStrikeSharp.API;
|
using CounterStrikeSharp.API;
|
||||||
using CounterStrikeSharp.API.Core;
|
using CounterStrikeSharp.API.Core;
|
||||||
using CounterStrikeSharp.API.Core.Attributes;
|
using CounterStrikeSharp.API.Core.Attributes;
|
||||||
|
using CounterStrikeSharp.API.Modules.Cvars;
|
||||||
|
using Microsoft.Extensions.Localization;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using MySqlConnector;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace WeaponPaints;
|
namespace WeaponPaints;
|
||||||
|
|
||||||
[MinimumApiVersion(276)]
|
[MinimumApiVersion(155)]
|
||||||
public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
|
public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
|
||||||
{
|
{
|
||||||
internal static WeaponPaints Instance { get; private set; } = new();
|
internal static readonly Dictionary<string, string> weaponList = new()
|
||||||
|
{
|
||||||
|
{"weapon_deagle", "Desert Eagle"},
|
||||||
|
{"weapon_elite", "Dual Berettas"},
|
||||||
|
{"weapon_fiveseven", "Five-SeveN"},
|
||||||
|
{"weapon_glock", "Glock-18"},
|
||||||
|
{"weapon_ak47", "AK-47"},
|
||||||
|
{"weapon_aug", "AUG"},
|
||||||
|
{"weapon_awp", "AWP"},
|
||||||
|
{"weapon_famas", "FAMAS"},
|
||||||
|
{"weapon_g3sg1", "G3SG1"},
|
||||||
|
{"weapon_galilar", "Galil AR"},
|
||||||
|
{"weapon_m249", "M249"},
|
||||||
|
{"weapon_m4a1", "M4A1"},
|
||||||
|
{"weapon_mac10", "MAC-10"},
|
||||||
|
{"weapon_p90", "P90"},
|
||||||
|
{"weapon_mp5sd", "MP5-SD"},
|
||||||
|
{"weapon_ump45", "UMP-45"},
|
||||||
|
{"weapon_xm1014", "XM1014"},
|
||||||
|
{"weapon_bizon", "PP-Bizon"},
|
||||||
|
{"weapon_mag7", "MAG-7"},
|
||||||
|
{"weapon_negev", "Negev"},
|
||||||
|
{"weapon_sawedoff", "Sawed-Off"},
|
||||||
|
{"weapon_tec9", "Tec-9"},
|
||||||
|
{"weapon_hkp2000", "P2000"},
|
||||||
|
{"weapon_mp7", "MP7"},
|
||||||
|
{"weapon_mp9", "MP9"},
|
||||||
|
{"weapon_nova", "Nova"},
|
||||||
|
{"weapon_p250", "P250"},
|
||||||
|
{"weapon_scar20", "SCAR-20"},
|
||||||
|
{"weapon_sg556", "SG 553"},
|
||||||
|
{"weapon_ssg08", "SSG 08"},
|
||||||
|
{"weapon_m4a1_silencer", "M4A1-S"},
|
||||||
|
{"weapon_usp_silencer", "USP-S"},
|
||||||
|
{"weapon_cz75a", "CZ75-Auto"},
|
||||||
|
{"weapon_revolver", "R8 Revolver"},
|
||||||
|
{ "weapon_knife", "Default Knife" },
|
||||||
|
{ "weapon_knife_m9_bayonet", "M9 Bayonet" },
|
||||||
|
{ "weapon_knife_karambit", "Karambit" },
|
||||||
|
{ "weapon_bayonet", "Bayonet" },
|
||||||
|
{ "weapon_knife_survival_bowie", "Bowie Knife" },
|
||||||
|
{ "weapon_knife_butterfly", "Butterfly Knife" },
|
||||||
|
{ "weapon_knife_falchion", "Falchion Knife" },
|
||||||
|
{ "weapon_knife_flip", "Flip Knife" },
|
||||||
|
{ "weapon_knife_gut", "Gut Knife" },
|
||||||
|
{ "weapon_knife_tactical", "Huntsman Knife" },
|
||||||
|
{ "weapon_knife_push", "Shadow Daggers" },
|
||||||
|
{ "weapon_knife_gypsy_jackknife", "Navaja Knife" },
|
||||||
|
{ "weapon_knife_stiletto", "Stiletto Knife" },
|
||||||
|
{ "weapon_knife_widowmaker", "Talon Knife" },
|
||||||
|
{ "weapon_knife_ursus", "Ursus Knife" },
|
||||||
|
{ "weapon_knife_css", "Classic Knife" },
|
||||||
|
{ "weapon_knife_cord", "Paracord Knife" },
|
||||||
|
{ "weapon_knife_canis", "Survival Knife" },
|
||||||
|
{ "weapon_knife_outdoor", "Nomad Knife" },
|
||||||
|
{ "weapon_knife_skeleton", "Skeleton Knife" }
|
||||||
|
};
|
||||||
|
public static Dictionary<int, string> WeaponDefindex { get; } = new Dictionary<int, string>
|
||||||
|
{
|
||||||
|
{ 1, "weapon_deagle" },
|
||||||
|
{ 2, "weapon_elite" },
|
||||||
|
{ 3, "weapon_fiveseven" },
|
||||||
|
{ 4, "weapon_glock" },
|
||||||
|
{ 7, "weapon_ak47" },
|
||||||
|
{ 8, "weapon_aug" },
|
||||||
|
{ 9, "weapon_awp" },
|
||||||
|
{ 10, "weapon_famas" },
|
||||||
|
{ 11, "weapon_g3sg1" },
|
||||||
|
{ 13, "weapon_galilar" },
|
||||||
|
{ 14, "weapon_m249" },
|
||||||
|
{ 16, "weapon_m4a1" },
|
||||||
|
{ 17, "weapon_mac10" },
|
||||||
|
{ 19, "weapon_p90" },
|
||||||
|
{ 23, "weapon_mp5sd" },
|
||||||
|
{ 24, "weapon_ump45" },
|
||||||
|
{ 25, "weapon_xm1014" },
|
||||||
|
{ 26, "weapon_bizon" },
|
||||||
|
{ 27, "weapon_mag7" },
|
||||||
|
{ 28, "weapon_negev" },
|
||||||
|
{ 29, "weapon_sawedoff" },
|
||||||
|
{ 30, "weapon_tec9" },
|
||||||
|
{ 32, "weapon_hkp2000" },
|
||||||
|
{ 33, "weapon_mp7" },
|
||||||
|
{ 34, "weapon_mp9" },
|
||||||
|
{ 35, "weapon_nova" },
|
||||||
|
{ 36, "weapon_p250" },
|
||||||
|
{ 38, "weapon_scar20" },
|
||||||
|
{ 39, "weapon_sg556" },
|
||||||
|
{ 40, "weapon_ssg08" },
|
||||||
|
{ 60, "weapon_m4a1_silencer" },
|
||||||
|
{ 61, "weapon_usp_silencer" },
|
||||||
|
{ 63, "weapon_cz75a" },
|
||||||
|
{ 64, "weapon_revolver" },
|
||||||
|
{ 500, "weapon_bayonet" },
|
||||||
|
{ 503, "weapon_knife_css" },
|
||||||
|
{ 505, "weapon_knife_flip" },
|
||||||
|
{ 506, "weapon_knife_gut" },
|
||||||
|
{ 507, "weapon_knife_karambit" },
|
||||||
|
{ 508, "weapon_knife_m9_bayonet" },
|
||||||
|
{ 509, "weapon_knife_tactical" },
|
||||||
|
{ 512, "weapon_knife_falchion" },
|
||||||
|
{ 514, "weapon_knife_survival_bowie" },
|
||||||
|
{ 515, "weapon_knife_butterfly" },
|
||||||
|
{ 516, "weapon_knife_push" },
|
||||||
|
{ 517, "weapon_knife_cord" },
|
||||||
|
{ 518, "weapon_knife_canis" },
|
||||||
|
{ 519, "weapon_knife_ursus" },
|
||||||
|
{ 520, "weapon_knife_gypsy_jackknife" },
|
||||||
|
{ 521, "weapon_knife_outdoor" },
|
||||||
|
{ 522, "weapon_knife_stiletto" },
|
||||||
|
{ 523, "weapon_knife_widowmaker" },
|
||||||
|
{ 525, "weapon_knife_skeleton" }
|
||||||
|
};
|
||||||
|
|
||||||
public WeaponPaintsConfig Config { get; set; } = new();
|
internal static WeaponPaintsConfig _config = new WeaponPaintsConfig();
|
||||||
private static WeaponPaintsConfig _config { get; set; } = new();
|
internal static IStringLocalizer? _localizer;
|
||||||
public override string ModuleAuthor => "Nereziel & daffyy";
|
internal static Dictionary<int, int> g_knifePickupCount = new Dictionary<int, int>();
|
||||||
public override string ModuleDescription => "Skin, gloves, agents and knife selector, standalone and web-based";
|
internal static ConcurrentDictionary<int, int> g_playersDatabaseIndex = new ConcurrentDictionary<int, int>();
|
||||||
|
internal static ConcurrentDictionary<int, string> g_playersKnife = new ConcurrentDictionary<int, string>();
|
||||||
|
internal static ConcurrentDictionary<int, int?> g_playersMusicKit = new ConcurrentDictionary<int, int?>();
|
||||||
|
internal static ConcurrentDictionary<int, ConcurrentDictionary<ushort, WeaponInfo>> gPlayerWeaponsInfo = new ConcurrentDictionary<int, ConcurrentDictionary<ushort, WeaponInfo>>();
|
||||||
|
internal static List<JObject> skinsList = new List<JObject>();
|
||||||
|
internal static WeaponSynchronization? weaponSync;
|
||||||
|
internal bool g_bCommandsAllowed = true;
|
||||||
|
|
||||||
|
internal Uri GlobalShareApi = new("https://weaponpaints.fun/api.php");
|
||||||
|
internal int GlobalShareServerId = 0;
|
||||||
|
internal static Dictionary<int, DateTime> commandsCooldown = new Dictionary<int, DateTime>();
|
||||||
|
private string DatabaseConnectionString = string.Empty;
|
||||||
|
private CounterStrikeSharp.API.Modules.Timers.Timer? g_hTimerCheckSkinsData = null;
|
||||||
|
public WeaponPaintsConfig Config { get; set; } = new();
|
||||||
|
public override string ModuleAuthor => "Nereziel & daffyy";
|
||||||
|
public override string ModuleDescription => "Skin and knife selector, standalone and web-based";
|
||||||
public override string ModuleName => "WeaponPaints";
|
public override string ModuleName => "WeaponPaints";
|
||||||
public override string ModuleVersion => "2.6a";
|
public override string ModuleVersion => "1.5.0";
|
||||||
|
|
||||||
|
public static WeaponPaintsConfig GetWeaponPaintsConfig()
|
||||||
|
{
|
||||||
|
return _config;
|
||||||
|
}
|
||||||
|
|
||||||
public override void Load(bool hotReload)
|
public override void Load(bool hotReload)
|
||||||
{
|
{
|
||||||
Instance = this;
|
if (!Config.GlobalShare)
|
||||||
|
{
|
||||||
|
DatabaseConnectionString = Utility.BuildDatabaseConnectionString();
|
||||||
|
Utility.TestDatabaseConnection();
|
||||||
|
}
|
||||||
|
|
||||||
if (hotReload)
|
if (hotReload)
|
||||||
{
|
{
|
||||||
OnMapStart(string.Empty);
|
OnMapStart(string.Empty);
|
||||||
|
|
||||||
foreach (var player in Enumerable
|
List<CCSPlayerController> players = 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 }))
|
|
||||||
{
|
|
||||||
GPlayerWeaponsInfo.TryRemove(player.Slot, out _);
|
|
||||||
GPlayersKnife.TryRemove(player.Slot, out _);
|
|
||||||
GPlayersGlove.TryRemove(player.Slot, out _);
|
|
||||||
GPlayersAgent.TryRemove(player.Slot, out _);
|
|
||||||
|
|
||||||
var playerInfo = new PlayerInfo
|
foreach (CCSPlayerController player in players)
|
||||||
|
{
|
||||||
|
if (player == null || !player.IsValid || player.IsBot || player.IsHLTV || player.SteamID.ToString() == "") continue;
|
||||||
|
if (g_playersDatabaseIndex.ContainsKey((int)player.Index)) continue;
|
||||||
|
|
||||||
|
PlayerInfo playerInfo = new PlayerInfo
|
||||||
{
|
{
|
||||||
UserId = player.UserId,
|
UserId = player.UserId,
|
||||||
Slot = player.Slot,
|
|
||||||
Index = (int)player.Index,
|
Index = (int)player.Index,
|
||||||
SteamId = player?.SteamID.ToString(),
|
SteamId = player?.SteamID,
|
||||||
Name = player?.PlayerName,
|
Name = player?.PlayerName,
|
||||||
IpAddress = player?.IpAddress?.Split(":")[0]
|
IpAddress = player?.IpAddress?.Split(":")[0]
|
||||||
};
|
};
|
||||||
|
if (weaponSync != null)
|
||||||
_ = Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
if (WeaponSync != null) await WeaponSync.GetPlayerData(playerInfo);
|
_ = weaponSync!.GetPlayerDatabaseIndex(playerInfo);
|
||||||
});
|
}
|
||||||
|
g_knifePickupCount[(int)player!.Index] = 0;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
RegisterListeners();
|
||||||
|
RegisterCommands();
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
Utility.LoadSkinsFromFile(ModuleDirectory + $"/data/skins_{_config.SkinsLanguage}.json", Logger);
|
if (Config.AdditionalSetting.KnifeEnabled)
|
||||||
Utility.LoadGlovesFromFile(ModuleDirectory + $"/data/gloves_{_config.SkinsLanguage}.json", Logger);
|
|
||||||
Utility.LoadAgentsFromFile(ModuleDirectory + $"/data/agents_{_config.SkinsLanguage}.json", Logger);
|
|
||||||
Utility.LoadMusicFromFile(ModuleDirectory + $"/data/music_{_config.SkinsLanguage}.json", Logger);
|
|
||||||
|
|
||||||
if (Config.Additional.KnifeEnabled)
|
|
||||||
SetupKnifeMenu();
|
SetupKnifeMenu();
|
||||||
if (Config.Additional.SkinEnabled)
|
if (Config.AdditionalSetting.SkinEnabled)
|
||||||
SetupSkinsMenu();
|
SetupSkinsMenu();
|
||||||
if (Config.Additional.GloveEnabled)
|
|
||||||
SetupGlovesMenu();
|
|
||||||
if (Config.Additional.AgentEnabled)
|
|
||||||
SetupAgentsMenu();
|
|
||||||
if (Config.Additional.MusicEnabled)
|
|
||||||
SetupMusicMenu();
|
|
||||||
|
|
||||||
RegisterListeners();
|
RegisterListeners();
|
||||||
RegisterCommands();
|
RegisterCommands();
|
||||||
|
|
||||||
|
Utility.LoadSkinsFromFile(ModuleDirectory + "/skins.json");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnConfigParsed(WeaponPaintsConfig config)
|
public void OnConfigParsed(WeaponPaintsConfig config)
|
||||||
{
|
{
|
||||||
|
if (!config.GlobalShare)
|
||||||
|
{
|
||||||
|
if (config.DatabaseHost.Length < 1 || config.DatabaseName.Length < 1 || config.DatabaseUser.Length < 1)
|
||||||
|
{
|
||||||
|
// maybe more spam to get attention?
|
||||||
|
for (int i = 1; i <= 30; i++)
|
||||||
|
{
|
||||||
|
Console.WriteLine("[WeaponPaints] You need to setup Database credentials in config!");
|
||||||
|
}
|
||||||
|
Logger.LogError("You need to setup Database credentials in config!");
|
||||||
|
throw new Exception("[WeaponPaints] You need to setup Database credentials in config!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// maybe more spam to get attention?
|
||||||
|
for (int i = 1; i <= 30; i++)
|
||||||
|
{
|
||||||
|
Console.WriteLine("[WeaponPaints] GLOBAL SHARE IS NOT SUPPORTED NOW !!");
|
||||||
|
}
|
||||||
|
Logger.LogError("GLOBAL SHARE IS NOT SUPPORTED NOW !!");
|
||||||
|
throw new Exception("[WeaponPaints] GLOBAL SHARE IS NOT SUPPORTED NOW !!");
|
||||||
|
}
|
||||||
|
|
||||||
Config = config;
|
Config = config;
|
||||||
_config = config;
|
_config = config;
|
||||||
|
|
||||||
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\"!");
|
|
||||||
Unload(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!File.Exists(Path.GetDirectoryName(Path.GetDirectoryName(ModuleDirectory)) + "/gamedata/weaponpaints.json"))
|
|
||||||
{
|
|
||||||
Logger.LogError("You need to upload \"weaponpaints.json\" to \"gamedata directory\"!");
|
|
||||||
Unload(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var builder = new MySqlConnectionStringBuilder
|
|
||||||
{
|
|
||||||
Server = config.DatabaseHost,
|
|
||||||
UserID = config.DatabaseUser,
|
|
||||||
Password = config.DatabasePassword,
|
|
||||||
Database = config.DatabaseName,
|
|
||||||
Port = (uint)config.DatabasePort,
|
|
||||||
Pooling = true,
|
|
||||||
MaximumPoolSize = 640,
|
|
||||||
};
|
|
||||||
|
|
||||||
Database = new Database(builder.ConnectionString);
|
|
||||||
|
|
||||||
_ = Utility.CheckDatabaseTables();
|
|
||||||
_localizer = Localizer;
|
_localizer = Localizer;
|
||||||
|
|
||||||
Utility.Config = config;
|
/*
|
||||||
|
* GLOBAL SHARE IS NOT SUPPORTED NOW!
|
||||||
|
*/
|
||||||
|
if (Config.GlobalShare)
|
||||||
|
Config.GlobalShare = false;
|
||||||
|
|
||||||
|
Utility.Config = config;
|
||||||
Utility.ShowAd(ModuleVersion);
|
Utility.ShowAd(ModuleVersion);
|
||||||
Task.Run(async () => await Utility.CheckVersion(ModuleVersion, Logger));
|
Task.Run(async () => await Utility.CheckVersion(ModuleVersion, Logger));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void OnAllPluginsLoaded(bool hotReload)
|
public override void Unload(bool hotReload)
|
||||||
{
|
{
|
||||||
try
|
base.Unload(hotReload);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GlobalShareConnect()
|
||||||
|
{
|
||||||
|
if (!Config.GlobalShare) return;
|
||||||
|
|
||||||
|
var values = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "server_address", $"{ConVar.Find("ip")!.StringValue}:{ConVar.Find("hostport")!.GetPrimitiveValue<int>().ToString()}" },
|
||||||
|
{ "server_hostname", ConVar.Find("hostname")!.StringValue }
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var httpClient = new HttpClient())
|
||||||
{
|
{
|
||||||
MenuApi = MenuCapability.Get();
|
httpClient.BaseAddress = GlobalShareApi;
|
||||||
}
|
var formContent = new FormUrlEncodedContent(values);
|
||||||
catch (Exception)
|
|
||||||
{
|
Task<HttpResponseMessage> responseTask = httpClient.PostAsync("", formContent);
|
||||||
MenuApi = null;
|
responseTask.Wait();
|
||||||
Logger.LogError("Error while loading required plugins");
|
HttpResponseMessage response = responseTask.Result;
|
||||||
throw;
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
Task<string> responseBodyTask = response.Content.ReadAsStringAsync();
|
||||||
|
responseBodyTask.Wait();
|
||||||
|
string responseBody = responseBodyTask.Result;
|
||||||
|
GlobalShareServerId = Int32.Parse(responseBody);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.LogError("Unable to retrieve serverid from GlobalShare!");
|
||||||
|
throw new Exception("[WeaponPaints] Unable to retrieve serverid from GlobalShare!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Logger.LogInformation("GlobalShare ONLINE!");
|
||||||
|
Console.WriteLine("[WeaponPaints] GlobalShare ONLINE");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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,23 +9,13 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.281" />
|
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.148" />
|
||||||
<PackageReference Include="Dapper" Version="2.1.35" />
|
<PackageReference Include="Dapper" Version="2.1.28" />
|
||||||
<PackageReference Include="MySqlConnector" Version="2.4.0-beta.1" />
|
<PackageReference Include="MySqlConnector" Version="2.3.4" />
|
||||||
<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>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="MenuManagerApi">
|
|
||||||
<HintPath>3rd_party\MenuManagerApi.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
|
||||||
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=C_003A_005CUsers_005Cxdaff_005CDocuments_005CGitHub_005Ccs2_002DWeaponPaints_005C3rd_005Fparty_005CMenuManagerApi_002Edll/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
|
||||||
@@ -1,353 +1,331 @@
|
|||||||
using Dapper;
|
using CounterStrikeSharp.API.Core;
|
||||||
|
using CounterStrikeSharp.API.Modules.Entities;
|
||||||
|
using Dapper;
|
||||||
using MySqlConnector;
|
using MySqlConnector;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
|
||||||
namespace WeaponPaints
|
namespace WeaponPaints
|
||||||
{
|
{
|
||||||
internal class WeaponSynchronization
|
internal class WeaponSynchronization
|
||||||
{
|
{
|
||||||
private readonly WeaponPaintsConfig _config;
|
private readonly WeaponPaintsConfig _config;
|
||||||
private readonly Database _database;
|
private readonly string _databaseConnectionString;
|
||||||
|
private readonly Uri _globalShareApi;
|
||||||
|
private readonly int _globalShareServerId;
|
||||||
|
|
||||||
internal WeaponSynchronization(Database database, WeaponPaintsConfig config)
|
internal WeaponSynchronization(string databaseConnectionString, WeaponPaintsConfig config, Uri globalShareApi, int globalShareServerId)
|
||||||
{
|
{
|
||||||
_database = database;
|
_databaseConnectionString = databaseConnectionString;
|
||||||
_config = config;
|
_config = config;
|
||||||
|
_globalShareApi = globalShareApi;
|
||||||
|
_globalShareServerId = globalShareServerId;
|
||||||
}
|
}
|
||||||
|
internal async Task GetPlayerDatabaseIndex(PlayerInfo player)
|
||||||
internal async Task GetPlayerData(PlayerInfo? player)
|
|
||||||
{
|
{
|
||||||
try
|
if (player.SteamId == null || player.Index == 0) return;
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
await using var connection = await _database.GetConnectionAsync();
|
using (var connection = new MySqlConnection(_databaseConnectionString))
|
||||||
|
|
||||||
if (_config.Additional.KnifeEnabled)
|
|
||||||
GetKnifeFromDatabase(player, connection);
|
|
||||||
if (_config.Additional.GloveEnabled)
|
|
||||||
GetGloveFromDatabase(player, connection);
|
|
||||||
if (_config.Additional.AgentEnabled)
|
|
||||||
GetAgentFromDatabase(player, connection);
|
|
||||||
if (_config.Additional.MusicEnabled)
|
|
||||||
GetMusicFromDatabase(player, connection);
|
|
||||||
if (_config.Additional.SkinEnabled)
|
|
||||||
GetWeaponPaintsFromDatabase(player, connection);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
// Log the exception or handle it appropriately
|
|
||||||
Console.WriteLine($"An error occurred: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GetKnifeFromDatabase(PlayerInfo? player, MySqlConnection connection)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!_config.Additional.KnifeEnabled || string.IsNullOrEmpty(player?.SteamId))
|
|
||||||
return;
|
|
||||||
|
|
||||||
const string query = "SELECT `knife` FROM `wp_player_knife` WHERE `steamid` = @steamid";
|
|
||||||
var playerKnife = connection.QueryFirstOrDefault<string>(query, new { steamid = player.SteamId });
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(playerKnife))
|
|
||||||
{
|
{
|
||||||
WeaponPaints.GPlayersKnife[player.Slot] = playerKnife;
|
await connection.OpenAsync();
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Utility.Log($"An error occurred in GetKnifeFromDatabase: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GetGloveFromDatabase(PlayerInfo? player, MySqlConnection connection)
|
string query = "SELECT `id` FROM `wp_users` WHERE `steamid` = @steamid";
|
||||||
|
int? databaseIndex = await connection.QueryFirstOrDefaultAsync<int?>(query, new { steamid = player.SteamId });
|
||||||
|
if (databaseIndex != null)
|
||||||
|
{
|
||||||
|
WeaponPaints.g_playersDatabaseIndex[player.Index] = (int)databaseIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string insertQuery = "INSERT INTO `wp_users` (`steamid`) VALUES (@steamid)";
|
||||||
|
await connection.ExecuteAsync(insertQuery, new { steamid = player.SteamId });
|
||||||
|
Console.WriteLine("SQL Insert Query: " + insertQuery);
|
||||||
|
databaseIndex = await connection.QueryFirstOrDefaultAsync<int?>(query, new { steamid = player.SteamId });
|
||||||
|
WeaponPaints.g_playersDatabaseIndex[(int)player.Index] = (int)databaseIndex;
|
||||||
|
}
|
||||||
|
await connection.CloseAsync();
|
||||||
|
|
||||||
|
if (databaseIndex != null)
|
||||||
|
{
|
||||||
|
if (_config.AdditionalSetting.SkinEnabled)
|
||||||
|
await GetWeaponPaintsFromDatabase(player);
|
||||||
|
if (_config.AdditionalSetting.KnifeEnabled)
|
||||||
|
await GetKnifeFromDatabase(player);
|
||||||
|
if (_config.AdditionalSetting.MusicKitEnabled)
|
||||||
|
await GetMusicKitFromDatabase(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Utility.Log("GetPlayerDatabaseIndex: " + e.Message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
internal async Task GetKnifeFromDatabase(PlayerInfo player)
|
||||||
{
|
{
|
||||||
|
if (!_config.AdditionalSetting.KnifeEnabled) return;
|
||||||
|
if (player.SteamId == null || player.Index == 0) return;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!_config.Additional.GloveEnabled || string.IsNullOrEmpty(player?.SteamId))
|
if (_config.GlobalShare)
|
||||||
return;
|
|
||||||
|
|
||||||
const string query = "SELECT `weapon_defindex` FROM `wp_player_gloves` WHERE `steamid` = @steamid";
|
|
||||||
var gloveData = connection.QueryFirstOrDefault<ushort?>(query, new { steamid = player.SteamId });
|
|
||||||
|
|
||||||
if (gloveData != null)
|
|
||||||
{
|
{
|
||||||
WeaponPaints.GPlayersGlove[player.Slot] = gloveData.Value;
|
var values = new Dictionary<string, string>
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Utility.Log($"An error occurred in GetGloveFromDatabase: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GetAgentFromDatabase(PlayerInfo? player, MySqlConnection connection)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!_config.Additional.AgentEnabled || string.IsNullOrEmpty(player?.SteamId))
|
|
||||||
return;
|
|
||||||
|
|
||||||
const string query = "SELECT `agent_ct`, `agent_t` FROM `wp_player_agents` WHERE `steamid` = @steamid";
|
|
||||||
var agentData = connection.QueryFirstOrDefault<(string, string)>(query, new { steamid = player.SteamId });
|
|
||||||
|
|
||||||
if (agentData == default) return;
|
|
||||||
var agentCT = agentData.Item1;
|
|
||||||
var agentT = agentData.Item2;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(agentCT) || !string.IsNullOrEmpty(agentT))
|
|
||||||
{
|
|
||||||
WeaponPaints.GPlayersAgent[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>();
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
int weaponDefIndex = row?.weapon_defindex ?? 0;
|
|
||||||
int weaponPaintId = row?.weapon_paint_id ?? 0;
|
|
||||||
float weaponWear = row?.weapon_wear ?? 0f;
|
|
||||||
int weaponSeed = row?.weapon_seed ?? 0;
|
|
||||||
string weaponNameTag = row?.weapon_nametag ?? "";
|
|
||||||
|
|
||||||
string[]? keyChainParts = row?.weapon_keychain?.ToString().Split(';');
|
|
||||||
|
|
||||||
KeyChainInfo keyChainInfo = new KeyChainInfo();
|
|
||||||
|
|
||||||
if (keyChainParts!.Length == 5 &&
|
|
||||||
uint.TryParse(keyChainParts[0], out uint keyChainId) &&
|
|
||||||
float.TryParse(keyChainParts[1], out float keyChainOffsetX) &&
|
|
||||||
float.TryParse(keyChainParts[2], out float keyChainOffsetY) &&
|
|
||||||
float.TryParse(keyChainParts[3], out float keyChainOffsetZ) &&
|
|
||||||
uint.TryParse(keyChainParts[4], out uint keyChainSeed))
|
|
||||||
{
|
{
|
||||||
// Successfully parsed the values
|
{ "server_id", _globalShareServerId.ToString() },
|
||||||
keyChainInfo.Id = keyChainId;
|
{ "steamid", player.SteamId.ToString()! },
|
||||||
keyChainInfo.OffsetX = keyChainOffsetX;
|
{ "knife", "1" }
|
||||||
keyChainInfo.OffsetY = keyChainOffsetY;
|
};
|
||||||
keyChainInfo.OffsetZ = keyChainOffsetZ;
|
|
||||||
keyChainInfo.Seed = keyChainSeed;
|
UriBuilder builder = new UriBuilder(_globalShareApi);
|
||||||
|
builder.Query = string.Join("&", values.Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}"));
|
||||||
|
|
||||||
|
using (var httpClient = new HttpClient())
|
||||||
|
{
|
||||||
|
httpClient.BaseAddress = _globalShareApi;
|
||||||
|
var formContent = new FormUrlEncodedContent(values);
|
||||||
|
HttpResponseMessage response = await httpClient.GetAsync(builder.Uri);
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
string result = await response.Content.ReadAsStringAsync();
|
||||||
|
if (!string.IsNullOrEmpty(result))
|
||||||
|
{
|
||||||
|
WeaponPaints.g_playersKnife[player.Index] = result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!WeaponPaints.g_playersDatabaseIndex.TryGetValue(player.Index, out _))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var connection = new MySqlConnection(_databaseConnectionString))
|
||||||
|
{
|
||||||
|
await connection.OpenAsync();
|
||||||
|
string query = "SELECT `knife` FROM `wp_users_knife` WHERE `user_id` = @userId";
|
||||||
|
string? PlayerKnife = await connection.QueryFirstOrDefaultAsync<string>(query, new { userId = WeaponPaints.g_playersDatabaseIndex[player.Index] });
|
||||||
|
if (PlayerKnife != null)
|
||||||
|
{
|
||||||
|
WeaponPaints.g_playersKnife[player.Index] = PlayerKnife;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Failed to parse the values, default to 0
|
return;
|
||||||
keyChainInfo.Id = 0;
|
|
||||||
keyChainInfo.OffsetX = 0f;
|
|
||||||
keyChainInfo.OffsetY = 0f;
|
|
||||||
keyChainInfo.OffsetZ = 0f;
|
|
||||||
keyChainInfo.Seed = 0;
|
|
||||||
}
|
}
|
||||||
|
await connection.CloseAsync();
|
||||||
// Create the WeaponInfo object
|
|
||||||
WeaponInfo weaponInfo = new WeaponInfo
|
|
||||||
{
|
|
||||||
Paint = weaponPaintId,
|
|
||||||
Seed = weaponSeed,
|
|
||||||
Wear = weaponWear,
|
|
||||||
Nametag = weaponNameTag,
|
|
||||||
KeyChain = keyChainInfo
|
|
||||||
};
|
|
||||||
|
|
||||||
// Retrieve and parse sticker data (up to 5 slots)
|
|
||||||
for (int i = 0; i <= 4; i++)
|
|
||||||
{
|
|
||||||
// Access the sticker data dynamically using reflection
|
|
||||||
string stickerColumn = $"weapon_sticker_{i}";
|
|
||||||
var stickerData = ((IDictionary<string, object>)row!)[stickerColumn]; // Safely cast row to a dictionary
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(stickerData.ToString())) continue;
|
|
||||||
|
|
||||||
var parts = stickerData.ToString()!.Split(';');
|
|
||||||
|
|
||||||
//"id;schema;x;y;wear;scale;rotation"
|
|
||||||
if (parts.Length != 7 ||
|
|
||||||
!uint.TryParse(parts[0], out uint stickerId) ||
|
|
||||||
!uint.TryParse(parts[1], out uint stickerSchema) ||
|
|
||||||
!float.TryParse(parts[2], out float stickerOffsetX) ||
|
|
||||||
!float.TryParse(parts[3], out float stickerOffsetY) ||
|
|
||||||
!float.TryParse(parts[4], out float stickerWear) ||
|
|
||||||
!float.TryParse(parts[5], out float stickerScale) ||
|
|
||||||
!float.TryParse(parts[6], out float stickerRotation)) continue;
|
|
||||||
|
|
||||||
StickerInfo stickerInfo = new StickerInfo
|
|
||||||
{
|
|
||||||
Id = stickerId,
|
|
||||||
Schema = stickerSchema,
|
|
||||||
OffsetX = stickerOffsetX,
|
|
||||||
OffsetY = stickerOffsetY,
|
|
||||||
Wear = stickerWear,
|
|
||||||
Scale = stickerScale,
|
|
||||||
Rotation = stickerRotation
|
|
||||||
};
|
|
||||||
|
|
||||||
weaponInfo.Stickers.Add(stickerInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
weaponInfos[weaponDefIndex] = weaponInfo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WeaponPaints.GPlayerWeaponsInfo[player.Slot] = weaponInfos;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Utility.Log($"An error occurred in GetWeaponPaintsFromDatabase: {ex.Message}");
|
Utility.Log("GetKnifeFromDatabase: " + e.Message);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
internal async Task GetMusicKitFromDatabase(PlayerInfo player)
|
||||||
|
{
|
||||||
|
if (!_config.AdditionalSetting.MusicKitEnabled) return;
|
||||||
|
if (player.SteamId == null || player.Index == 0) return;
|
||||||
|
if (!WeaponPaints.g_playersDatabaseIndex.TryGetValue(player.Index, out _))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var connection = new MySqlConnection(_databaseConnectionString))
|
||||||
|
{
|
||||||
|
await connection.OpenAsync();
|
||||||
|
string query = "SELECT `music` FROM `wp_users_music` WHERE `user_id` = @userId";
|
||||||
|
int? PlayerMusitKit = await connection.QueryFirstOrDefaultAsync<int?>(query, new { userId = WeaponPaints.g_playersDatabaseIndex[player.Index] });
|
||||||
|
if (PlayerMusitKit != null)
|
||||||
|
{
|
||||||
|
WeaponPaints.g_playersMusicKit[player.Index] = PlayerMusitKit;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await connection.CloseAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Utility.Log("GetMusicKitFromDatabase: " + e.Message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void GetMusicFromDatabase(PlayerInfo? player, MySqlConnection connection)
|
internal async Task GetWeaponPaintsFromDatabase(PlayerInfo player)
|
||||||
{
|
{
|
||||||
try
|
if (!_config.AdditionalSetting.SkinEnabled) return;
|
||||||
|
if (player.SteamId == null || player.Index == 0) return;
|
||||||
|
|
||||||
|
if (!WeaponPaints.gPlayerWeaponsInfo.TryGetValue(player.Index, out _))
|
||||||
|
{
|
||||||
|
WeaponPaints.gPlayerWeaponsInfo[player.Index] = new ConcurrentDictionary<ushort, WeaponInfo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (!_config.Additional.MusicEnabled || string.IsNullOrEmpty(player?.SteamId))
|
if (_config.GlobalShare)
|
||||||
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.GPlayersMusic[player.Slot] = musicData.Value;
|
var values = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "server_id", _globalShareServerId.ToString() },
|
||||||
|
{ "steamid", player.SteamId.ToString()! },
|
||||||
|
{ "skins", "1" }
|
||||||
|
};
|
||||||
|
UriBuilder builder = new UriBuilder(_globalShareApi);
|
||||||
|
builder.Query = string.Join("&", values.Select(p => $"{Uri.EscapeDataString(p.Key)}={Uri.EscapeDataString(p.Value)}"));
|
||||||
|
|
||||||
|
using (var httpClient = new HttpClient())
|
||||||
|
{
|
||||||
|
httpClient.BaseAddress = _globalShareApi;
|
||||||
|
var formContent = new FormUrlEncodedContent(values);
|
||||||
|
HttpResponseMessage response = await httpClient.GetAsync(builder.Uri);
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
string responseBody = await response.Content.ReadAsStringAsync();
|
||||||
|
JArray jsonArray = JArray.Parse(responseBody);
|
||||||
|
if (jsonArray != null && jsonArray.Count > 0)
|
||||||
|
{
|
||||||
|
foreach (var weapon in jsonArray)
|
||||||
|
{
|
||||||
|
ushort? weaponDefIndex = weapon["weapon_defindex"]?.Value<ushort>();
|
||||||
|
ushort? weaponPaintId = weapon["weapon_paint_id"]?.Value<ushort>();
|
||||||
|
float? weaponWear = weapon["weapon_wear"]?.Value<float>();
|
||||||
|
ushort? weaponSeed = weapon["weapon_seed"]?.Value<ushort>();
|
||||||
|
|
||||||
|
if (weaponDefIndex != null && weaponPaintId != null && weaponWear != null && weaponSeed != null)
|
||||||
|
{
|
||||||
|
WeaponInfo weaponInfo = new WeaponInfo
|
||||||
|
{
|
||||||
|
Paint = weaponPaintId.Value,
|
||||||
|
Seed = weaponSeed.Value,
|
||||||
|
Wear = weaponWear.Value,
|
||||||
|
NameTag = null
|
||||||
|
};
|
||||||
|
WeaponPaints.gPlayerWeaponsInfo[player.Index][weaponDefIndex.Value] = weaponInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!WeaponPaints.g_playersDatabaseIndex.TryGetValue(player.Index, out _))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var connection = new MySqlConnection(_databaseConnectionString))
|
||||||
|
{
|
||||||
|
await connection.OpenAsync();
|
||||||
|
string query = "SELECT `weapon`, `paint`, `wear`, `seed`, `nametag` FROM `wp_users_items` WHERE `user_id` = @userId";
|
||||||
|
IEnumerable<dynamic> PlayerSkins = await connection.QueryAsync<dynamic>(query, new { userId = WeaponPaints.g_playersDatabaseIndex[player.Index] });
|
||||||
|
if (PlayerSkins != null && PlayerSkins.Any())
|
||||||
|
{
|
||||||
|
PlayerSkins.ToList().ForEach(row =>
|
||||||
|
{
|
||||||
|
ushort weaponDefIndex = row.weapon ?? default(ushort);
|
||||||
|
ushort weaponPaintId = row.paint ?? default(ushort);
|
||||||
|
float weaponWear = row.wear ?? default(float);
|
||||||
|
ushort weaponSeed = row.seed ?? default(ushort);
|
||||||
|
string weaponNameTag = row.nametag;
|
||||||
|
|
||||||
|
WeaponInfo weaponInfo = new WeaponInfo
|
||||||
|
{
|
||||||
|
Paint = weaponPaintId,
|
||||||
|
Seed = weaponSeed,
|
||||||
|
Wear = weaponWear,
|
||||||
|
NameTag = weaponNameTag
|
||||||
|
};
|
||||||
|
WeaponPaints.gPlayerWeaponsInfo[player.Index][weaponDefIndex] = weaponInfo;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await connection.CloseAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Utility.Log($"An error occurred in GetMusicFromDatabase: {ex.Message}");
|
Utility.Log("GetWeaponPaintsFromDatabase: " + e.Message);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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.AdditionalSetting.KnifeEnabled) return;
|
||||||
|
if(player == null || player.Index <= 0) 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();
|
if (!WeaponPaints.g_playersDatabaseIndex.TryGetValue(player.Index, out _))
|
||||||
await connection.ExecuteAsync(query, new { steamid = player.SteamId, newKnife = knife });
|
return;
|
||||||
|
|
||||||
|
using var connection = new MySqlConnection(_databaseConnectionString);
|
||||||
|
await connection.OpenAsync();
|
||||||
|
string query = "INSERT INTO `wp_users_knife` (`user_id`, `knife`) VALUES(@userId, @newKnife) ON DUPLICATE KEY UPDATE `knife` = @newKnife";
|
||||||
|
await connection.ExecuteAsync(query, new { userId = WeaponPaints.g_playersDatabaseIndex[player.Index], newKnife = knife });
|
||||||
|
await connection.CloseAsync();
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
Utility.Log($"Error syncing knife to database: {e.Message}");
|
Utility.Log(e.Message);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task SyncGloveToDatabase(PlayerInfo player, int defindex)
|
|
||||||
{
|
|
||||||
if (!_config.Additional.GloveEnabled || string.IsNullOrEmpty(player.SteamId)) return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
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";
|
|
||||||
await connection.ExecuteAsync(query, new { steamid = player.SteamId, weapon_defindex = defindex });
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Utility.Log($"Error syncing glove to database: {e.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.GPlayersAgent[player.Slot].CT, agent_t = WeaponPaints.GPlayersAgent[player.Slot].T });
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Utility.Log($"Error syncing agents to database: {e.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal async Task SyncWeaponPaintsToDatabase(PlayerInfo player)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(player.SteamId) || !WeaponPaints.GPlayerWeaponsInfo.TryGetValue(player.Slot, out var weaponsInfo))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await using var connection = await _database.GetConnectionAsync();
|
|
||||||
|
|
||||||
foreach (var (weaponDefIndex, weaponInfo) in weaponsInfo)
|
|
||||||
{
|
|
||||||
var paintId = weaponInfo.Paint;
|
|
||||||
var wear = weaponInfo.Wear;
|
|
||||||
var seed = weaponInfo.Seed;
|
|
||||||
|
|
||||||
const string queryCheckExistence = "SELECT COUNT(*) FROM `wp_player_skins` WHERE `steamid` = @steamid AND `weapon_defindex` = @weaponDefIndex";
|
|
||||||
|
|
||||||
var existingRecordCount = await connection.ExecuteScalarAsync<int>(queryCheckExistence, new { steamid = player.SteamId, weaponDefIndex = weaponDefIndex });
|
|
||||||
|
|
||||||
string query;
|
|
||||||
object parameters;
|
|
||||||
|
|
||||||
if (existingRecordCount > 0)
|
|
||||||
{
|
|
||||||
query = "UPDATE `wp_player_skins` SET `weapon_paint_id` = @paintId, `weapon_wear` = @wear, `weapon_seed` = @seed WHERE `steamid` = @steamid AND `weapon_defindex` = @weaponDefIndex";
|
|
||||||
parameters = new { steamid = player.SteamId, weaponDefIndex = weaponDefIndex, paintId, wear, seed };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
query = "INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed`) " +
|
|
||||||
"VALUES (@steamid, @weaponDefIndex, @paintId, @wear, @seed)";
|
|
||||||
parameters = new { steamid = player.SteamId, weaponDefIndex = weaponDefIndex, paintId, wear, seed };
|
|
||||||
}
|
|
||||||
|
|
||||||
await connection.ExecuteAsync(query, parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Utility.Log($"Error syncing weapon paints to database: {e.Message}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
internal async Task SyncWeaponPaintToDatabase(PlayerInfo player, ushort weaponDefIndex)
|
||||||
internal async Task SyncMusicToDatabase(PlayerInfo player, ushort music)
|
|
||||||
{
|
{
|
||||||
if (!_config.Additional.MusicEnabled || string.IsNullOrEmpty(player.SteamId)) return;
|
if (!_config.AdditionalSetting.SkinEnabled) return;
|
||||||
|
if (player == null || player.Index <= 0) return;
|
||||||
|
|
||||||
try
|
if (!WeaponPaints.g_playersDatabaseIndex.TryGetValue(player.Index, out var playerDatabaseIndex))
|
||||||
{
|
return;
|
||||||
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";
|
if (!WeaponPaints.gPlayerWeaponsInfo.TryGetValue(player.Index, out var playerSavedWeapons))
|
||||||
await connection.ExecuteAsync(query, new { steamid = player.SteamId, newMusic = music });
|
return;
|
||||||
}
|
|
||||||
catch (Exception e)
|
if (!playerSavedWeapons.TryGetValue(weaponDefIndex, out var weaponInfo))
|
||||||
{
|
return;
|
||||||
Utility.Log($"Error syncing music kit to database: {e.Message}");
|
|
||||||
}
|
using var connection = new MySqlConnection(_databaseConnectionString);
|
||||||
|
string querySql = @"
|
||||||
|
INSERT INTO `wp_users_items`
|
||||||
|
(`user_id`, `weapon`, `paint`, `wear`, `seed`)
|
||||||
|
VALUES
|
||||||
|
(@userId, @weaponDefIndex, @paintId, @wear, @seed)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
paint = @paintId,
|
||||||
|
wear = @wear,
|
||||||
|
seed = @seed";
|
||||||
|
var queryParams = new { weaponDefIndex, userId = playerDatabaseIndex, paintId = weaponInfo.Paint, wear = weaponInfo.Wear, seed = weaponInfo.Seed };
|
||||||
|
await connection.ExecuteAsync(querySql, queryParams);
|
||||||
|
await connection.CloseAsync();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
15
lang/en.json
15
lang/en.json
@@ -3,23 +3,12 @@
|
|||||||
"wp_info_website": "Visit {lime}{0}{default} where you can change skins",
|
"wp_info_website": "Visit {lime}{0}{default} where you can change skins",
|
||||||
"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_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",
|
||||||
"wp_knife_menu_kill": "",
|
"wp_knife_menu_kill": "To correctly apply skin for knife, you need to type {lime}!kill{default}",
|
||||||
"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_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"
|
|
||||||
}
|
}
|
||||||
35
lang/lv.json
35
lang/lv.json
@@ -1,25 +1,14 @@
|
|||||||
{
|
{
|
||||||
"wp_prefix": "{lightblue}[WeaponPaints] {default}",
|
"wp_prefix": "{lightblue}[Ieroču Ādiņas] {default}",
|
||||||
"wp_info_website": "Apmeklē {lime}{0}{default}, kur varat mainīt ādas",
|
"wp_info_website": "Apmeklē {lime}{0}{default} kur tu vari nomainīt skinus",
|
||||||
"wp_info_refresh": "Ievadiet {lime}!wp{default}, lai sinhronizētu izvēlētās ādas",
|
"wp_info_refresh": "Raksti {lime}!wp{default} lai sinhronizētu izvēlētos skinus",
|
||||||
"wp_info_knife": "Ievadiet {lime}!knife{default}, lai atvērtu nazis izvēlni",
|
"wp_info_knife": "Raksti {lime}!knife{default} lai atvērtu nažu izvēlni",
|
||||||
"wp_info_glove": "Ievadiet {lime}!gloves{default}, lai atvērtu cimdi izvēlni",
|
"wp_command_cooldown": "{lightred} Tu šobrīd nevari atjaunot ieroču skinus...",
|
||||||
"wp_info_agent": "Ievadiet {lime}!agents{default}, lai atvērtu aģentu izvēlni",
|
"wp_command_refresh_done": "{lime}Izvēlētie skini tiek atjaunoti",
|
||||||
"wp_info_music": "Ievadiet {lime}!music{default}, lai atvērtu mūzikas izvēlni",
|
"wp_knife_menu_select": "Tu esi izvēlējies {lime}{0}{default} nazi",
|
||||||
"wp_command_cooldown": "{lightred}Šobrīd nevarat atsvaidzināt ieroča krāsas",
|
"wp_knife_menu_kill": "Lai pareizi atjaunotu naža skinu, ieraksti čatā {lime}!kill{default}",
|
||||||
"wp_command_refresh_done": "{lime}Atsvaidzinot ieroča krāsas",
|
"wp_knife_menu_title": "Nažu Izvēlne",
|
||||||
"wp_knife_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu nazi",
|
|
||||||
"wp_knife_menu_kill": "",
|
|
||||||
"wp_knife_menu_title": "Nazi Izvēlne",
|
|
||||||
"wp_glove_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu cimdu",
|
|
||||||
"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ēlies skinu ierocim: {lime}{0}{default}",
|
||||||
"wp_skin_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu ādu",
|
"wp_skin_menu_select": "Tu esi izvēlējies {lime}{0}{default} kā savu skinu"
|
||||||
|
}
|
||||||
"None": "Nav"
|
|
||||||
}
|
|
||||||
|
|||||||
31
lang/pl.json
31
lang/pl.json
@@ -1,25 +1,14 @@
|
|||||||
{
|
{
|
||||||
"wp_prefix": "{lightblue}[WeaponPaints] {default}",
|
"wp_prefix": "{lightblue}[WeaponPaints] {default}",
|
||||||
"wp_info_website": "Odwiedź {lime}{0}{default}, gdzie możesz zmieniać skórki",
|
"wp_info_website": "Odwiedź {lime}{0}{default} gdzie będziesz mógł ustawić skiny",
|
||||||
"wp_info_refresh": "Wpisz {lime}!wp{default}, aby zsynchronizować wybrane skórki",
|
"wp_info_refresh": "Wpisz {lime}!wp{default} aby zsynchronizować swoje skiny",
|
||||||
"wp_info_knife": "Wpisz {lime}!knife{default}, aby otworzyć menu noży",
|
"wp_info_knife": "Wpisz {lime}!knife{default} aby wy<EFBFBD>wietlić menu no<EFBFBD>y",
|
||||||
"wp_info_glove": "Wpisz {lime}!gloves{default}, aby otworzyć menu rękawic",
|
"wp_command_cooldown": "{lightred}Odczekaj chwilę przed wykonaniem tej komendy...",
|
||||||
"wp_info_agent": "Wpisz {lime}!agents{default}, aby otworzyć menu agentów",
|
"wp_command_refresh_done": "{lime}Pomyslnie zsynchronizowano twoje skiny",
|
||||||
"wp_info_music": "Wpisz {lime}!music{default}, aby otworzyć menu muzyczne",
|
|
||||||
"wp_command_cooldown": "{lightred}Nie możesz teraz odświeżyć kolorów broni",
|
|
||||||
"wp_command_refresh_done": "{lime}Odświeżanie kolorów broni",
|
|
||||||
"wp_knife_menu_select": "Wybrałeś {lime}{0}{default} jako swój nóż",
|
"wp_knife_menu_select": "Wybrałeś {lime}{0}{default} jako swój nóż",
|
||||||
"wp_knife_menu_kill": "",
|
"wp_knife_menu_kill": "Do prawidłowego zastosowania noża użyj {lime}!kill{default}",
|
||||||
"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_skin_menu_weapon_title": "Menu broni",
|
||||||
"wp_glove_menu_title": "Menu Rękawiczek",
|
"wp_skin_menu_skin_title": "Wybierz skin dla {lime}{0}{default}",
|
||||||
"wp_agent_menu_select": "Wybrałeś {lime}{0}{default} jako swojego agenta",
|
"wp_skin_menu_select": "Wybrałeś {lime}{0}{default} jako swój skin"
|
||||||
"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_skin_title": "Wybierz skórkę dla {lime}{0}{default}",
|
|
||||||
"wp_skin_menu_select": "Wybrałeś {lime}{0}{default} jako swoją skórkę",
|
|
||||||
|
|
||||||
"None": "Brak"
|
|
||||||
}
|
}
|
||||||
@@ -1,25 +1,14 @@
|
|||||||
{
|
{
|
||||||
"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} para mudar suas skins e faca",
|
||||||
"wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins escolhidas",
|
"wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as suas skins",
|
||||||
"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_command_cooldown": "{lightred}Você não pode atualizar as skins das armas agora",
|
||||||
"wp_info_agent": "Digite {lime}!agents{default} para abrir o menu de agentes",
|
"wp_command_refresh_done": "{lime}Sincronizando as suas skins",
|
||||||
"wp_info_music": "Digite {lime}!music{default} para abrir o menu de música",
|
|
||||||
"wp_command_cooldown": "{lightred}Você não pode atualizar as skins de armas agora",
|
|
||||||
"wp_command_refresh_done": "{lime}Atualizando as skins de armas",
|
|
||||||
"wp_knife_menu_select": "Você escolheu {lime}{0}{default} como sua faca",
|
"wp_knife_menu_select": "Você escolheu {lime}{0}{default} como sua faca",
|
||||||
"wp_knife_menu_kill": "",
|
"wp_knife_menu_kill": "Para aplicar corretamente a skin da faca, você precisa digitar {lime}!kill{default}",
|
||||||
"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_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": "Selecionou a skin para {lime}{0}{default}",
|
||||||
"wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin",
|
"wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin"
|
||||||
|
}
|
||||||
"None": "Nenhum"
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,25 +1,14 @@
|
|||||||
{
|
{
|
||||||
"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": "Visita {lime}{0}{default} onde podes mudar as tuas skins",
|
||||||
"wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins escolhidas",
|
"wp_info_refresh": "Digita {lime}!wp{default} para sincronizar as tuas skins",
|
||||||
"wp_info_knife": "Digite {lime}!knife{default} para abrir o menu de facas",
|
"wp_info_knife": "Digita {lime}!knife{default} para abrir o menu de facas",
|
||||||
"wp_info_glove": "Digite {lime}!gloves{default} para abrir o menu de luvas",
|
"wp_command_cooldown": "{lightred}Tu não podes sincronizar agora as tuas skins",
|
||||||
"wp_info_agent": "Digite {lime}!agents{default} para abrir o menu de agentes",
|
"wp_command_refresh_done": "{lime}Sincronizando as tuas skins",
|
||||||
"wp_info_music": "Digite {lime}!music{default} para abrir o menu de música",
|
"wp_knife_menu_select": "Tu escolheste {lime}{0}{default} como a tua faca",
|
||||||
"wp_command_cooldown": "{lightred}Não pode atualizar as skins de armas de momento",
|
"wp_knife_menu_kill": "Para aplicar corretamente a skins para a tua faca, digita {lime}!kill{default}",
|
||||||
"wp_command_refresh_done": "{lime}Atualizando as skins de armas",
|
"wp_knife_menu_title": "Menu Facas",
|
||||||
"wp_knife_menu_select": "Escolheu {lime}{0}{default} como a sua faca",
|
|
||||||
"wp_knife_menu_kill": "",
|
|
||||||
"wp_knife_menu_title": "Menu de Facas",
|
|
||||||
"wp_glove_menu_select": "Escolheu {lime}{0}{default} como a sua luva",
|
|
||||||
"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": "Escolhe a skin para {lime}{0}{default}",
|
||||||
"wp_skin_menu_select": "Escolheu {lime}{0}{default} como a sua skin",
|
"wp_skin_menu_select": "Tu escolheste {lime}{0}{default} como a tua skin"
|
||||||
|
}
|
||||||
"None": "Nenhum"
|
|
||||||
}
|
|
||||||
|
|||||||
31
lang/ru.json
31
lang/ru.json
@@ -1,25 +1,14 @@
|
|||||||
{
|
{
|
||||||
"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_command_cooldown": "{lightred}Вы не можете выбрать оружие прямо сейчас",
|
||||||
"wp_info_agent": "Введите {lime}!agents{default}, чтобы открыть меню агентов",
|
"wp_command_refresh_done": "{lime}Обновление скинов для оружия",
|
||||||
"wp_info_music": "Введите {lime}!music{default}, чтобы открыть меню музыки",
|
"wp_knife_menu_select": "Вы выбрали {lime}{0}{default} скин для ножа",
|
||||||
"wp_command_cooldown": "{lightred}Вы не можете обновить раскраску оружия сейчас",
|
"wp_knife_menu_kill": "Чтобы правильно применить скин для ножа, набери в чат {lime}!kill{default}",
|
||||||
"wp_command_refresh_done": "{lime}Обновление раскраски оружия",
|
"wp_knife_menu_title": "Меню ножей",
|
||||||
"wp_knife_menu_select": "Вы выбрали {lime}{0}{default} в качестве вашего ножа",
|
"wp_skin_menu_weapon_title": "Меню оружия",
|
||||||
"wp_knife_menu_kill": "",
|
|
||||||
"wp_knife_menu_title": "Меню Ножей",
|
|
||||||
"wp_glove_menu_select": "Вы выбрали {lime}{0}{default} в качестве ваших перчаток",
|
|
||||||
"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_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": "Нет"
|
|
||||||
}
|
}
|
||||||
29
lang/tr.json
29
lang/tr.json
@@ -1,25 +1,14 @@
|
|||||||
{
|
{
|
||||||
"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": "Görünümleri değiştirebileceğiniz {lime}{0}{default} adresini ziyaret edin",
|
||||||
"wp_info_refresh": "Senkronize etmek için {lime}!wp{default} yazın seçilen deriler",
|
"wp_info_refresh": "Seçilen kaplamyı senkronize etmek 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_command_cooldown": "{lightred}Şu anda silah kaplamasını yenileyemezsiniz",
|
||||||
"wp_info_agent": "Ajan menüsünü açmak için {lime}!agents{default} yazın",
|
"wp_command_refresh_done": "{lime}Silah kaplaması yenileniyor",
|
||||||
"wp_info_music": "Müzik menüsünü açmak için {lime}!music{default} yazın",
|
"wp_knife_menu_select": "Bıçağınız olarak {lime}{0}{default} seçtiniz",
|
||||||
"wp_command_cooldown": "{lightred}Şu anda silah boyalarını yenileyemezsiniz",
|
"wp_knife_menu_kill": "Bıçak için doğru şekilde kaplama uygulamak için {lime}!kill{default} yazmanız gerekir",
|
||||||
"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_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_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": "Select skin for {lime}{0}{default}",
|
||||||
"wp_skin_menu_select": "{lime}{0}{default} olarak cildinizi seçtiniz",
|
"wp_skin_menu_select": "Teniniz olarak {lime}{0}{default} seçtiniz"
|
||||||
|
}
|
||||||
"None": "Hiçbiri"
|
|
||||||
}
|
|
||||||
|
|||||||
33
lang/ua.json
33
lang/ua.json
@@ -1,25 +1,14 @@
|
|||||||
{
|
{
|
||||||
"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_command_cooldown": "{lightred}Ви не можете вибрати зброю зараз",
|
||||||
"wp_info_agent": "Введіть {lime}!agents{default}, щоб відкрити меню агентів",
|
"wp_command_refresh_done": "{lime}Оновлення скинів для зброї",
|
||||||
"wp_info_music": "Введіть {lime}!music{default}, щоб відкрити меню музики",
|
"wp_knife_menu_select": "Ви вибрали скин {lime}{0}{default} для ножа",
|
||||||
"wp_command_cooldown": "{lightred}Ви не можете оновити фарби зброї зараз",
|
"wp_knife_menu_kill": "Щоб правильно застосувати скин для ножа, напишіть у чат {lime}!kill{default}",
|
||||||
"wp_command_refresh_done": "{lime}Оновлення фарби зброї",
|
"wp_knife_menu_title": "Меню ножів",
|
||||||
"wp_knife_menu_select": "Ви обрали {lime}{0}{default} як свій ніж",
|
"wp_skin_menu_weapon_title": "Меню зброї",
|
||||||
"wp_knife_menu_kill": "",
|
"wp_skin_menu_skin_title": "Виберіть скин для {lime}{0}{default}",
|
||||||
"wp_knife_menu_title": "Меню Ножів",
|
"wp_skin_menu_select": "Ви вибрали скин {lime}{0}{default} для зброї"
|
||||||
"wp_glove_menu_select": "Ви обрали {lime}{0}{default} як свої рукавички",
|
|
||||||
"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_skin_title": "Виберіть шкіру для {lime}{0}{default}",
|
|
||||||
"wp_skin_menu_select": "Ви обрали {lime}{0}{default} як свою шкіру",
|
|
||||||
|
|
||||||
"None": "Немає"
|
|
||||||
}
|
}
|
||||||
@@ -1,25 +1,14 @@
|
|||||||
{
|
{
|
||||||
"wp_prefix": "{lightblue}[WeaponPaints] {default}",
|
"wp_prefix": "{lightblue}[武器皮肤] {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_command_cooldown": "{lightred}皮肤同步刷新冷却中",
|
||||||
"wp_info_agent": "输入 {lime}!agents{default} 打开代理菜单",
|
"wp_command_refresh_done": "{lime}刷新武器皮肤中",
|
||||||
"wp_info_music": "输入 {lime}!music{default} 打开音乐菜单",
|
"wp_knife_menu_select": "你选择了 {lime}{0}{default} 作为你的刀",
|
||||||
"wp_command_cooldown": "{lightred}您现在无法刷新武器涂装",
|
"wp_knife_menu_kill": "如需完全应用皮肤到刀上, 你需要输入 {lime}!kill{default} 自杀来进行刷新",
|
||||||
"wp_command_refresh_done": "{lime}正在刷新武器涂装",
|
"wp_knife_menu_title": "刀菜单",
|
||||||
"wp_knife_menu_select": "您选择了 {lime}{0}{default} 作为您的刀具",
|
|
||||||
"wp_knife_menu_kill": "",
|
|
||||||
"wp_knife_menu_title": "刀具菜单",
|
|
||||||
"wp_glove_menu_select": "您选择了 {lime}{0}{default} 作为您的手套",
|
|
||||||
"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": "无"
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,29 +1,66 @@
|
|||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* Class DataBase
|
||||||
|
*
|
||||||
|
* This class handles database operations using PDO.
|
||||||
|
*/
|
||||||
class DataBase {
|
class DataBase {
|
||||||
|
/**
|
||||||
|
* @var PDO The PDO instance for database connection.
|
||||||
|
*/
|
||||||
private $PDO;
|
private $PDO;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor method to initialize the database connection.
|
||||||
|
*/
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
try {
|
try {
|
||||||
$this->PDO = new PDO("mysql:host=".DB_HOST."; port=".DB_PORT."; dbname=".DB_NAME, DB_USER, DB_PASS, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
|
// Establish a connection to the database using PDO
|
||||||
|
$this->PDO = new PDO(
|
||||||
|
"mysql:host=".DB_HOST.";port=".DB_PORT.";dbname=".DB_NAME,
|
||||||
|
DB_USER,
|
||||||
|
DB_PASS
|
||||||
|
);
|
||||||
|
// Set the connection to use utf8 encoding
|
||||||
|
$this->PDO->exec("SET NAMES utf8");
|
||||||
}
|
}
|
||||||
catch(PDOException $ex)
|
catch(PDOException $ex) {
|
||||||
{
|
// Display error message if connection fails
|
||||||
echo "<div style='display: flex; flex-direction: column;align-items: center;justify-content: center;text-align: center;'><h2>Problem with database!</h2>";
|
echo "<div style='display: flex; flex-direction: column;align-items: center;justify-content: center;text-align: center;'><h2>Problem with database!</h2>";
|
||||||
die("<pre style='padding: 10px;text-wrap: balance; border: 2px solid #ed6bd3;background: #252525; color: #ed6bd3; width: 50%;'>" . $ex . "</pre>");
|
die("<pre style='padding: 10px;text-wrap: balance; border: 2px solid #ed6bd3;background: #252525; color: #ed6bd3; width: 50%;'>" . $ex . "</pre>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public function select($query, $bindings = []) {
|
|
||||||
|
/**
|
||||||
|
* Perform a SELECT query on the database.
|
||||||
|
*
|
||||||
|
* @param string $query The SQL query to execute.
|
||||||
|
* @param array $bindings An associative array of parameters and their values.
|
||||||
|
* @return array|false Returns an array of rows as associative arrays or false if no results are found.
|
||||||
|
*/
|
||||||
|
public function select($query, $bindings = array()) {
|
||||||
|
// Prepare and execute the SQL query
|
||||||
$STH = $this->PDO->prepare($query);
|
$STH = $this->PDO->prepare($query);
|
||||||
$STH->execute($bindings);
|
$STH->execute($bindings);
|
||||||
|
|
||||||
|
// Fetch the results as associative arrays
|
||||||
$result = $STH->fetchAll(PDO::FETCH_ASSOC);
|
$result = $STH->fetchAll(PDO::FETCH_ASSOC);
|
||||||
$result ??= false;
|
if ($result === false) {
|
||||||
return $result;
|
$result = array(); // Set $result to an empty array if no results found
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function query($query, $bindings = []){
|
/**
|
||||||
|
* Perform a non-query SQL statement on the database.
|
||||||
|
*
|
||||||
|
* @param string $query The SQL query to execute.
|
||||||
|
* @param array $bindings An associative array of parameters and their values.
|
||||||
|
* @return bool Returns true on success or false on failure.
|
||||||
|
*/
|
||||||
|
public function query($query, $bindings = array()) {
|
||||||
|
// Prepare and execute the SQL query
|
||||||
$STH = $this->PDO->prepare($query);
|
$STH = $this->PDO->prepare($query);
|
||||||
return $STH->execute($bindings);
|
return $STH->execute($bindings);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
71
website/class/header.php
Normal file
71
website/class/header.php
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?php
|
||||||
|
// Set security headers to enhance security
|
||||||
|
header("X-Frame-Options: SAMEORIGIN");
|
||||||
|
header("X-XSS-Protection: 1; mode=block");
|
||||||
|
header("X-Content-Type-Options: nosniff");
|
||||||
|
header("Referrer-Policy: no-referrer-when-downgrade");
|
||||||
|
header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://code.jquery.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; img-src 'self' data: https://cdn.jsdelivr.net https://steamcommunity-a.akamaihd.net https://raw.githubusercontent.com;");
|
||||||
|
|
||||||
|
|
||||||
|
// Include necessary classes and files
|
||||||
|
require 'class/config.php';
|
||||||
|
require 'class/database.php';
|
||||||
|
require 'steamauth/steamauth.php';
|
||||||
|
require 'class/utils.php';
|
||||||
|
|
||||||
|
// Create a database instance
|
||||||
|
$db = new DataBase();
|
||||||
|
|
||||||
|
// Check if the user is logged in
|
||||||
|
if (isset($_SESSION['steamid'])) {
|
||||||
|
// Insert or update user's Steam ID in the database
|
||||||
|
$steamid = $_SESSION['steamid'];
|
||||||
|
$db->query("INSERT INTO `wp_users` (`steamid`) VALUES ('{$steamid}') ON DUPLICATE KEY UPDATE `updated_at` = CURRENT_TIMESTAMP");
|
||||||
|
|
||||||
|
// Get user's database index
|
||||||
|
$userInfoQuery = $db->select("SELECT `id` FROM `wp_users` WHERE `steamid` = :steamid", ["steamid" => $steamid]);
|
||||||
|
$_SESSION['userDbIndex'] = $userDbIndex = (int)$userInfoQuery[0]['id'];
|
||||||
|
|
||||||
|
// Get weapons and skins information
|
||||||
|
$weapons = UtilsClass::getWeaponsFromArray();
|
||||||
|
$skins = UtilsClass::skinsFromJson();
|
||||||
|
|
||||||
|
// Retrieve user's selected skins and knife
|
||||||
|
$querySelected = $db->select("SELECT `weapon`, `paint`, `wear`, `seed`, `nametag` FROM `wp_users_items` WHERE `user_id` = :user_id", ["user_id" => $userDbIndex]);
|
||||||
|
$selectedSkins = UtilsClass::getSelectedSkins($querySelected);
|
||||||
|
$selectedKnifeResult = $db->select("SELECT `knife` FROM `wp_users_knife` WHERE `user_id` = :user_id", ["user_id" => $userDbIndex]);
|
||||||
|
|
||||||
|
// Determine user's selected knife or set default knife
|
||||||
|
if (!empty($selectedKnifeResult)) {
|
||||||
|
$selectedKnife = $selectedKnifeResult[0]['knife'];
|
||||||
|
} else {
|
||||||
|
$selectedKnife = "weapon_knife";
|
||||||
|
}
|
||||||
|
$knifes = UtilsClass::getKnifeTypes();
|
||||||
|
|
||||||
|
// Handle form submission
|
||||||
|
if (isset($_POST['forma'])) {
|
||||||
|
$ex = explode("-", $_POST['forma']);
|
||||||
|
|
||||||
|
// Handle knife selection
|
||||||
|
if ($ex[0] == "knife") {
|
||||||
|
$db->query("INSERT INTO `wp_users_knife` (`user_id`, `knife`) VALUES(:user_id, :knife) ON DUPLICATE KEY UPDATE `knife` = :knife", ["user_id" => $userDbIndex, "knife" => $knifes[$ex[1]]['weapon_name']]);
|
||||||
|
} else {
|
||||||
|
// Handle skin selection
|
||||||
|
if (array_key_exists($ex[1], $skins[$ex[0]]) && isset($_POST['wear']) && $_POST['wear'] >= 0.00 && $_POST['wear'] <= 1.00 && isset($_POST['seed'])) {
|
||||||
|
$wear = floatval($_POST['wear']); // wear
|
||||||
|
$seed = intval($_POST['seed']); // seed
|
||||||
|
|
||||||
|
// Check if the skin is already selected and update or insert accordingly
|
||||||
|
if (array_key_exists($ex[0], $selectedSkins)) {
|
||||||
|
$db->query("UPDATE wp_users_items SET paint = :weapon_paint_id, wear = :weapon_wear, seed = :weapon_seed WHERE user_id = :user_id AND weapon = :weapon_defindex", ["user_id" => $userDbIndex, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1], "weapon_wear" => $wear, "weapon_seed" => $seed]);
|
||||||
|
} else {
|
||||||
|
$db->query("INSERT INTO wp_users_items (`user_id`, `weapon`, `paint`, `wear`, `seed`) VALUES (:user_id, :weapon_defindex, :weapon_paint_id, :weapon_wear, :weapon_seed)", ["user_id" => $userDbIndex, "weapon_defindex" => $ex[0], "weapon_paint_id" => $ex[1], "weapon_wear" => $wear, "weapon_seed" => $seed]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Redirect to the same page after form submission
|
||||||
|
header("Location: {$_SERVER['PHP_SELF']}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
@@ -1,99 +1,112 @@
|
|||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* Class UtilsClass
|
||||||
|
*
|
||||||
|
* Provides utility methods for handling skin and weapon data.
|
||||||
|
*/
|
||||||
class UtilsClass
|
class UtilsClass
|
||||||
{
|
{
|
||||||
public static function skinsFromJson(): array
|
/**
|
||||||
|
* Retrieve skins data from the JSON file.
|
||||||
|
*
|
||||||
|
* @return array An associative array containing skin data.
|
||||||
|
*/
|
||||||
|
public static function skinsFromJson()
|
||||||
{
|
{
|
||||||
$skins = [];
|
$skins = array();
|
||||||
$json = json_decode(file_get_contents(__DIR__ . "/../data/skins.json"), true);
|
$jsonFilePath = __DIR__ . "/../data/skins.json";
|
||||||
|
|
||||||
foreach ($json as $skin) {
|
if (file_exists($jsonFilePath) && is_readable($jsonFilePath)) {
|
||||||
$skins[(int) $skin['weapon_defindex']][(int) $skin['paint']] = [
|
$json = json_decode(file_get_contents($jsonFilePath), true);
|
||||||
'weapon_name' => $skin['weapon_name'],
|
|
||||||
'paint_name' => $skin['paint_name'],
|
foreach ($json as $skin) {
|
||||||
'image_url' => $skin['image'],
|
$skins[(int) $skin['weapon_defindex']][(int) $skin['paint']] = array(
|
||||||
];
|
'weapon_name' => $skin['weapon_name'],
|
||||||
|
'paint_name' => $skin['paint_name'],
|
||||||
|
'image_url' => $skin['image'],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Handle file not found or unreadable error
|
||||||
|
// You can throw an exception or log an error message
|
||||||
}
|
}
|
||||||
|
|
||||||
return $skins;
|
return $skins;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve weapons data from the skin data array.
|
||||||
|
*
|
||||||
|
* @return array An associative array containing weapon data.
|
||||||
|
*/
|
||||||
public static function getWeaponsFromArray()
|
public static function getWeaponsFromArray()
|
||||||
{
|
{
|
||||||
$weapons = [];
|
$weapons = array();
|
||||||
$temp = self::skinsFromJson();
|
$skinsData = self::skinsFromJson();
|
||||||
|
|
||||||
foreach ($temp as $key => $value) {
|
foreach ($skinsData as $key => $value) {
|
||||||
if (key_exists($key, $weapons))
|
$weapons[$key] = array(
|
||||||
continue;
|
|
||||||
|
|
||||||
$weapons[$key] = [
|
|
||||||
'weapon_name' => $value[0]['weapon_name'],
|
'weapon_name' => $value[0]['weapon_name'],
|
||||||
'paint_name' => $value[0]['paint_name'],
|
'paint_name' => $value[0]['paint_name'],
|
||||||
'image_url' => $value[0]['image_url'],
|
'image_url' => $value[0]['image_url'],
|
||||||
];
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $weapons;
|
return $weapons;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve knife types from the weapon data array.
|
||||||
|
*
|
||||||
|
* @return array An associative array containing knife types data.
|
||||||
|
*/
|
||||||
public static function getKnifeTypes()
|
public static function getKnifeTypes()
|
||||||
{
|
{
|
||||||
$knifes = [];
|
$knifes = array();
|
||||||
$temp = self::getWeaponsFromArray();
|
$weaponsData = self::getWeaponsFromArray();
|
||||||
|
|
||||||
foreach ($temp as $key => $weapon) {
|
$allowedKnifeKeys = array(
|
||||||
if (
|
500, 503, 505, 506, 507, 508, 509, 512, 514, 515,
|
||||||
!in_array($key, [
|
516, 517, 518, 519, 520, 521, 522, 523, 525
|
||||||
500,
|
);
|
||||||
503,
|
|
||||||
505,
|
|
||||||
506,
|
|
||||||
507,
|
|
||||||
508,
|
|
||||||
509,
|
|
||||||
512,
|
|
||||||
514,
|
|
||||||
515,
|
|
||||||
516,
|
|
||||||
517,
|
|
||||||
518,
|
|
||||||
519,
|
|
||||||
520,
|
|
||||||
521,
|
|
||||||
522,
|
|
||||||
523,
|
|
||||||
525,
|
|
||||||
526
|
|
||||||
])
|
|
||||||
)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$knifes[$key] = [
|
foreach ($weaponsData as $key => $weapon) {
|
||||||
'weapon_name' => $weapon['weapon_name'],
|
if (in_array($key, $allowedKnifeKeys)) {
|
||||||
'paint_name' => rtrim(explode("|", $weapon['paint_name'])[0]),
|
$knifes[$key] = array(
|
||||||
'image_url' => $weapon['image_url'],
|
'weapon_name' => $weapon['weapon_name'],
|
||||||
];
|
'paint_name' => rtrim(explode("|", $weapon['paint_name'])[0]),
|
||||||
$knifes[0] = [
|
'image_url' => $weapon['image_url'],
|
||||||
'weapon_name' => "weapon_knife",
|
);
|
||||||
'paint_name' => "Default knife",
|
}
|
||||||
'image_url' => "https://raw.githubusercontent.com/Nereziel/cs2-WeaponPaints/main/website/img/skins/weapon_knife.png",
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add default knife
|
||||||
|
$knifes[0] = array(
|
||||||
|
'weapon_name' => "weapon_knife",
|
||||||
|
'paint_name' => "Default knife",
|
||||||
|
'image_url' => "https://raw.githubusercontent.com/Nereziel/cs2-WeaponPaints/main/website/img/skins/weapon_knife.png",
|
||||||
|
);
|
||||||
|
|
||||||
ksort($knifes);
|
ksort($knifes);
|
||||||
return $knifes;
|
return $knifes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getSelectedSkins(array $temp)
|
/**
|
||||||
|
* Retrieve selected skins data from the database result.
|
||||||
|
*
|
||||||
|
* @param array $temp An array containing the selected skins data.
|
||||||
|
* @return array An associative array containing selected skins data.
|
||||||
|
*/
|
||||||
|
public static function getSelectedSkins($temp)
|
||||||
{
|
{
|
||||||
$selected = [];
|
$selected = array();
|
||||||
|
|
||||||
foreach ($temp as $weapon) {
|
foreach ($temp as $weapon) {
|
||||||
$selected[$weapon['weapon_defindex']] = [
|
$selected[$weapon['weapon']] = array(
|
||||||
'weapon_paint_id' => $weapon['weapon_paint_id'],
|
'weapon_paint_id' => $weapon['paint'],
|
||||||
'weapon_seed' => $weapon['weapon_seed'],
|
'weapon_seed' => $weapon['seed'],
|
||||||
'weapon_wear' => $weapon['weapon_wear'],
|
'weapon_wear' => $weapon['wear'],
|
||||||
];
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $selected;
|
return $selected;
|
||||||
|
|||||||
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
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
[{"weapon_defindex":0,"paint":0,"image":"","paint_name":"Gloves | Default"}]
|
|
||||||
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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user