mirror of
https://github.com/Nereziel/cs2-WeaponPaints.git
synced 2026-02-20 19:23:07 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2abe48f71a | ||
|
|
219c201fde | ||
|
|
acf4a766ca |
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']
|
||||
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@@ -58,8 +58,10 @@ jobs:
|
||||
${{ env.OUTPUT_PATH }}/McMaster.NETCore.Plugins.dll \
|
||||
${{ env.OUTPUT_PATH }}/Microsoft.DotNet.PlatformAbstractions.dll \
|
||||
${{ env.OUTPUT_PATH }}/Microsoft.Extensions.DependencyModel.dll \
|
||||
- name: Copy skins
|
||||
run: cp -R website/data ${{ env.OUTPUT_PATH }}
|
||||
- name: Copy skins.json
|
||||
run: cp website/data/skins.json ${{ env.OUTPUT_PATH }}/skins.json
|
||||
- name: Copy gloves.json
|
||||
run: cp website/data/gloves.json ${{ env.OUTPUT_PATH }}/gloves.json
|
||||
- name: Zip
|
||||
run: zip -r "${{ env.PROJECT_NAME }}.zip" "${{ env.OUTPUT_PATH }}" gamedata/
|
||||
- name: Clean files Website
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,4 +2,3 @@
|
||||
bin/
|
||||
obj/
|
||||
website/getskins.php
|
||||
.idea/
|
||||
BIN
3rd_party/MenuManagerApi.dll
vendored
BIN
3rd_party/MenuManagerApi.dll
vendored
Binary file not shown.
937
Commands.cs
937
Commands.cs
File diff suppressed because it is too large
Load Diff
45
Config.cs
45
Config.cs
@@ -5,24 +5,18 @@ namespace WeaponPaints
|
||||
{
|
||||
public class Additional
|
||||
{
|
||||
[JsonPropertyName("SkinVisibilityFix")]
|
||||
public bool SkinVisibilityFix { get; set; } = true;
|
||||
|
||||
[JsonPropertyName("KnifeEnabled")]
|
||||
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")]
|
||||
public bool SkinEnabled { get; set; } = true;
|
||||
|
||||
[JsonPropertyName("PinsEnabled")]
|
||||
public bool PinsEnabled { get; set; } = true;
|
||||
|
||||
[JsonPropertyName("CommandWpEnabled")]
|
||||
public bool CommandWpEnabled { get; set; } = true;
|
||||
|
||||
@@ -30,28 +24,22 @@ namespace WeaponPaints
|
||||
public bool CommandKillEnabled { get; set; } = true;
|
||||
|
||||
[JsonPropertyName("CommandKnife")]
|
||||
public List<string> CommandKnife { get; set; } = ["knife"];
|
||||
|
||||
[JsonPropertyName("CommandMusic")]
|
||||
public List<string> CommandMusic { get; set; } = ["music"];
|
||||
public string CommandKnife { get; set; } = "knife";
|
||||
|
||||
[JsonPropertyName("CommandGlove")]
|
||||
public List<string> CommandGlove { get; set; } = ["gloves"];
|
||||
|
||||
[JsonPropertyName("CommandAgent")]
|
||||
public List<string> CommandAgent { get; set; } = ["agents"];
|
||||
public string CommandGlove { get; set; } = "gloves";
|
||||
|
||||
[JsonPropertyName("CommandSkin")]
|
||||
public List<string> CommandSkin { get; set; } = ["ws"];
|
||||
public string CommandSkin { get; set; } = "ws";
|
||||
|
||||
[JsonPropertyName("CommandSkinSelection")]
|
||||
public List<string> CommandSkinSelection { get; set; } = ["skins"];
|
||||
public string CommandSkinSelection { get; set; } = "skins";
|
||||
|
||||
[JsonPropertyName("CommandRefresh")]
|
||||
public List<string> CommandRefresh { get; set; } = ["wp"];
|
||||
public string CommandRefresh { get; set; } = "wp";
|
||||
|
||||
[JsonPropertyName("CommandKill")]
|
||||
public List<string> CommandKill { get; set; } = ["kill"];
|
||||
public string CommandKill { get; set; } = "kill";
|
||||
|
||||
[JsonPropertyName("GiveRandomKnife")]
|
||||
public bool GiveRandomKnife { get; set; } = false;
|
||||
@@ -59,16 +47,16 @@ namespace WeaponPaints
|
||||
[JsonPropertyName("GiveRandomSkin")]
|
||||
public bool GiveRandomSkin { get; set; } = false;
|
||||
|
||||
[JsonPropertyName("GiveKnifeAfterRemove")]
|
||||
public bool GiveKnifeAfterRemove { get; set; } = false;
|
||||
|
||||
[JsonPropertyName("ShowSkinImage")]
|
||||
public bool ShowSkinImage { get; set; } = true;
|
||||
}
|
||||
|
||||
public class WeaponPaintsConfig : BasePluginConfig
|
||||
{
|
||||
[JsonPropertyName("ConfigVersion")] public override int Version { get; set; } = 8;
|
||||
|
||||
[JsonPropertyName("SkinsLanguage")]
|
||||
public string SkinsLanguage { get; set; } = "en";
|
||||
public override int Version { get; set; } = 4;
|
||||
|
||||
[JsonPropertyName("DatabaseHost")]
|
||||
public string DatabaseHost { get; set; } = "";
|
||||
@@ -86,12 +74,15 @@ namespace WeaponPaints
|
||||
public string DatabaseName { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("CmdRefreshCooldownSeconds")]
|
||||
public int CmdRefreshCooldownSeconds { get; set; } = 10;
|
||||
public int CmdRefreshCooldownSeconds { get; set; } = 60;
|
||||
|
||||
[JsonPropertyName("Prefix")]
|
||||
public string Prefix { get; set; } = "[WeaponPaints]";
|
||||
|
||||
[JsonPropertyName("Website")]
|
||||
public string Website { get; set; } = "example.com/skins";
|
||||
|
||||
[JsonPropertyName("Additional")]
|
||||
public Additional Additional { get; set; } = new();
|
||||
public Additional Additional { get; set; } = new Additional();
|
||||
}
|
||||
}
|
||||
28
Database.cs
28
Database.cs
@@ -1,23 +1,21 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MySqlConnector;
|
||||
using MySqlConnector;
|
||||
|
||||
namespace WeaponPaints
|
||||
{
|
||||
public class Database(string dbConnectionString)
|
||||
public class Database
|
||||
{
|
||||
public async Task<MySqlConnection> GetConnectionAsync()
|
||||
private readonly string _dbConnectionString;
|
||||
|
||||
public Database(string dbConnectionString)
|
||||
{
|
||||
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;
|
||||
}
|
||||
_dbConnectionString = dbConnectionString;
|
||||
}
|
||||
|
||||
public async Task<MySqlConnection> GetConnectionAsync()
|
||||
{
|
||||
var connection = new MySqlConnection(_dbConnectionString);
|
||||
await connection.OpenAsync();
|
||||
return connection;
|
||||
}
|
||||
}
|
||||
}
|
||||
419
Events.cs
419
Events.cs
@@ -1,9 +1,6 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes.Registration;
|
||||
using CounterStrikeSharp.API.Modules.Entities;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
|
||||
namespace WeaponPaints
|
||||
{
|
||||
@@ -14,47 +11,36 @@ namespace WeaponPaints
|
||||
{
|
||||
CCSPlayerController? player = @event.Userid;
|
||||
|
||||
if (player is null || !player.IsValid || player.IsBot ||
|
||||
WeaponSync == null || Database == null) return HookResult.Continue;
|
||||
if (player is null || !player.IsValid || player.IsBot || player.IsHLTV || player.SteamID.ToString().Length != 17 ||
|
||||
weaponSync == null || _database == null) return HookResult.Continue;
|
||||
|
||||
var 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]
|
||||
};
|
||||
PlayerInfo playerInfo = new(
|
||||
(int)player.Index,
|
||||
player.Slot,
|
||||
player.UserId,
|
||||
player.SteamID.ToString(),
|
||||
player.PlayerName,
|
||||
player.IpAddress?.Split(":")[0]
|
||||
);
|
||||
|
||||
try
|
||||
try
|
||||
{
|
||||
_ = Task.Run(async () => await WeaponSync.GetPlayerData(playerInfo));
|
||||
/*
|
||||
if (Config.Additional.SkinEnabled)
|
||||
{
|
||||
_ = Task.Run(async () => await weaponSync.GetWeaponPaintsFromDatabase(playerInfo));
|
||||
Task.Run(() => weaponSync.GetWeaponPaintsFromDatabase(playerInfo));
|
||||
}
|
||||
if (Config.Additional.KnifeEnabled)
|
||||
{
|
||||
_ = Task.Run(async () => await weaponSync.GetKnifeFromDatabase(playerInfo));
|
||||
Task.Run(() => weaponSync.GetKnifeFromDatabase(playerInfo));
|
||||
}
|
||||
if (Config.Additional.GloveEnabled)
|
||||
{
|
||||
_ = Task.Run(async () => await weaponSync.GetGloveFromDatabase(playerInfo));
|
||||
Task.Run(() => weaponSync.GetGloveFromDatabase(playerInfo));
|
||||
}
|
||||
if (Config.Additional.AgentEnabled)
|
||||
{
|
||||
_ = Task.Run(async () => await weaponSync.GetAgentFromDatabase(playerInfo));
|
||||
}
|
||||
if (Config.Additional.MusicEnabled)
|
||||
{
|
||||
_ = Task.Run(async () => await weaponSync.GetMusicFromDatabase(playerInfo));
|
||||
}
|
||||
*/
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"An error occurred: {ex.Message}");
|
||||
}
|
||||
|
||||
return HookResult.Continue;
|
||||
@@ -63,238 +49,261 @@ namespace WeaponPaints
|
||||
[GameEventHandler]
|
||||
public HookResult OnPlayerDisconnect(EventPlayerDisconnect @event, GameEventInfo info)
|
||||
{
|
||||
CCSPlayerController? player = @event.Userid;
|
||||
CCSPlayerController player = @event.Userid;
|
||||
|
||||
if (player is null || !player.IsValid || player.IsBot) return HookResult.Continue;
|
||||
if (player is null || !player.IsValid || player.IsBot ||
|
||||
player.IsHLTV || player.SteamID.ToString().Length != 17) return HookResult.Continue;
|
||||
|
||||
var 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]
|
||||
};
|
||||
PlayerInfo playerInfo = new(
|
||||
(int)player.Index,
|
||||
player.Slot,
|
||||
player.UserId,
|
||||
player.SteamID.ToString(),
|
||||
player.PlayerName,
|
||||
player.IpAddress?.Split(":")[0]
|
||||
);
|
||||
|
||||
if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out var weaponInfos))
|
||||
return HookResult.Continue;
|
||||
|
||||
if (WeaponSync != null)
|
||||
_ = Task.Run(async () => await WeaponSync.SyncStatTrakToDatabase(playerInfo, weaponInfos));
|
||||
if (weaponSync != null)
|
||||
{
|
||||
// Run weapon sync tasks asynchronously
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await weaponSync.SyncWeaponPaintsToDatabase(playerInfo);
|
||||
});
|
||||
|
||||
if (Config.Additional.SkinEnabled)
|
||||
{
|
||||
GPlayerWeaponsInfo.TryRemove(player.Slot, out _);
|
||||
}
|
||||
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 _);
|
||||
}
|
||||
if (Config.Additional.PinsEnabled)
|
||||
{
|
||||
GPlayersPin.TryRemove(player.Slot, out _);
|
||||
// Remove player data
|
||||
if (Config.Additional.SkinEnabled)
|
||||
{
|
||||
gPlayerWeaponsInfo.TryRemove(player.Slot, out _);
|
||||
}
|
||||
if (Config.Additional.KnifeEnabled)
|
||||
{
|
||||
g_playersKnife.TryRemove(player.Slot, out _);
|
||||
}
|
||||
if (Config.Additional.GloveEnabled)
|
||||
{
|
||||
g_playersGlove.TryRemove(player.Slot, out _);
|
||||
}
|
||||
}
|
||||
|
||||
_temporaryPlayerWeaponWear.TryRemove(player.Slot, out _);
|
||||
CommandsCooldown.Remove(player.Slot);
|
||||
// Remove player's command cooldown
|
||||
commandsCooldown.Remove(player.Slot);
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
|
||||
private void OnEntityCreated(CEntityInstance entity)
|
||||
{
|
||||
if (!Config.Additional.SkinEnabled) return;
|
||||
if (entity == null || !entity.IsValid || string.IsNullOrEmpty(entity.DesignerName)) return;
|
||||
string designerName = entity.DesignerName;
|
||||
if (!weaponList.ContainsKey(designerName)) return;
|
||||
bool isKnife = false;
|
||||
var weapon = new CBasePlayerWeapon(entity.Handle);
|
||||
|
||||
if (designerName.Contains("knife") || designerName.Contains("bayonet"))
|
||||
{
|
||||
isKnife = true;
|
||||
}
|
||||
|
||||
Server.NextFrame(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!weapon.IsValid) return;
|
||||
if (weapon.OwnerEntity.Value == null) return;
|
||||
if (weapon.OwnerEntity.Index <= 0) return;
|
||||
int weaponOwner = (int)weapon.OwnerEntity.Index;
|
||||
CBasePlayerPawn? pawn = Utilities.GetEntityFromIndex<CCSPlayerPawn>(weaponOwner);
|
||||
if (!pawn.IsValid) return;
|
||||
|
||||
var playerIndex = (int)pawn.Controller.Index;
|
||||
var player = Utilities.GetPlayerFromIndex(playerIndex);
|
||||
if (!Utility.IsPlayerValid(player)) return;
|
||||
|
||||
ChangeWeaponAttributes(weapon, player, isKnife);
|
||||
}
|
||||
catch (Exception) { }
|
||||
});
|
||||
}
|
||||
|
||||
public HookResult OnPickup(CEntityIOOutput output, string name, CEntityInstance activator, CEntityInstance caller, CVariant value, float delay)
|
||||
{
|
||||
if (!Config.Additional.GiveKnifeAfterRemove)
|
||||
return HookResult.Continue;
|
||||
|
||||
CCSPlayerController? player = Utilities.GetEntityFromIndex<CCSPlayerPawn>((int)activator.Index).OriginalController.Value;
|
||||
|
||||
if (player == null || player.IsBot || player.IsHLTV ||
|
||||
player.SteamID.ToString().Length != 17 || !g_knifePickupCount.TryGetValue(player.Slot, out var pickupCount) ||
|
||||
!g_playersKnife.ContainsKey(player.Slot))
|
||||
{
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
CBasePlayerWeapon weapon = new(caller.Handle);
|
||||
|
||||
if (weapon.AttributeManager.Item.ItemDefinitionIndex != 42 && weapon.AttributeManager.Item.ItemDefinitionIndex != 59)
|
||||
{
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
if (pickupCount >= 2)
|
||||
{
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
if (g_playersKnife[player.Slot] != "weapon_knife")
|
||||
{
|
||||
pickupCount++;
|
||||
g_knifePickupCount[player.Slot] = pickupCount;
|
||||
|
||||
AddTimer(0.2f, () => RefreshWeapons(player));
|
||||
}
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
private void OnMapStart(string mapName)
|
||||
{
|
||||
if (Config.Additional is { KnifeEnabled: false, SkinEnabled: false, GloveEnabled: false }) return;
|
||||
if (!Config.Additional.KnifeEnabled && !Config.Additional.SkinEnabled && !Config.Additional.GloveEnabled) return;
|
||||
|
||||
if (Database != null)
|
||||
WeaponSync = new WeaponSynchronization(Database, Config);
|
||||
if (_database != null)
|
||||
weaponSync = new WeaponSynchronization(_database, Config);
|
||||
|
||||
// TODO
|
||||
// needed for now
|
||||
AddTimer(2.0f, () =>
|
||||
{
|
||||
NativeAPI.IssueServerCommand("mp_t_default_melee \"\"");
|
||||
NativeAPI.IssueServerCommand("mp_ct_default_melee \"\"");
|
||||
NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0");
|
||||
});
|
||||
}
|
||||
|
||||
private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info)
|
||||
{
|
||||
CCSPlayerController? player = @event.Userid;
|
||||
|
||||
if (player is null || !player.IsValid || Config.Additional is { KnifeEnabled: false, GloveEnabled: false })
|
||||
if (player is null || !player.IsValid || !Config.Additional.KnifeEnabled && !Config.Additional.GloveEnabled)
|
||||
return HookResult.Continue;
|
||||
|
||||
CCSPlayerPawn? pawn = player.PlayerPawn.Value;
|
||||
g_knifePickupCount[player.Slot] = 0;
|
||||
|
||||
if (pawn == null || !pawn.IsValid)
|
||||
return HookResult.Continue;
|
||||
if (!PlayerHasKnife(player))
|
||||
GiveKnifeToPlayer(player);
|
||||
|
||||
GivePlayerMusicKit(player);
|
||||
GivePlayerAgent(player);
|
||||
GivePlayerGloves(player);
|
||||
Server.NextFrame(() =>
|
||||
{
|
||||
RefreshGloves(player);
|
||||
});
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
private HookResult OnRoundEnd(EventRoundEnd @event, GameEventInfo info)
|
||||
{
|
||||
_gBCommandsAllowed = false;
|
||||
g_bCommandsAllowed = false;
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
private HookResult OnRoundStart(EventRoundStart @event, GameEventInfo info)
|
||||
{
|
||||
_gBCommandsAllowed = true;
|
||||
return HookResult.Continue;
|
||||
}
|
||||
NativeAPI.IssueServerCommand("mp_t_default_melee \"\"");
|
||||
NativeAPI.IssueServerCommand("mp_ct_default_melee \"\"");
|
||||
NativeAPI.IssueServerCommand("mp_equipment_reset_rounds 0");
|
||||
|
||||
public HookResult OnGiveNamedItemPost(DynamicHook hook)
|
||||
{
|
||||
try
|
||||
{
|
||||
var itemServices = hook.GetParam<CCSPlayer_ItemServices>(0);
|
||||
var weapon = hook.GetReturn<CBasePlayerWeapon>();
|
||||
if (!weapon.DesignerName.Contains("weapon"))
|
||||
return HookResult.Continue;
|
||||
|
||||
var player = GetPlayerFromItemServices(itemServices);
|
||||
if (player != null)
|
||||
GivePlayerWeaponSkin(player, weapon);
|
||||
}
|
||||
catch { }
|
||||
g_bCommandsAllowed = true;
|
||||
|
||||
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()
|
||||
{
|
||||
if (!Config.Additional.ShowSkinImage) return;
|
||||
|
||||
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 }
|
||||
p is not null && p.IsValid &&
|
||||
(LifeState_t)p.LifeState == LifeState_t.LIFE_ALIVE && p.SteamID.ToString().Length == 17
|
||||
&& !p.IsBot && !p.IsHLTV && p.Connected == PlayerConnectedState.PlayerConnected && p.Team != CounterStrikeSharp.API.Modules.Utils.CsTeam.None
|
||||
)
|
||||
)
|
||||
{
|
||||
if (_playerWeaponImage.TryGetValue(player.Slot, out var value) && !string.IsNullOrEmpty(value))
|
||||
try
|
||||
{
|
||||
if (Config.Additional.ShowSkinImage && PlayerWeaponImage.ContainsKey(player.Slot) && !string.IsNullOrEmpty(PlayerWeaponImage[player.Slot]))
|
||||
{
|
||||
player.PrintToCenterHtml("<img src='{PATH}'</img>".Replace("{PATH}", PlayerWeaponImage[player.Slot]));
|
||||
}
|
||||
|
||||
if (player.PlayerPawn?.IsValid != true || player.PlayerPawn?.Value?.IsValid != true)
|
||||
continue;
|
||||
|
||||
var viewModels = GetPlayerViewModels(player);
|
||||
if (viewModels == null || viewModels.Length == 0)
|
||||
continue;
|
||||
|
||||
var viewModel = viewModels[0];
|
||||
if (viewModel == null || viewModel.Value == null || viewModel.Value.Weapon == null || viewModel.Value.Weapon.Value == null)
|
||||
continue;
|
||||
|
||||
var weapon = viewModel.Value.Weapon.Value;
|
||||
if (weapon == null || !weapon.IsValid || weapon.FallbackPaintKit == 0)
|
||||
continue;
|
||||
|
||||
if (viewModel.Value.VMName.Contains("knife"))
|
||||
continue;
|
||||
|
||||
var sceneNode = viewModel.Value.CBodyComponent?.SceneNode;
|
||||
if (sceneNode == null)
|
||||
continue;
|
||||
|
||||
var skeleton = GetSkeletonInstance(sceneNode);
|
||||
if (skeleton == null)
|
||||
continue;
|
||||
|
||||
bool skeletonChange = false;
|
||||
|
||||
int[] newPaints = { 1171, 1170, 1169, 1164, 1162, 1161, 1159, 1175, 1174, 1167, 1165, 1168, 1163, 1160, 1166, 1173 };
|
||||
if (newPaints.Contains(weapon.FallbackPaintKit))
|
||||
{
|
||||
if (skeleton.ModelState.MeshGroupMask != 1)
|
||||
{
|
||||
skeleton.ModelState.MeshGroupMask = 1;
|
||||
skeletonChange = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (skeleton.ModelState.MeshGroupMask != 2)
|
||||
{
|
||||
skeleton.ModelState.MeshGroupMask = 2;
|
||||
skeletonChange = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (skeletonChange)
|
||||
Utilities.SetStateChanged(viewModel.Value, "CBaseEntity", "m_CBodyComponent");
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
player.PrintToCenterHtml("<img src='{PATH}'</img>".Replace("{PATH}", value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[GameEventHandler]
|
||||
public HookResult OnItemPickup(EventItemPickup @event, GameEventInfo _)
|
||||
{
|
||||
if (!IsWindows) return HookResult.Continue;
|
||||
|
||||
var player = @event.Userid;
|
||||
if (player != null && player is { IsValid: true, Connected: PlayerConnectedState.PlayerConnected, PawnIsAlive: true, PlayerPawn.IsValid: true })
|
||||
{
|
||||
GiveOnItemPickup(player);
|
||||
}
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
private HookResult OnPlayerDeath(EventPlayerDeath @event, GameEventInfo info)
|
||||
{
|
||||
CCSPlayerController? player = @event.Attacker;
|
||||
|
||||
if (player is null || !player.IsValid)
|
||||
return HookResult.Continue;
|
||||
|
||||
if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out _)) return HookResult.Continue;
|
||||
|
||||
CBasePlayerWeapon? weapon = player.PlayerPawn.Value?.WeaponServices?.ActiveWeapon.Value;
|
||||
|
||||
if (weapon == null) return HookResult.Continue;
|
||||
|
||||
int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
|
||||
|
||||
if (!GPlayerWeaponsInfo[player.Slot].TryGetValue(weaponDefIndex, out var weaponInfo) || weaponInfo.Paint == 0)
|
||||
return HookResult.Continue;
|
||||
|
||||
if (!weaponInfo.StatTrak) return HookResult.Continue;
|
||||
|
||||
weaponInfo.StatTrakCount += 1;
|
||||
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater", ViewAsFloat((uint)weaponInfo.StatTrakCount));
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater score type", 0);
|
||||
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater", ViewAsFloat((uint)weaponInfo.StatTrakCount));
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater score type", 0);
|
||||
|
||||
return HookResult.Continue;
|
||||
}
|
||||
|
||||
private void RegisterListeners()
|
||||
{
|
||||
RegisterListener<Listeners.OnEntitySpawned>(OnEntityCreated);
|
||||
//RegisterListener<Listeners.OnClientPutInServer>(OnClientPutInServer);
|
||||
//RegisterListener<Listeners.OnClientDisconnect>(OnClientDisconnect);
|
||||
RegisterListener<Listeners.OnMapStart>(OnMapStart);
|
||||
RegisterListener<Listeners.OnTick>(OnTick);
|
||||
|
||||
RegisterEventHandler<EventPlayerSpawn>(OnPlayerSpawn);
|
||||
RegisterEventHandler<EventRoundStart>(OnRoundStart);
|
||||
RegisterEventHandler<EventRoundStart>(OnRoundStart, HookMode.Pre);
|
||||
RegisterEventHandler<EventRoundEnd>(OnRoundEnd);
|
||||
RegisterListener<Listeners.OnEntityCreated>(OnEntityCreated);
|
||||
RegisterEventHandler<EventPlayerDeath>(OnPlayerDeath);
|
||||
|
||||
if (Config.Additional.ShowSkinImage)
|
||||
RegisterListener<Listeners.OnTick>(OnTick);
|
||||
//RegisterEventHandler<EventItemPickup>(OnItemPickup);
|
||||
|
||||
if (!IsWindows)
|
||||
VirtualFunctions.GiveNamedItemFunc.Hook(OnGiveNamedItemPost, HookMode.Post);
|
||||
HookEntityOutput("weapon_knife", "OnPlayerPickup", OnPickup);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,17 @@ namespace WeaponPaints;
|
||||
|
||||
public static class PlayerExtensions
|
||||
{
|
||||
public static void Print(this CCSPlayerController controller, string message)
|
||||
{
|
||||
if (WeaponPaints._localizer == null) return;
|
||||
|
||||
StringBuilder _message = new(WeaponPaints._localizer["wp_prefix"]);
|
||||
_message.Append(message);
|
||||
controller.PrintToChat(_message.ToString());
|
||||
}
|
||||
public static void Print(this CCSPlayerController controller, string message)
|
||||
{
|
||||
if (WeaponPaints._localizer == null)
|
||||
{
|
||||
controller.PrintToChat(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
StringBuilder _message = new(WeaponPaints._localizer["wp_prefix"]);
|
||||
_message.Append(message);
|
||||
controller.PrintToChat(_message.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,24 @@
|
||||
namespace WeaponPaints
|
||||
{
|
||||
public class PlayerInfo
|
||||
{
|
||||
public int Index { get; set; }
|
||||
public int Slot { get; init; }
|
||||
public int? UserId { get; set; }
|
||||
public string? SteamId { get; init; }
|
||||
public string? Name { get; set; }
|
||||
public string? IpAddress { get; set; }
|
||||
}
|
||||
public class PlayerInfo
|
||||
{
|
||||
public int Index { get; set; }
|
||||
public int Slot { get; set; }
|
||||
public int? UserId { get; set; }
|
||||
public string? SteamId { get; set; }
|
||||
public string? Name { get; set; }
|
||||
public string? IpAddress { get; set; }
|
||||
|
||||
public PlayerInfo() { }
|
||||
|
||||
public PlayerInfo(int index, int slot, int? userId, string? steamId, string? name, string? ipAddress)
|
||||
{
|
||||
Index = index;
|
||||
Slot = slot;
|
||||
UserId = userId;
|
||||
SteamId = steamId;
|
||||
Name = name;
|
||||
IpAddress = ipAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
31
README.md
31
README.md
@@ -9,31 +9,21 @@ 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)
|
||||
|
||||
## 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
|
||||
- Data syncs on player connect
|
||||
- Added command **`!wp`** to refresh skins ***(with cooldown in seconds can be configured)***
|
||||
- Added command **`!ws`** to show website
|
||||
- Added command **`!knife`** to show menu with knives
|
||||
- Added command **`!gloves`** to show menu with gloves
|
||||
- Added command **`!agents`** to show menu with agents
|
||||
- Knife change is now limited to have these cvars empty **`mp_t_default_melee ""`** and **`mp_ct_default_melee ""`**
|
||||
- Translations support, submit a PR if you want to share your translation
|
||||
|
||||
## ⚙️ Requirements
|
||||
**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
|
||||
- Have working CounterStrikeSharp (**with RUNTIME!**)
|
||||
- Download from Release and copy plugin to plugins
|
||||
- Run server with plugin, **it will generate config if installed correctly!**
|
||||
- Edit `addons/counterstrikesharp/configs/`**`plugins/WeaponPaints/WeaponPaints.json`** include database credentials
|
||||
- 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
|
||||
<details>
|
||||
@@ -62,6 +52,7 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
|
||||
"SkinMenuTitle": "Select skin for {WEAPON}" // Menu title (!skins menu, after weapon select)
|
||||
},
|
||||
"Additional": {
|
||||
"SkinVisibilityFix": true, // Enable or disable fix for skin visibility
|
||||
"KnifeEnabled": true, // Enable or disable knife feature
|
||||
"SkinEnabled": true, // Enable or disable skin feature
|
||||
"CommandWpEnabled": true, // Enable or disable refreshing command
|
||||
@@ -74,11 +65,13 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
|
||||
"GiveRandomKnife": false, // Give random knife 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>
|
||||
|
||||
## Web install
|
||||
- Requires PHP >= 7.4 with curl and pdo_mysql ***(Tested on php ver **`8.2.3`** and nginx webserver)***
|
||||
- Requires PHP >= 7.4 ***(Tested on php ver **`8.2.3`** and nginx webserver)***
|
||||
- **Before using website, make sure the plugin is correctly loaded in cs2 server!** Mysql tables are created by plugin not by website.
|
||||
- Copy website to web server ***(Folder `img` not needed)***
|
||||
- Get [Steam API Key](https://steamcommunity.com/dev/apikey)
|
||||
@@ -90,6 +83,11 @@ Unfinished, unoptimized and not fully functional ugly demo weapon paints plugin
|
||||
- Steam login/logout
|
||||
- Change knife, paint, seed and wear
|
||||
|
||||
## Known issues
|
||||
- Issue on Windows servers, no knives are given.
|
||||
- You can't change knife if it's equpied in cs2 inventory
|
||||
- Can cause incompatibility with plugins/maps which manipulates weapons and knives
|
||||
|
||||
## Troubleshooting
|
||||
<details>
|
||||
**Skins are not changing:**
|
||||
@@ -98,6 +96,11 @@ Set FollowCSGOGuidelines to false in cssharp’s core.jcon config
|
||||
**Database error table does not exists:**
|
||||
Plugin is not loaded or configured with mysql credentials. Tables are auto-created by plugin.
|
||||
|
||||
**Knives are disappearing:**
|
||||
Set in config GiveKnifeAfterRemove to true
|
||||
|
||||
**Knives are not changing for players:**
|
||||
You can't change knife if you have your own equipped
|
||||
</details>
|
||||
|
||||
### 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/)
|
||||
|
||||
25
SchemaString.cs
Normal file
25
SchemaString.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
namespace WeaponPaints;
|
||||
|
||||
public class SchemaString<TSchemaClass> : NativeObject where TSchemaClass : NativeObject
|
||||
{
|
||||
internal SchemaString(TSchemaClass instance, string member) : base(Schema.GetSchemaValue<nint>(instance.Handle, typeof(TSchemaClass).Name!, member))
|
||||
{ }
|
||||
|
||||
internal unsafe void Set(string str)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(str);
|
||||
var handle = Handle.ToInt64();
|
||||
|
||||
for (var i = 0; i < bytes.Length; i++)
|
||||
{
|
||||
Unsafe.Write((void*)(handle + i), bytes[i]);
|
||||
}
|
||||
|
||||
Unsafe.Write((void*)(handle + bytes.Length), 0);
|
||||
}
|
||||
}
|
||||
253
Utility.cs
253
Utility.cs
@@ -1,9 +1,11 @@
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Translations;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Dapper;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MySqlConnector;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace WeaponPaints
|
||||
{
|
||||
@@ -11,75 +13,63 @@ namespace WeaponPaints
|
||||
{
|
||||
internal static WeaponPaintsConfig? Config { get; set; }
|
||||
|
||||
internal static string BuildDatabaseConnectionString()
|
||||
{
|
||||
if (Config == null) return string.Empty;
|
||||
var builder = new MySqlConnectionStringBuilder
|
||||
{
|
||||
Server = Config.DatabaseHost,
|
||||
UserID = Config.DatabaseUser,
|
||||
Password = Config.DatabasePassword,
|
||||
Database = Config.DatabaseName,
|
||||
Port = (uint)Config.DatabasePort,
|
||||
Pooling = true
|
||||
};
|
||||
|
||||
return builder.ConnectionString;
|
||||
}
|
||||
|
||||
internal static async Task CheckDatabaseTables()
|
||||
{
|
||||
if (WeaponPaints.Database is null) return;
|
||||
if (WeaponPaints._database is null) return;
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await WeaponPaints.Database.GetConnectionAsync();
|
||||
await using var connection = await WeaponPaints._database.GetConnectionAsync();
|
||||
|
||||
await using var transaction = await connection.BeginTransactionAsync();
|
||||
|
||||
try
|
||||
{
|
||||
string[] createTableQueries =
|
||||
[
|
||||
"""
|
||||
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_stattrak` tinyint(1) NOT NULL,
|
||||
`weapon_stattrak_count` int(10) NOT 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
|
||||
""",
|
||||
string[] createTableQueries = new[]
|
||||
{
|
||||
@"CREATE TABLE IF NOT EXISTS `wp_player_skins` (
|
||||
`steamid` varchar(64) 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
|
||||
) 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
|
||||
""",
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS `wp_player_pins` (
|
||||
`steamid` varchar(64) NOT NULL,
|
||||
`id` int(11) NOT NULL,
|
||||
UNIQUE (`steamid`)
|
||||
) ENGINE=InnoDB
|
||||
""",
|
||||
];
|
||||
|
||||
foreach (var query in createTableQueries)
|
||||
`steamid` varchar(64) NOT NULL,
|
||||
`knife` varchar(64) NOT NULL,
|
||||
UNIQUE (`steamid`)
|
||||
) ENGINE = InnoDB",
|
||||
@"CREATE TABLE IF NOT EXISTS `wp_player_gloves` (
|
||||
`steamid` varchar(64) NOT NULL,
|
||||
`weapon_defindex` int(11) NOT NULL,
|
||||
UNIQUE (`steamid`)
|
||||
) ENGINE=InnoDB"
|
||||
};
|
||||
/*string[] createTableQueries = new[]
|
||||
{
|
||||
@"CREATE TABLE IF NOT EXISTS `wp_players` (`steamid` BIGINT UNSIGNED NOT NULL, `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
|
||||
@"CREATE TABLE IF NOT EXISTS `wp_players_skins` (`steamid` BIGINT UNSIGNED NOT NULL, `team` SMALLINT UNSIGNED NOT NULL, `weapon` SMALLINT UNSIGNED NOT NULL, `paint` SMALLINT UNSIGNED NOT NULL, `wear` FLOAT NOT NULL DEFAULT 0.00001, `seed` SMALLINT UNSIGNED NOT NULL DEFAULT 0, `nametag` VARCHAR(20) DEFAULT NULL, `stattrack` INT UNSIGNED NOT NULL DEFAULT 0, `stattrack_enabled` SMALLINT NOT NULL DEFAULT 0, PRIMARY KEY (`steamid`,`weapon`,`team`), FOREIGN KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
|
||||
@"CREATE TABLE IF NOT EXISTS `wp_players_knife` (`steamid` BIGINT UNSIGNED NOT NULL, `knife_t` SMALLINT UNSIGNED NOT NULL, `knife_ct` SMALLINT UNSIGNED NOT NULL, PRIMARY KEY (`steamid`), FOREIGN KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
|
||||
@"CREATE TABLE IF NOT EXISTS `wp_players_music` (`steamid` BIGINT UNSIGNED NOT NULL, `music` SMALLINT UNSIGNED DEFAULT NULL, PRIMARY KEY (`steamid`), FOREIGN KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;",
|
||||
@"CREATE TABLE IF NOT EXISTS `wp_players_gloves` (`steamid` BIGINT UNSIGNED NOT NULL, `glove_t` SMALLINT UNSIGNED NOT NULL, `glove_ct` SMALLINT UNSIGNED NOT NULL, PRIMARY KEY (`steamid`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;"
|
||||
};*/
|
||||
foreach (var query in createTableQueries)
|
||||
{
|
||||
await connection.ExecuteAsync(query, transaction: transaction);
|
||||
}
|
||||
@@ -100,64 +90,37 @@ namespace WeaponPaints
|
||||
|
||||
internal static bool IsPlayerValid(CCSPlayerController? player)
|
||||
{
|
||||
if (player is null || WeaponPaints.WeaponSync is null) return false;
|
||||
if (player is null) return false;
|
||||
|
||||
return player is { IsValid: true, IsBot: false, IsHLTV: false, UserId: not null };
|
||||
return (player is not null && player.IsValid && !player.IsBot && !player.IsHLTV && player.UserId.HasValue
|
||||
&& WeaponPaints.weaponSync != null && player.Connected == PlayerConnectedState.PlayerConnected && player.SteamID.ToString().Length == 17);
|
||||
}
|
||||
|
||||
internal static void LoadSkinsFromFile(string filePath, ILogger logger)
|
||||
{
|
||||
var json = File.ReadAllText(filePath);
|
||||
try
|
||||
{
|
||||
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
|
||||
WeaponPaints.SkinsList = deserializedSkins ?? [];
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
logger?.LogError("Not found \"skins.json\" file");
|
||||
}
|
||||
}
|
||||
|
||||
internal static void LoadGlovesFromFile(string filePath, ILogger logger)
|
||||
internal static void LoadSkinsFromFile(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = File.ReadAllText(filePath);
|
||||
string json = File.ReadAllText(filePath);
|
||||
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
|
||||
WeaponPaints.GlovesList = deserializedSkins ?? [];
|
||||
WeaponPaints.skinsList = deserializedSkins ?? new List<JObject>();
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
logger?.LogError("Not found \"gloves.json\" file");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void LoadAgentsFromFile(string filePath, ILogger logger)
|
||||
internal static void LoadGlovesFromFile(string filePath)
|
||||
{
|
||||
try
|
||||
{
|
||||
var json = File.ReadAllText(filePath);
|
||||
string json = File.ReadAllText(filePath);
|
||||
var deserializedSkins = JsonConvert.DeserializeObject<List<JObject>>(json);
|
||||
WeaponPaints.AgentsList = deserializedSkins ?? [];
|
||||
WeaponPaints.glovesList = deserializedSkins ?? new List<JObject>();
|
||||
}
|
||||
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");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,49 +134,68 @@ namespace WeaponPaints
|
||||
|
||||
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)
|
||||
{
|
||||
using HttpClient client = new();
|
||||
|
||||
try
|
||||
using (HttpClient client = new HttpClient())
|
||||
{
|
||||
var response = await client.GetAsync("https://raw.githubusercontent.com/Nereziel/cs2-WeaponPaints/main/VERSION").ConfigureAwait(false);
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
try
|
||||
{
|
||||
var remoteVersion = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
remoteVersion = remoteVersion.Trim();
|
||||
HttpResponseMessage response = await client.GetAsync("https://raw.githubusercontent.com/Nereziel/cs2-WeaponPaints/main/VERSION").ConfigureAwait(false);
|
||||
|
||||
var comparisonResult = string.CompareOrdinal(version, remoteVersion);
|
||||
|
||||
switch (comparisonResult)
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
case < 0:
|
||||
string remoteVersion = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
|
||||
remoteVersion = remoteVersion.Trim();
|
||||
|
||||
int comparisonResult = string.Compare(version, remoteVersion);
|
||||
|
||||
if (comparisonResult < 0)
|
||||
{
|
||||
logger.LogWarning("Plugin is outdated! Check https://github.com/Nereziel/cs2-WeaponPaints");
|
||||
break;
|
||||
case > 0:
|
||||
}
|
||||
else if (comparisonResult > 0)
|
||||
{
|
||||
logger.LogInformation("Probably dev version detected");
|
||||
break;
|
||||
default:
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogInformation("Plugin is up to date");
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.LogWarning("Failed to check version");
|
||||
}
|
||||
}
|
||||
else
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
logger.LogWarning("Failed to check version");
|
||||
logger.LogError(ex, "Failed to connect to the version server.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "An error occurred while checking version.");
|
||||
}
|
||||
}
|
||||
catch (HttpRequestException ex)
|
||||
{
|
||||
logger.LogError(ex, "Failed to connect to the version server.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogError(ex, "An error occurred while checking version.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,5 +213,26 @@ namespace WeaponPaints
|
||||
Console.WriteLine(" >> GitHub: https://github.com/Nereziel/cs2-WeaponPaints");
|
||||
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();
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
166
Variables.cs
166
Variables.cs
@@ -1,166 +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();
|
||||
internal static readonly ConcurrentDictionary<int, ushort> GPlayersPin = 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");
|
||||
}
|
||||
587
WeaponAction.cs
587
WeaponAction.cs
@@ -1,375 +1,407 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Modules.Entities.Constants;
|
||||
using CounterStrikeSharp.API.Modules.Memory;
|
||||
using CounterStrikeSharp.API.Modules.Timers;
|
||||
using CounterStrikeSharp.API.Modules.Utils;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace 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 (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out _)) return;
|
||||
if (player is null || weapon is null || !weapon.IsValid || !Utility.IsPlayerValid(player)) return;
|
||||
if (!gPlayerWeaponsInfo.TryGetValue(player.Slot, out _)) return;
|
||||
|
||||
bool isKnife = weapon.DesignerName.Contains("knife") || weapon.DesignerName.Contains("bayonet");
|
||||
if (isKnife && !g_playersKnife.ContainsKey(player.Slot) || isKnife && g_playersKnife[player.Slot] == "weapon_knife") return;
|
||||
|
||||
if (isKnife && !GPlayersKnife.ContainsKey(player.Slot) || isKnife && GPlayersKnife[player.Slot] == "weapon_knife") return;
|
||||
|
||||
int[] newPaints = { 106, 112, 113, 114, 115, 117, 118, 120, 121, 123, 126, 127, 128, 129, 130, 131, 133, 134, 137, 138, 139, 140, 142, 144, 145, 146, 152, 160, 161, 163, 173, 239, 292, 324, 331, 412, 461, 513, 766, 768, 770, 773, 774, 830, 831, 832, 834, 874, 875, 877, 878, 882, 883, 901, 912, 936, 937, 938, 939, 940, 1054, 1062, 1159, 1160, 1161, 1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, 1170, 1171, 1172, 1173, 1174, 1175, 1177, 1178, 1179, 1180 };
|
||||
int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
|
||||
|
||||
if (isKnife)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
UpdatePlayerEconItemId(weapon.AttributeManager.Item);
|
||||
|
||||
int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
|
||||
int fallbackPaintKit = 0;
|
||||
|
||||
weapon.AttributeManager.Item.AccountID = (uint)player.SteamID;
|
||||
int fallbackPaintKit = weapon.FallbackPaintKit;
|
||||
|
||||
if (_config.Additional.GiveRandomSkin &&
|
||||
!GPlayerWeaponsInfo[player.Slot].ContainsKey(weaponDefIndex))
|
||||
!gPlayerWeaponsInfo[player.Slot].ContainsKey(weaponDefIndex))
|
||||
{
|
||||
// Random skins
|
||||
weapon.AttributeManager.Item.ItemID = 16384;
|
||||
weapon.AttributeManager.Item.ItemIDLow = 16384 & 0xFFFFFFFF;
|
||||
weapon.AttributeManager.Item.ItemIDHigh = weapon.AttributeManager.Item.ItemIDLow >> 32;
|
||||
weapon.FallbackPaintKit = GetRandomPaint(weaponDefIndex);
|
||||
weapon.FallbackSeed = 0;
|
||||
weapon.FallbackWear = 0.01f;
|
||||
|
||||
weapon.AttributeManager.Item.NetworkedDynamicAttributes.Attributes.RemoveAll();
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture prefab", GetRandomPaint(weaponDefIndex));
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture seed", 0);
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture wear", 0.01f);
|
||||
|
||||
weapon.AttributeManager.Item.AttributeList.Attributes.RemoveAll();
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "set item texture prefab", GetRandomPaint(weaponDefIndex));
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "set item texture seed", 0);
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "set item texture wear", 0.01f);
|
||||
weapon.FallbackWear = 0.000001f;
|
||||
|
||||
fallbackPaintKit = weapon.FallbackPaintKit;
|
||||
|
||||
if (fallbackPaintKit == 0)
|
||||
return;
|
||||
|
||||
if (isKnife) return;
|
||||
UpdatePlayerWeaponMeshGroupMask(player, weapon, !newPaints.Contains(fallbackPaintKit));
|
||||
var foundSkin = skinsList.FirstOrDefault(skin =>
|
||||
((int?)skin?["weapon_defindex"] ?? 0) == weaponDefIndex &&
|
||||
((int?)skin?["paint"] ?? 0) == fallbackPaintKit &&
|
||||
skin?["paint_name"] != null
|
||||
);
|
||||
|
||||
string skinName = foundSkin?["paint_name"]?.ToString() ?? "";
|
||||
if (!string.IsNullOrEmpty(skinName))
|
||||
new SchemaString<CEconItemView>(weapon.AttributeManager.Item, "m_szCustomName").Set(skinName);
|
||||
|
||||
if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null)
|
||||
{
|
||||
var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode);
|
||||
|
||||
int[] newPaints = { 1171, 1170, 1169, 1164, 1162, 1161, 1159, 1175, 1174, 1167, 1165, 1168, 1163, 1160, 1166, 1173 };
|
||||
|
||||
if (newPaints.Contains(fallbackPaintKit))
|
||||
{
|
||||
skeleton.ModelState.MeshGroupMask = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (skeleton.ModelState.MeshGroupMask != 2)
|
||||
{
|
||||
skeleton.ModelState.MeshGroupMask = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var viewModels = GetPlayerViewModels(player);
|
||||
if (viewModels == null || viewModels.Length == 0)
|
||||
return;
|
||||
|
||||
var viewModel = viewModels[0];
|
||||
if (viewModel == null || viewModel.Value == null || viewModel.Value.Weapon == null || viewModel.Value.Weapon.Value == null)
|
||||
return;
|
||||
|
||||
Utilities.SetStateChanged(viewModel.Value, "CBaseEntity", "m_CBodyComponent");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GPlayerWeaponsInfo[player.Slot].TryGetValue(weaponDefIndex, out var value) || value.Paint == 0) return;
|
||||
|
||||
var weaponInfo = value;
|
||||
if (!gPlayerWeaponsInfo[player.Slot].ContainsKey(weaponDefIndex)) return;
|
||||
WeaponInfo weaponInfo = gPlayerWeaponsInfo[player.Slot][weaponDefIndex];
|
||||
//Log($"Apply on {weapon.DesignerName}({weapon.AttributeManager.Item.ItemDefinitionIndex}) paint {gPlayerWeaponPaints[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex]} seed {gPlayerWeaponSeed[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex]} wear {gPlayerWeaponWear[steamId.SteamId64][weapon.AttributeManager.Item.ItemDefinitionIndex]}");
|
||||
|
||||
weapon.AttributeManager.Item.ItemID = 16384;
|
||||
weapon.AttributeManager.Item.ItemIDLow = 16384 & 0xFFFFFFFF;
|
||||
weapon.AttributeManager.Item.ItemIDHigh = weapon.AttributeManager.Item.ItemIDLow >> 32;
|
||||
weapon.AttributeManager.Item.CustomName = weaponInfo.Nametag;
|
||||
weapon.FallbackPaintKit = weaponInfo.Paint;
|
||||
weapon.FallbackSeed = weaponInfo.Seed;
|
||||
weapon.FallbackWear = weaponInfo.Wear;
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "set item texture prefab", weapon.FallbackPaintKit);
|
||||
|
||||
if (weaponInfo.StatTrak)
|
||||
{
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater", ViewAsFloat((uint)weaponInfo.StatTrakCount));
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle, "kill eater score type", 0);
|
||||
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater", ViewAsFloat((uint)weaponInfo.StatTrakCount));
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.AttributeList.Handle, "kill eater score type", 0);
|
||||
}
|
||||
|
||||
fallbackPaintKit = weapon.FallbackPaintKit;
|
||||
|
||||
if (fallbackPaintKit == 0)
|
||||
return;
|
||||
|
||||
if (isKnife) return;
|
||||
var foundSkin1 = skinsList.FirstOrDefault(skin =>
|
||||
((int?)skin?["weapon_defindex"] ?? 0) == weaponDefIndex &&
|
||||
((int?)skin?["paint"] ?? 0) == fallbackPaintKit &&
|
||||
skin?["paint_name"] != null
|
||||
);
|
||||
|
||||
if (weaponInfo.Stickers.Count > 0) SetStickers(player, weapon);
|
||||
if (weaponInfo.KeyChain != null) SetKeychain(player, weapon);
|
||||
var skinName1 = foundSkin1?["paint_name"]?.ToString() ?? "";
|
||||
if (!string.IsNullOrEmpty(skinName1))
|
||||
new SchemaString<CEconItemView>(weapon.AttributeManager.Item, "m_szCustomName").Set(skinName1);
|
||||
|
||||
UpdatePlayerWeaponMeshGroupMask(player, weapon, !newPaints.Contains(fallbackPaintKit));
|
||||
}
|
||||
|
||||
// silly method to update sticker when call RefreshWeapons()
|
||||
private void IncrementWearForWeaponWithStickers(CCSPlayerController player, CBasePlayerWeapon weapon)
|
||||
{
|
||||
int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
|
||||
if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out var playerWeapons) ||
|
||||
!playerWeapons.TryGetValue(weaponDefIndex, out var weaponInfo) ||
|
||||
weaponInfo.Stickers.Count <= 0) return;
|
||||
|
||||
float wearIncrement = 0.001f;
|
||||
float currentWear = weaponInfo.Wear;
|
||||
|
||||
var playerWear = _temporaryPlayerWeaponWear.GetOrAdd(player.Slot, _ => new ConcurrentDictionary<int, float>());
|
||||
|
||||
float incrementedWear = playerWear.AddOrUpdate(
|
||||
weaponDefIndex,
|
||||
currentWear + wearIncrement,
|
||||
(_, oldWear) => Math.Min(oldWear + wearIncrement, 1.0f)
|
||||
);
|
||||
|
||||
weapon.FallbackWear = incrementedWear;
|
||||
}
|
||||
|
||||
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))
|
||||
if (!isKnife && weapon.CBodyComponent != null && weapon.CBodyComponent.SceneNode != null)
|
||||
{
|
||||
var skeleton = GetSkeletonInstance(weapon.CBodyComponent.SceneNode);
|
||||
int[] newPaints = { 1171, 1170, 1169, 1164, 1162, 1161, 1159, 1175, 1174, 1167, 1165, 1168, 1163, 1160, 1166, 1173 };
|
||||
if (newPaints.Contains(fallbackPaintKit))
|
||||
{
|
||||
skeleton.ModelState.MeshGroupMask = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (skeleton.ModelState.MeshGroupMask != 2)
|
||||
{
|
||||
skeleton.ModelState.MeshGroupMask = 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var viewModels1 = GetPlayerViewModels(player);
|
||||
if (viewModels1 == null || viewModels1.Length == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var sticker in weaponInfo.Stickers)
|
||||
{
|
||||
int stickerSlot = weaponInfo.Stickers.IndexOf(sticker);
|
||||
var viewModel1 = viewModels1[0];
|
||||
if (viewModel1 == null || viewModel1.Value == null || viewModel1.Value.Weapon == null || viewModel1.Value.Weapon.Value == null)
|
||||
return;
|
||||
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
||||
$"sticker slot {stickerSlot} id", ViewAsFloat(sticker.Id));
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
||||
$"sticker slot {stickerSlot} schema", sticker.Schema);
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
||||
$"sticker slot {stickerSlot} offset x", sticker.OffsetX);
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
||||
$"sticker slot {stickerSlot} offset y", sticker.OffsetY);
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
||||
$"sticker slot {stickerSlot} wear", sticker.Wear);
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
||||
$"sticker slot {stickerSlot} scale", sticker.Scale);
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle,
|
||||
$"sticker slot {stickerSlot} rotation", sticker.Rotation);
|
||||
}
|
||||
|
||||
if (_temporaryPlayerWeaponWear.TryGetValue(player.Slot, out var playerWear) &&
|
||||
playerWear.TryGetValue(weaponDefIndex, out float storedWear))
|
||||
{
|
||||
weapon.FallbackWear = storedWear;
|
||||
}
|
||||
Utilities.SetStateChanged(viewModel1.Value, "CBaseEntity", "m_CBodyComponent");
|
||||
}
|
||||
|
||||
private void SetKeychain(CCSPlayerController? player, CBasePlayerWeapon weapon)
|
||||
{
|
||||
if (player == null || !player.IsValid) return;
|
||||
|
||||
int weaponDefIndex = weapon.AttributeManager.Item.ItemDefinitionIndex;
|
||||
|
||||
if (!GPlayerWeaponsInfo.TryGetValue(player.Slot, out var playerWeaponsInfo) ||
|
||||
!playerWeaponsInfo.TryGetValue(weaponDefIndex, out var value) ||
|
||||
value.KeyChain == null) 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)
|
||||
internal static void GiveKnifeToPlayer(CCSPlayerController? player)
|
||||
{
|
||||
if (!_config.Additional.KnifeEnabled || player == null || !player.IsValid) return;
|
||||
|
||||
if (PlayerHasKnife(player)) return;
|
||||
WeaponPaints.Instance.AddTimer(1.0f, () =>
|
||||
{
|
||||
string knifeToGive;
|
||||
if (g_playersKnife.TryGetValue(player.Slot, out var knife))
|
||||
{
|
||||
knifeToGive = knife;
|
||||
}
|
||||
else if (_config.Additional.GiveRandomKnife)
|
||||
{
|
||||
var knifeTypes = weaponList.Where(pair => pair.Key.StartsWith("weapon_knife") || pair.Key.StartsWith("weapon_bayonet")).ToList();
|
||||
|
||||
//string knifeToGive = (CsTeam)player.TeamNum == CsTeam.Terrorist ? "weapon_knife_t" : "weapon_knife";
|
||||
player.GiveNamedItem(CsItem.Knife);
|
||||
if (knifeTypes.Count == 0)
|
||||
{
|
||||
Utility.Log("No valid knife types found.");
|
||||
return;
|
||||
}
|
||||
|
||||
Random random = new();
|
||||
int index = random.Next(knifeTypes.Count);
|
||||
knifeToGive = knifeTypes[index].Key;
|
||||
}
|
||||
else
|
||||
{
|
||||
knifeToGive = (CsTeam)player.TeamNum == CsTeam.Terrorist ? "weapon_knife_t" : "weapon_knife";
|
||||
}
|
||||
|
||||
player.GiveNamedItem(knifeToGive);
|
||||
});
|
||||
}
|
||||
|
||||
private static bool PlayerHasKnife(CCSPlayerController? player)
|
||||
internal static bool PlayerHasKnife(CCSPlayerController? player)
|
||||
{
|
||||
if (!_config.Additional.KnifeEnabled) return false;
|
||||
|
||||
if (player == null || !player.IsValid || !player.PlayerPawn.IsValid)
|
||||
if (player == null || !player.IsValid || player.PlayerPawn == null || !player.PlayerPawn.IsValid)
|
||||
{
|
||||
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;
|
||||
|
||||
var weapons = player.PlayerPawn.Value.WeaponServices?.MyWeapons;
|
||||
if (weapons == null) return false;
|
||||
foreach (var weapon in weapons)
|
||||
{
|
||||
if (!weapon.IsValid || weapon.Value == null || !weapon.Value.IsValid) continue;
|
||||
if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet"))
|
||||
if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid)
|
||||
{
|
||||
return true;
|
||||
if (weapon.Value.DesignerName.Contains("knife") || weapon.Value.DesignerName.Contains("bayonet"))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void RefreshWeapons(CCSPlayerController? player)
|
||||
internal void RefreshWeapons(CCSPlayerController? player)
|
||||
{
|
||||
if (!_gBCommandsAllowed) return;
|
||||
if (player == null || !player.IsValid || player.PlayerPawn.Value == null || (LifeState_t)player.LifeState != LifeState_t.LIFE_ALIVE)
|
||||
if (player == null || !player.IsValid || player.PlayerPawn?.Value == null || (LifeState_t)player.LifeState != LifeState_t.LIFE_ALIVE)
|
||||
return;
|
||||
if (player.PlayerPawn.Value.WeaponServices == null || player.PlayerPawn.Value.ItemServices == null)
|
||||
return;
|
||||
|
||||
var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons;
|
||||
|
||||
if (weapons.Count == 0)
|
||||
if (weapons == null || weapons.Count == 0)
|
||||
return;
|
||||
if (player.Team is CsTeam.None or CsTeam.Spectator)
|
||||
if (player.Team == CsTeam.None || player.Team == CsTeam.Spectator)
|
||||
return;
|
||||
|
||||
int playerTeam = player.TeamNum;
|
||||
|
||||
Dictionary<string, List<(int, int)>> weaponsWithAmmo = [];
|
||||
//Dictionary<string, (int, int)> weaponsWithAmmo = new Dictionary<string, (int, int)>();
|
||||
Dictionary<string, List<(int, int)>> weaponsWithAmmo = new Dictionary<string, List<(int, int)>>();
|
||||
|
||||
// Iterate through each weapon
|
||||
foreach (var weapon in weapons)
|
||||
{
|
||||
if (!weapon.IsValid || weapon.Value == null ||
|
||||
if (weapon == null || !weapon.IsValid || weapon.Value == null ||
|
||||
!weapon.Value.IsValid || !weapon.Value.DesignerName.Contains("weapon_"))
|
||||
continue;
|
||||
|
||||
CCSWeaponBaseGun gun = weapon.Value.As<CCSWeaponBaseGun>();
|
||||
|
||||
if (weapon.Value.Entity == null) continue;
|
||||
if (weapon.Value.OwnerEntity == null) continue;
|
||||
if (!weapon.Value.OwnerEntity.IsValid) continue;
|
||||
if (gun == null) continue;
|
||||
if (gun.Entity == null) continue;
|
||||
if (!gun.IsValid) continue;
|
||||
if (!gun.VisibleinPVS) continue;
|
||||
|
||||
try
|
||||
{
|
||||
string? weaponByDefindex = null;
|
||||
|
||||
CCSWeaponBaseVData? weaponData = weapon.Value.As<CCSWeaponBase>().VData;
|
||||
|
||||
if (weaponData == null) continue;
|
||||
|
||||
if (weaponData.GearSlot is gear_slot_t.GEAR_SLOT_RIFLE or gear_slot_t.GEAR_SLOT_PISTOL)
|
||||
if (weaponData.GearSlot == gear_slot_t.GEAR_SLOT_RIFLE || weaponData.GearSlot == gear_slot_t.GEAR_SLOT_PISTOL)
|
||||
{
|
||||
if (!WeaponDefindex.TryGetValue(weapon.Value.AttributeManager.Item.ItemDefinitionIndex, out var weaponByDefindex))
|
||||
if (!WeaponDefindex.TryGetValue(weapon.Value.AttributeManager.Item.ItemDefinitionIndex, out weaponByDefindex))
|
||||
continue;
|
||||
|
||||
int clip1 = weapon.Value.Clip1;
|
||||
int reservedAmmo = weapon.Value.ReserveAmmo[0];
|
||||
|
||||
if (!weaponsWithAmmo.TryGetValue(weaponByDefindex, out var value))
|
||||
if (!weaponsWithAmmo.ContainsKey(weaponByDefindex))
|
||||
{
|
||||
value = [];
|
||||
weaponsWithAmmo.Add(weaponByDefindex, value);
|
||||
weaponsWithAmmo.Add(weaponByDefindex, new List<(int, int)>());
|
||||
}
|
||||
|
||||
value.Add((clip1, reservedAmmo));
|
||||
|
||||
if (gun.VData == null) return;
|
||||
|
||||
weapon.Value?.AddEntityIOEvent("Kill", weapon.Value, null, "", 0.1f);
|
||||
weaponsWithAmmo[weaponByDefindex].Add((clip1, reservedAmmo));
|
||||
}
|
||||
|
||||
if (weaponData.GearSlot == gear_slot_t.GEAR_SLOT_KNIFE)
|
||||
{
|
||||
weapon.Value?.AddEntityIOEvent("Kill", weapon.Value, null, "", 0.1f);
|
||||
}
|
||||
//player.RemoveItemByDesignerName(weapon.Value.DesignerName, false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex.Message);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
AddTimer(0.23f, () =>
|
||||
{
|
||||
if (!_gBCommandsAllowed) return;
|
||||
|
||||
if (!PlayerHasKnife(player))
|
||||
GiveKnifeToPlayer(player);
|
||||
for (int i = 1; i <= 3; i++)
|
||||
{
|
||||
player.ExecuteClientCommand($"slot {i}");
|
||||
player.ExecuteClientCommand($"slot {i}");
|
||||
|
||||
foreach (var entry in weaponsWithAmmo)
|
||||
AddTimer(0.1f, () =>
|
||||
{
|
||||
var weapon = player.PlayerPawn.Value.WeaponServices.ActiveWeapon.Value;
|
||||
CCSWeaponBaseGun? gun = weapon?.As<CCSWeaponBaseGun>();
|
||||
|
||||
if (gun == null || gun.VData == null) return;
|
||||
|
||||
if (gun?.VData?.GearSlot == gear_slot_t.GEAR_SLOT_C4 || gun?.VData?.GearSlot == gear_slot_t.GEAR_SLOT_GRENADES) return;
|
||||
|
||||
player.DropActiveWeapon();
|
||||
|
||||
AddTimer(0.22f, () =>
|
||||
{
|
||||
if (gun != null && gun.IsValid && gun.State == CSWeaponState_t.WEAPON_NOT_CARRIED)
|
||||
{
|
||||
foreach (var ammo in entry.Value)
|
||||
{
|
||||
var newWeapon = new CBasePlayerWeapon(player.GiveNamedItem(entry.Key));
|
||||
Server.NextFrame(() =>
|
||||
weapon?.Remove();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
AddTimer(1.2f, () =>
|
||||
{
|
||||
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);
|
||||
if (newWeapon != null)
|
||||
{
|
||||
newWeapon.Clip1 = ammo.Item1;
|
||||
newWeapon.ReserveAmmo[0] = ammo.Item2;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning("Error setting weapon properties: " + ex.Message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}, TimerFlags.STOP_ON_MAPCHANGE);
|
||||
}
|
||||
}
|
||||
}, TimerFlags.STOP_ON_MAPCHANGE);
|
||||
}
|
||||
|
||||
private void GivePlayerGloves(CCSPlayerController player)
|
||||
internal void RefreshKnife(CCSPlayerController? player)
|
||||
{
|
||||
if (player == null || !player.IsValid || player.PlayerPawn?.Value == null)
|
||||
return;
|
||||
|
||||
if (player.PlayerPawn.Value.WeaponServices == null)
|
||||
return;
|
||||
|
||||
var weapons = player.PlayerPawn.Value.WeaponServices.MyWeapons;
|
||||
if (weapons != null && weapons.Count > 0)
|
||||
{
|
||||
foreach (var weapon in weapons)
|
||||
{
|
||||
if (weapon != null && weapon.IsValid && weapon.Value != null && weapon.Value.IsValid && weapon.Index > 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
CCSWeaponBaseVData? weaponData = weapon.Value.As<CCSWeaponBase>().VData;
|
||||
|
||||
if (weapon.Value.DesignerName.Contains("knife") || weaponData?.GearSlot == gear_slot_t.GEAR_SLOT_KNIFE)
|
||||
{
|
||||
player.ExecuteClientCommand("slot 3");
|
||||
|
||||
weapon.Value.Remove();
|
||||
GiveKnifeToPlayer(player);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning($"Cannot remove knife: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void RefreshGloves(CCSPlayerController player)
|
||||
{
|
||||
if (!Utility.IsPlayerValid(player) || (LifeState_t)player.LifeState != LifeState_t.LIFE_ALIVE) return;
|
||||
|
||||
CCSPlayerPawn? pawn = player.PlayerPawn.Value;
|
||||
if (pawn == null || !pawn.IsValid)
|
||||
if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE)
|
||||
return;
|
||||
|
||||
var model = pawn.CBodyComponent?.SceneNode?.GetSkeletonInstance()?.ModelState.ModelName ?? string.Empty;
|
||||
string model = pawn.CBodyComponent?.SceneNode?.GetSkeletonInstance()?.ModelState.ModelName ?? string.Empty;
|
||||
if (!string.IsNullOrEmpty(model))
|
||||
{
|
||||
pawn.SetModel("characters/models/tm_jumpsuit/tm_jumpsuit_varianta.vmdl");
|
||||
pawn.SetModel(model);
|
||||
}
|
||||
|
||||
Instance.AddTimer(0.08f, () =>
|
||||
Instance.AddTimer(0.06f, () =>
|
||||
{
|
||||
CEconItemView item = pawn.EconGloves;
|
||||
try
|
||||
{
|
||||
if (!player.IsValid)
|
||||
if (player == null || !player.IsValid)
|
||||
return;
|
||||
|
||||
if (!player.PawnIsAlive)
|
||||
if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE)
|
||||
return;
|
||||
|
||||
if (!GPlayersGlove.TryGetValue(player.Slot, out var gloveInfo) || gloveInfo == 0) return;
|
||||
if (g_playersGlove.TryGetValue(player.Slot, out var gloveInfo) && gloveInfo != 0)
|
||||
{
|
||||
CCSPlayerPawn? pawn = player.PlayerPawn.Value;
|
||||
if (pawn == null || !pawn.IsValid || pawn.LifeState != (byte)LifeState_t.LIFE_ALIVE)
|
||||
return;
|
||||
|
||||
WeaponInfo weaponInfo = GPlayerWeaponsInfo[player.Slot][gloveInfo];
|
||||
WeaponInfo weaponInfo = gPlayerWeaponsInfo[player.Slot][gloveInfo];
|
||||
|
||||
item.ItemDefinitionIndex = gloveInfo;
|
||||
item.ItemIDLow = 16384 & 0xFFFFFFFF;
|
||||
item.ItemIDHigh = 16384;
|
||||
CEconItemView item = pawn.EconGloves;
|
||||
item.ItemDefinitionIndex = gloveInfo;
|
||||
item.ItemIDLow = 16384 & 0xFFFFFFFF;
|
||||
item.ItemIDHigh = 16384;
|
||||
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture prefab", weaponInfo.Paint);
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture seed", weaponInfo.Seed);
|
||||
CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture wear", weaponInfo.Wear);
|
||||
CAttributeList_SetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture prefab", weaponInfo.Paint);
|
||||
CAttributeList_SetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture seed", weaponInfo.Seed);
|
||||
CAttributeList_SetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture wear", weaponInfo.Wear);
|
||||
|
||||
item.Initialized = true;
|
||||
item.Initialized = true;
|
||||
|
||||
SetBodygroup(pawn.Handle, "default_gloves", 1);
|
||||
CBaseModelEntity_SetBodygroup.Invoke(pawn, "default_gloves", 1);
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}, TimerFlags.STOP_ON_MAPCHANGE);
|
||||
@@ -377,149 +409,50 @@ namespace WeaponPaints
|
||||
|
||||
private static int GetRandomPaint(int defindex)
|
||||
{
|
||||
if (SkinsList.Count == 0)
|
||||
if (skinsList == null || skinsList.Count == 0)
|
||||
return 0;
|
||||
|
||||
Random rnd = new Random();
|
||||
|
||||
// Filter weapons by the provided defindex
|
||||
var filteredWeapons = SkinsList.Where(w => w["weapon_defindex"]?.ToString() == defindex.ToString()).ToList();
|
||||
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;
|
||||
if (int.TryParse(randomWeapon["paint"]?.ToString(), out int paintValue))
|
||||
return paintValue;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void SubclassChange(CBasePlayerWeapon weapon, ushort itemD)
|
||||
private static CSkeletonInstance GetSkeletonInstance(CGameSceneNode node)
|
||||
{
|
||||
var subclassChangeFunc = VirtualFunction.Create<nint, string, int>(
|
||||
GameData.GetSignature("ChangeSubclass")
|
||||
);
|
||||
|
||||
subclassChangeFunc(weapon.Handle, itemD.ToString());
|
||||
Func<nint, nint> GetSkeletonInstance = VirtualFunction.Create<nint, nint>(node.Handle, 8);
|
||||
return new CSkeletonInstance(GetSkeletonInstance(node.Handle));
|
||||
}
|
||||
|
||||
private static void UpdateWeaponMeshGroupMask(CBaseEntity weapon, bool isLegacy = false)
|
||||
{
|
||||
if (weapon.CBodyComponent?.SceneNode == null) return;
|
||||
var skeleton = weapon.CBodyComponent.SceneNode.GetSkeletonInstance();
|
||||
var value = (ulong)(isLegacy ? 2 : 1);
|
||||
|
||||
if (skeleton.ModelState.MeshGroupMask != value)
|
||||
{
|
||||
skeleton.ModelState.MeshGroupMask = value;
|
||||
}
|
||||
}
|
||||
|
||||
private static void UpdatePlayerWeaponMeshGroupMask(CCSPlayerController player, CBasePlayerWeapon weapon, bool isLegacy)
|
||||
{
|
||||
UpdateWeaponMeshGroupMask(weapon, isLegacy);
|
||||
|
||||
var viewModel = GetPlayerViewModel(player);
|
||||
if (viewModel == null || viewModel.Weapon.Value == null ||
|
||||
viewModel.Weapon.Value.Index != weapon.Index) return;
|
||||
UpdateWeaponMeshGroupMask(viewModel, isLegacy);
|
||||
Utilities.SetStateChanged(viewModel, "CBaseEntity", "m_CBodyComponent");
|
||||
}
|
||||
|
||||
private static void GivePlayerAgent(CCSPlayerController player)
|
||||
{
|
||||
if (!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(
|
||||
$"characters/models/{model}.vmdl"
|
||||
);
|
||||
});
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
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 static void GivePlayerPin(CCSPlayerController player)
|
||||
{
|
||||
if (!GPlayersPin.TryGetValue(player.Slot, out var pin)) return;
|
||||
|
||||
if (player.InventoryServices == null) return;
|
||||
|
||||
for (var index = 0; index < player.InventoryServices.Rank.Length; index++)
|
||||
{
|
||||
player.InventoryServices.Rank[index] = index == 5 ? (MedalRank_t)pin : MedalRank_t.MEDAL_RANK_NONE;
|
||||
Utilities.SetStateChanged(player, "CCSPlayerController", "m_pInventoryServices");
|
||||
}
|
||||
}
|
||||
|
||||
private void GiveOnItemPickup(CCSPlayerController player)
|
||||
{
|
||||
var pawn = player.PlayerPawn.Value;
|
||||
if (pawn == null) return;
|
||||
|
||||
var myWeapons = pawn.WeaponServices?.MyWeapons;
|
||||
if (myWeapons == null) return;
|
||||
foreach (var handle in myWeapons)
|
||||
{
|
||||
var weapon = handle.Value;
|
||||
if (weapon != null && weapon.DesignerName.Contains("knife"))
|
||||
{
|
||||
GivePlayerWeaponSkin(player, weapon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePlayerEconItemId(CEconItemView econItemView)
|
||||
{
|
||||
var itemId = _nextItemId++;
|
||||
econItemView.ItemID = itemId;
|
||||
|
||||
econItemView.ItemIDLow = (uint)itemId & 0xFFFFFFFF;
|
||||
econItemView.ItemIDHigh = (uint)itemId >> 32;
|
||||
}
|
||||
|
||||
private static CCSPlayerController? GetPlayerFromItemServices(CCSPlayer_ItemServices itemServices)
|
||||
{
|
||||
var pawn = itemServices.Pawn.Value;
|
||||
if (!pawn.IsValid || !pawn.Controller.IsValid || pawn.Controller.Value == null) return null;
|
||||
var player = new CCSPlayerController(pawn.Controller.Value.Handle);
|
||||
return !Utility.IsPlayerValid(player) ? null : player;
|
||||
}
|
||||
|
||||
private static unsafe CBaseViewModel? GetPlayerViewModel(CCSPlayerController player)
|
||||
private static unsafe CHandle<CBaseViewModel>[]? GetPlayerViewModels(CCSPlayerController player)
|
||||
{
|
||||
if (player.PlayerPawn.Value == null || player.PlayerPawn.Value.ViewModelServices == null) return null;
|
||||
CCSPlayer_ViewModelServices viewModelServices = new(player.PlayerPawn.Value.ViewModelServices!.Handle);
|
||||
var ptr = viewModelServices.Handle + Schema.GetSchemaOffset("CCSPlayer_ViewModelServices", "m_hViewModel");
|
||||
var references = MemoryMarshal.CreateSpan(ref ptr, 3);
|
||||
var viewModel = (CHandle<CBaseViewModel>)Activator.CreateInstance(typeof(CHandle<CBaseViewModel>), references[0])!;
|
||||
return viewModel.Value == null ? null : viewModel.Value;
|
||||
CCSPlayer_ViewModelServices viewModelServices = new CCSPlayer_ViewModelServices(player.PlayerPawn.Value.ViewModelServices!.Handle);
|
||||
return GetFixedArray<CHandle<CBaseViewModel>>(viewModelServices.Handle, "CCSPlayer_ViewModelServices", "m_hViewModel", 3);
|
||||
}
|
||||
|
||||
private static float ViewAsFloat(uint value)
|
||||
public static unsafe T[] GetFixedArray<T>(nint pointer, string @class, string member, int length) where T : CHandle<CBaseViewModel>
|
||||
{
|
||||
return BitConverter.Int32BitsToSingle((int)value);
|
||||
nint ptr = pointer + Schema.GetSchemaOffset(@class, member);
|
||||
Span<nint> references = MemoryMarshal.CreateSpan<nint>(ref ptr, length);
|
||||
T[] values = new T[length];
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
values[i] = (T)Activator.CreateInstance(typeof(T), references[i])!;
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,18 @@
|
||||
namespace WeaponPaints
|
||||
{
|
||||
public class WeaponInfo
|
||||
{
|
||||
public int Paint { get; set; }
|
||||
public int Seed { get; set; }
|
||||
public float Wear { get; set; }
|
||||
public string Nametag { get; set; } = "";
|
||||
public bool StatTrak { get; set; } = false;
|
||||
public int StatTrakCount { get; set; }
|
||||
public KeyChainInfo? KeyChain { get; set; }
|
||||
public List<StickerInfo> Stickers { get; set; } = new();
|
||||
}
|
||||
public class WeaponInfo
|
||||
{
|
||||
public int Paint { get; set; }
|
||||
public int Seed { get; set; }
|
||||
public float Wear { get; set; }
|
||||
|
||||
public class StickerInfo
|
||||
{
|
||||
public uint Id { get; set; }
|
||||
public uint Schema { get; set; }
|
||||
public float OffsetX { get; set; }
|
||||
public float OffsetY { get; set; }
|
||||
public float Wear { get; set; }
|
||||
public float Scale { get; set; }
|
||||
public float Rotation { get; set; }
|
||||
}
|
||||
public WeaponInfo() : this(0, 0, 0f) { }
|
||||
|
||||
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; }
|
||||
}
|
||||
public WeaponInfo(int paint, int seed, float wear)
|
||||
{
|
||||
Paint = paint;
|
||||
Seed = seed;
|
||||
Wear = wear;
|
||||
}
|
||||
}
|
||||
}
|
||||
263
WeaponPaints.cs
263
WeaponPaints.cs
@@ -1,22 +1,167 @@
|
||||
using CounterStrikeSharp.API;
|
||||
using CounterStrikeSharp.API.Core;
|
||||
using CounterStrikeSharp.API.Core.Attributes;
|
||||
using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using MySqlConnector;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace WeaponPaints;
|
||||
|
||||
[MinimumApiVersion(276)]
|
||||
[MinimumApiVersion(168)]
|
||||
public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig>
|
||||
{
|
||||
internal static WeaponPaints Instance { get; private set; } = new();
|
||||
internal static readonly int[] newPaints = { 1171, 1170, 1169, 1164, 1162, 1161, 1159, 1175, 1174, 1167, 1165, 1168, 1163, 1160, 1166, 1173 };
|
||||
internal static readonly Dictionary<string, string> weaponList = new()
|
||||
{
|
||||
{"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 WeaponPaintsConfig Config { get; set; } = new();
|
||||
private static WeaponPaintsConfig _config { get; set; } = new();
|
||||
public override string ModuleAuthor => "Nereziel & daffyy";
|
||||
public override string ModuleDescription => "Skin, gloves, agents and knife selector, standalone and web-based";
|
||||
internal static WeaponPaintsConfig _config = new WeaponPaintsConfig();
|
||||
internal static IStringLocalizer? _localizer;
|
||||
internal static Dictionary<int, int> g_knifePickupCount = new Dictionary<int, int>();
|
||||
internal static ConcurrentDictionary<int, string> g_playersKnife = new ConcurrentDictionary<int, string>();
|
||||
internal static ConcurrentDictionary<int, ushort> g_playersGlove = new ConcurrentDictionary<int, ushort>();
|
||||
internal static ConcurrentDictionary<int, ConcurrentDictionary<int, WeaponInfo>> gPlayerWeaponsInfo = new ConcurrentDictionary<int, ConcurrentDictionary<int, WeaponInfo>>();
|
||||
internal static List<JObject> skinsList = new List<JObject>();
|
||||
internal static List<JObject> glovesList = new List<JObject>();
|
||||
internal static WeaponSynchronization? weaponSync;
|
||||
public static bool g_bCommandsAllowed = true;
|
||||
internal Dictionary<int, string> PlayerWeaponImage = new();
|
||||
|
||||
internal static Dictionary<int, DateTime> commandsCooldown = new Dictionary<int, DateTime>();
|
||||
internal static Database? _database;
|
||||
|
||||
internal static MemoryFunctionVoid<nint, string, float> CAttributeList_SetOrAddAttributeValueByName = new(GameData.GetSignature("CAttributeList_SetOrAddAttributeValueByName"));
|
||||
internal static MemoryFunctionVoid<CBaseModelEntity, string, UInt64> CBaseModelEntity_SetBodygroup = new(GameData.GetSignature("CBaseModelEntity_SetBodygroup"));
|
||||
|
||||
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" },
|
||||
{ 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" }
|
||||
};
|
||||
public static MemoryFunctionVoid<IntPtr, string, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr, IntPtr> GiveNamedItem2 = new(@"\x55\x48\x89\xE5\x41\x57\x41\x56\x41\x55\x41\x54\x53\x48\x83\xEC\x18\x48\x89\x7D\xC8\x48\x85\xF6\x74");
|
||||
public WeaponPaintsConfig Config { get; set; } = new();
|
||||
public override string ModuleAuthor => "Nereziel & daffyy";
|
||||
public override string ModuleDescription => "Skin, gloves and knife selector, standalone and web-based";
|
||||
public override string ModuleName => "WeaponPaints";
|
||||
public override string ModuleVersion => "2.7a";
|
||||
public override string ModuleVersion => "1.9b";
|
||||
|
||||
public static WeaponPaintsConfig GetWeaponPaintsConfig()
|
||||
{
|
||||
return _config;
|
||||
}
|
||||
|
||||
public override void Load(bool hotReload)
|
||||
{
|
||||
@@ -26,41 +171,41 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
|
||||
{
|
||||
OnMapStart(string.Empty);
|
||||
|
||||
foreach (var player in Enumerable
|
||||
.OfType<CCSPlayerController>(Utilities.GetPlayers().TakeWhile(player => WeaponSync != null))
|
||||
.Where(player => player.IsValid &&
|
||||
!string.IsNullOrEmpty(player.IpAddress) && player is
|
||||
{ IsBot: false, Connected: PlayerConnectedState.PlayerConnected }))
|
||||
foreach (var player in Utilities.GetPlayers())
|
||||
{
|
||||
GPlayerWeaponsInfo.TryRemove(player.Slot, out _);
|
||||
GPlayersKnife.TryRemove(player.Slot, out _);
|
||||
GPlayersGlove.TryRemove(player.Slot, out _);
|
||||
GPlayersAgent.TryRemove(player.Slot, out _);
|
||||
GPlayersPin.TryRemove(player.Slot, out _);
|
||||
if (weaponSync == null || player is null || !player.IsValid || player.SteamID.ToString().Length != 17 || !player.PawnIsAlive || player.IsBot ||
|
||||
player.IsHLTV || player.Connected != PlayerConnectedState.PlayerConnected)
|
||||
continue;
|
||||
|
||||
var playerInfo = new PlayerInfo
|
||||
{
|
||||
UserId = player.UserId,
|
||||
Slot = player.Slot,
|
||||
Index = (int)player.Index,
|
||||
SteamId = player?.SteamID.ToString(),
|
||||
Name = player?.PlayerName,
|
||||
IpAddress = player?.IpAddress?.Split(":")[0]
|
||||
};
|
||||
g_knifePickupCount[(int)player.Slot] = 0;
|
||||
gPlayerWeaponsInfo.TryRemove((int)player.Slot, out _);
|
||||
g_playersKnife.TryRemove((int)player.Slot, out _);
|
||||
|
||||
_ = Task.Run(async () =>
|
||||
PlayerInfo playerInfo = new PlayerInfo(
|
||||
(int)player.Slot,
|
||||
player.Slot,
|
||||
player.UserId,
|
||||
player?.SteamID.ToString(),
|
||||
player?.PlayerName,
|
||||
player?.IpAddress?.Split(":")[0]);
|
||||
|
||||
if (Config.Additional.SkinEnabled)
|
||||
{
|
||||
if (WeaponSync != null) await WeaponSync.GetPlayerData(playerInfo);
|
||||
});
|
||||
Task.Run(() => weaponSync.GetWeaponPaintsFromDatabase(playerInfo));
|
||||
}
|
||||
if (Config.Additional.KnifeEnabled)
|
||||
{
|
||||
Task.Run(() => weaponSync.GetKnifeFromDatabase(playerInfo));
|
||||
}
|
||||
if (Config.Additional.GloveEnabled)
|
||||
{
|
||||
Task.Run(() => weaponSync.GetGloveFromDatabase(playerInfo));
|
||||
}
|
||||
}
|
||||
|
||||
AddTimer(2.0f, () => OnAllPluginsLoaded(hotReload));
|
||||
}
|
||||
|
||||
Utility.LoadSkinsFromFile(ModuleDirectory + $"/data/skins_{_config.SkinsLanguage}.json", Logger);
|
||||
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);
|
||||
Utility.LoadSkinsFromFile(ModuleDirectory + "/skins.json");
|
||||
Utility.LoadGlovesFromFile(ModuleDirectory + "/gloves.json");
|
||||
|
||||
if (Config.Additional.KnifeEnabled)
|
||||
SetupKnifeMenu();
|
||||
@@ -68,34 +213,27 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
|
||||
SetupSkinsMenu();
|
||||
if (Config.Additional.GloveEnabled)
|
||||
SetupGlovesMenu();
|
||||
if (Config.Additional.AgentEnabled)
|
||||
SetupAgentsMenu();
|
||||
if (Config.Additional.MusicEnabled)
|
||||
SetupMusicMenu();
|
||||
|
||||
RegisterListeners();
|
||||
RegisterCommands();
|
||||
}
|
||||
public static void PlayerGiveNamedItem(CCSPlayerController player, string item)
|
||||
{
|
||||
if (!player.PlayerPawn.IsValid) return;
|
||||
if (player.PlayerPawn.Value == null) return;
|
||||
if (!player.PlayerPawn.Value.IsValid) return;
|
||||
if (player.PlayerPawn.Value.ItemServices == null) return;
|
||||
|
||||
public void OnConfigParsed(WeaponPaintsConfig config)
|
||||
GiveNamedItem2.Invoke(player.PlayerPawn.Value.ItemServices.Handle, item, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
public void OnConfigParsed(WeaponPaintsConfig config)
|
||||
{
|
||||
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;
|
||||
Logger.LogError("You need to setup Database credentials in config!");
|
||||
throw new Exception("[WeaponPaints] You need to setup Database credentials in config!");
|
||||
}
|
||||
|
||||
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,
|
||||
@@ -103,13 +241,15 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
|
||||
Password = config.DatabasePassword,
|
||||
Database = config.DatabaseName,
|
||||
Port = (uint)config.DatabasePort,
|
||||
Pooling = true,
|
||||
MaximumPoolSize = 640,
|
||||
Pooling = true
|
||||
};
|
||||
|
||||
Database = new Database(builder.ConnectionString);
|
||||
_database = new(builder.ConnectionString);
|
||||
|
||||
_ = Utility.CheckDatabaseTables();
|
||||
|
||||
Config = config;
|
||||
_config = config;
|
||||
_localizer = Localizer;
|
||||
|
||||
Utility.Config = config;
|
||||
@@ -117,17 +257,8 @@ public partial class WeaponPaints : BasePlugin, IPluginConfig<WeaponPaintsConfig
|
||||
Task.Run(async () => await Utility.CheckVersion(ModuleVersion, Logger));
|
||||
}
|
||||
|
||||
public override void OnAllPluginsLoaded(bool hotReload)
|
||||
public override void Unload(bool hotReload)
|
||||
{
|
||||
try
|
||||
{
|
||||
MenuApi = MenuCapability.Get();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
MenuApi = null;
|
||||
Logger.LogError("Error while loading required plugins");
|
||||
throw;
|
||||
}
|
||||
base.Unload(hotReload);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
@@ -9,23 +9,13 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.281" />
|
||||
<PackageReference Include="Dapper" Version="2.1.35" />
|
||||
<PackageReference Include="MySqlConnector" Version="2.4.0-beta.1" />
|
||||
<PackageReference Include="CounterStrikeSharp.API" Version="1.0.172" />
|
||||
<PackageReference Include="Dapper" Version="2.1.28" />
|
||||
<PackageReference Include="MySqlConnector" Version="2.3.5" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="lang\**\*.*" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="gamedata\*.*" CopyToOutputDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Reference Include="MenuManagerApi">
|
||||
<HintPath>3rd_party\MenuManagerApi.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
</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,8 +1,6 @@
|
||||
using Dapper;
|
||||
using MySqlConnector;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
|
||||
namespace WeaponPaints
|
||||
{
|
||||
internal class WeaponSynchronization
|
||||
@@ -16,257 +14,104 @@ namespace WeaponPaints
|
||||
_config = config;
|
||||
}
|
||||
|
||||
internal async Task GetPlayerData(PlayerInfo? player)
|
||||
internal async Task GetKnifeFromDatabase(PlayerInfo player)
|
||||
{
|
||||
if (!_config.Additional.KnifeEnabled) return;
|
||||
try
|
||||
{
|
||||
await using var connection = await _database.GetConnectionAsync();
|
||||
|
||||
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);
|
||||
if (_config.Additional.PinsEnabled)
|
||||
GetPinsFromDatabase(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 });
|
||||
string query = "SELECT `knife` FROM `wp_player_knife` WHERE `steamid` = @steamid";
|
||||
string? playerKnife = await connection.QueryFirstOrDefaultAsync<string>(query, new { steamid = player.SteamId });
|
||||
|
||||
if (!string.IsNullOrEmpty(playerKnife))
|
||||
{
|
||||
WeaponPaints.GPlayersKnife[player.Slot] = playerKnife;
|
||||
WeaponPaints.g_playersKnife[player.Slot] = playerKnife;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception e)
|
||||
{
|
||||
Utility.Log($"An error occurred in GetKnifeFromDatabase: {ex.Message}");
|
||||
Utility.Log(e.Message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void GetGloveFromDatabase(PlayerInfo? player, MySqlConnection connection)
|
||||
internal async Task GetGloveFromDatabase(PlayerInfo player)
|
||||
{
|
||||
if (!_config.Additional.GloveEnabled) return;
|
||||
|
||||
try
|
||||
{
|
||||
if (!_config.Additional.GloveEnabled || string.IsNullOrEmpty(player?.SteamId))
|
||||
return;
|
||||
// Ensure proper disposal of resources using "using" statement
|
||||
await using var connection = await _database.GetConnectionAsync();
|
||||
|
||||
const string query = "SELECT `weapon_defindex` FROM `wp_player_gloves` WHERE `steamid` = @steamid";
|
||||
var gloveData = connection.QueryFirstOrDefault<ushort?>(query, new { steamid = player.SteamId });
|
||||
// Construct the SQL query with specific columns for better performance
|
||||
string query = "SELECT `weapon_defindex` FROM `wp_player_gloves` WHERE `steamid` = @steamid";
|
||||
|
||||
// Execute the query and retrieve glove data
|
||||
ushort? gloveData = await connection.QueryFirstOrDefaultAsync<ushort?>(query, new { steamid = player.SteamId });
|
||||
|
||||
// Check if glove data is retrieved successfully
|
||||
if (gloveData != null)
|
||||
{
|
||||
WeaponPaints.GPlayersGlove[player.Slot] = gloveData.Value;
|
||||
// Update g_playersGlove dictionary with glove data
|
||||
WeaponPaints.g_playersGlove[player.Slot] = gloveData.Value;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception e)
|
||||
{
|
||||
Utility.Log($"An error occurred in GetGloveFromDatabase: {ex.Message}");
|
||||
// Log any exceptions occurred during database operation
|
||||
Utility.Log("An error occurred while fetching glove data: " + e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private void GetAgentFromDatabase(PlayerInfo? player, MySqlConnection connection)
|
||||
internal async Task GetWeaponPaintsFromDatabase(PlayerInfo player)
|
||||
{
|
||||
if (!_config.Additional.SkinEnabled || player == null || player.SteamId == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
if (!_config.Additional.AgentEnabled || string.IsNullOrEmpty(player?.SteamId))
|
||||
return;
|
||||
await using var connection = await _database.GetConnectionAsync();
|
||||
string query = "SELECT `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed` FROM `wp_player_skins` WHERE `steamid` = @steamid";
|
||||
|
||||
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 });
|
||||
var playerSkins = await connection.QueryAsync<dynamic>(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;
|
||||
if (playerSkins == null) 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
|
||||
keyChainInfo.Id = keyChainId;
|
||||
keyChainInfo.OffsetX = keyChainOffsetX;
|
||||
keyChainInfo.OffsetY = keyChainOffsetY;
|
||||
keyChainInfo.OffsetZ = keyChainOffsetZ;
|
||||
keyChainInfo.Seed = keyChainSeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Failed to parse the values, default to 0
|
||||
keyChainInfo.Id = 0;
|
||||
keyChainInfo.OffsetX = 0f;
|
||||
keyChainInfo.OffsetY = 0f;
|
||||
keyChainInfo.OffsetZ = 0f;
|
||||
keyChainInfo.Seed = 0;
|
||||
}
|
||||
|
||||
// Create the WeaponInfo object
|
||||
WeaponInfo weaponInfo = new WeaponInfo
|
||||
{
|
||||
Paint = weaponPaintId,
|
||||
Seed = weaponSeed,
|
||||
Wear = weaponWear,
|
||||
Nametag = weaponNameTag,
|
||||
KeyChain = keyChainInfo
|
||||
Wear = weaponWear
|
||||
};
|
||||
|
||||
// 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;
|
||||
WeaponPaints.gPlayerWeaponsInfo[player.Slot] = weaponInfos;
|
||||
}
|
||||
catch (Exception ex)
|
||||
catch (Exception e)
|
||||
{
|
||||
Utility.Log($"An error occurred in GetWeaponPaintsFromDatabase: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GetMusicFromDatabase(PlayerInfo? player, MySqlConnection connection)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_config.Additional.MusicEnabled || string.IsNullOrEmpty(player?.SteamId))
|
||||
return;
|
||||
|
||||
const string query = "SELECT `music_id` FROM `wp_player_music` WHERE `steamid` = @steamid";
|
||||
var musicData = connection.QueryFirstOrDefault<ushort?>(query, new { steamid = player.SteamId });
|
||||
|
||||
if (musicData != null)
|
||||
{
|
||||
WeaponPaints.GPlayersMusic[player.Slot] = musicData.Value;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Utility.Log($"An error occurred in GetMusicFromDatabase: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void GetPinsFromDatabase(PlayerInfo? player, MySqlConnection connection)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(player?.SteamId))
|
||||
return;
|
||||
|
||||
const string query = "SELECT `id` FROM `wp_player_pins` WHERE `steamid` = @steamid";
|
||||
var pinData = connection.QueryFirstOrDefault<ushort?>(query, new { steamid = player.SteamId });
|
||||
|
||||
if (pinData != null)
|
||||
{
|
||||
WeaponPaints.GPlayersPin[player.Slot] = pinData.Value;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Utility.Log($"An error occurred in GetPinsFromDatabase: {ex.Message}");
|
||||
Utility.Log($"Database error occurred: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task SyncKnifeToDatabase(PlayerInfo player, string knife)
|
||||
{
|
||||
if (!_config.Additional.KnifeEnabled || string.IsNullOrEmpty(player.SteamId) || string.IsNullOrEmpty(knife)) return;
|
||||
|
||||
const string query = "INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(@steamid, @newKnife) ON DUPLICATE KEY UPDATE `knife` = @newKnife";
|
||||
if (!_config.Additional.KnifeEnabled || player == null || string.IsNullOrEmpty(player.SteamId) || string.IsNullOrEmpty(knife)) return;
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await _database.GetConnectionAsync();
|
||||
string query = "INSERT INTO `wp_player_knife` (`steamid`, `knife`) VALUES(@steamid, @newKnife) ON DUPLICATE KEY UPDATE `knife` = @newKnife";
|
||||
await connection.ExecuteAsync(query, new { steamid = player.SteamId, newKnife = knife });
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -275,14 +120,15 @@ namespace WeaponPaints
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal async Task SyncGloveToDatabase(PlayerInfo player, int defindex)
|
||||
{
|
||||
if (!_config.Additional.GloveEnabled || string.IsNullOrEmpty(player.SteamId)) return;
|
||||
if (!_config.Additional.GloveEnabled || player == null || string.IsNullOrEmpty(player.SteamId)) return;
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await _database.GetConnectionAsync();
|
||||
const string query = "INSERT INTO `wp_player_gloves` (`steamid`, `weapon_defindex`) VALUES(@steamid, @weapon_defindex) ON DUPLICATE KEY UPDATE `weapon_defindex` = @weapon_defindex";
|
||||
string query = "INSERT INTO `wp_player_gloves` (`steamid`, `weapon_defindex`) VALUES(@steamid, @weapon_defindex) ON DUPLICATE KEY UPDATE `weapon_defindex` = @weapon_defindex";
|
||||
await connection.ExecuteAsync(query, new { steamid = player.SteamId, weapon_defindex = defindex });
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -291,129 +137,44 @@ namespace WeaponPaints
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task SyncAgentToDatabase(PlayerInfo player)
|
||||
{
|
||||
if (!_config.Additional.AgentEnabled || string.IsNullOrEmpty(player.SteamId)) return;
|
||||
|
||||
const string query = """
|
||||
INSERT INTO `wp_player_agents` (`steamid`, `agent_ct`, `agent_t`)
|
||||
VALUES(@steamid, @agent_ct, @agent_t)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
`agent_ct` = @agent_ct,
|
||||
`agent_t` = @agent_t
|
||||
""";
|
||||
try
|
||||
{
|
||||
await using var connection = await _database.GetConnectionAsync();
|
||||
|
||||
await connection.ExecuteAsync(query, new { steamid = player.SteamId, agent_ct = WeaponPaints.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))
|
||||
if (player == null || string.IsNullOrEmpty(player.SteamId) || !WeaponPaints.gPlayerWeaponsInfo.TryGetValue(player.Slot, out var weaponsInfo))
|
||||
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;
|
||||
foreach (var weaponInfoPair in weaponsInfo)
|
||||
{
|
||||
int weaponDefIndex = weaponInfoPair.Key;
|
||||
WeaponInfo weaponInfo = weaponInfoPair.Value;
|
||||
|
||||
const string queryCheckExistence = "SELECT COUNT(*) FROM `wp_player_skins` WHERE `steamid` = @steamid AND `weapon_defindex` = @weaponDefIndex";
|
||||
int paintId = weaponInfo.Paint;
|
||||
float wear = weaponInfo.Wear;
|
||||
int seed = weaponInfo.Seed;
|
||||
|
||||
var existingRecordCount = await connection.ExecuteScalarAsync<int>(queryCheckExistence, new { steamid = player.SteamId, weaponDefIndex = weaponDefIndex });
|
||||
string query = "INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, `weapon_paint_id`, `weapon_wear`, `weapon_seed`) " +
|
||||
"VALUES (@steamid, @weaponDefIndex, @paintId, @wear, @seed) " +
|
||||
"ON DUPLICATE KEY UPDATE `weapon_paint_id` = @paintId, `weapon_wear` = @wear, `weapon_seed` = @seed";
|
||||
|
||||
string query;
|
||||
object parameters;
|
||||
var parameters = new DynamicParameters();
|
||||
parameters.Add("@steamid", player.SteamId);
|
||||
parameters.Add("@weaponDefIndex", weaponDefIndex);
|
||||
parameters.Add("@paintId", paintId);
|
||||
parameters.Add("@wear", wear);
|
||||
parameters.Add("@seed", seed);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
await connection.ExecuteAsync(query, parameters);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utility.Log($"Error syncing weapon paints to database: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task SyncMusicToDatabase(PlayerInfo player, ushort music)
|
||||
{
|
||||
if (!_config.Additional.MusicEnabled || string.IsNullOrEmpty(player.SteamId)) return;
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await _database.GetConnectionAsync();
|
||||
const string query = "INSERT INTO `wp_player_music` (`steamid`, `music_id`) VALUES(@steamid, @newMusic) ON DUPLICATE KEY UPDATE `music_id` = @newMusic";
|
||||
await connection.ExecuteAsync(query, new { steamid = player.SteamId, newMusic = music });
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utility.Log($"Error syncing music kit to database: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task SyncStatTrakToDatabase(PlayerInfo player, ConcurrentDictionary<int,WeaponInfo> weaponInfos)
|
||||
{
|
||||
if (WeaponPaints.WeaponSync == null || weaponInfos.IsEmpty) return;
|
||||
|
||||
var statTrakWeapons = weaponInfos
|
||||
.Where(w => w.Value is { StatTrak: true, StatTrakCount: > 0 })
|
||||
.ToDictionary(w => w.Key, w => w.Value.StatTrakCount);
|
||||
|
||||
if (statTrakWeapons.Count == 0) return;
|
||||
|
||||
if (string.IsNullOrEmpty(player.SteamId))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
await using var connection = await _database.GetConnectionAsync();
|
||||
await using var transaction = await connection.BeginTransactionAsync();
|
||||
|
||||
foreach (var (defindex, statTrakCount) in statTrakWeapons)
|
||||
{
|
||||
const string query = @"
|
||||
INSERT INTO `wp_player_skins` (`steamid`, `weapon_defindex`, `weapon_stattrak_count`)
|
||||
VALUES (@steamid, @weaponDefIndex, @StatTrakCount)
|
||||
ON DUPLICATE KEY UPDATE `weapon_stattrak_count` = @StatTrakCount";
|
||||
|
||||
var parameters = new
|
||||
{
|
||||
steamid = player.SteamId,
|
||||
weaponDefIndex = defindex,
|
||||
StatTrakCount = statTrakCount
|
||||
};
|
||||
|
||||
await connection.ExecuteAsync(query, parameters, transaction);
|
||||
}
|
||||
|
||||
await transaction.CommitAsync();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utility.Log($"Error syncing stattrak to database: {e.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
gamedata/gloves.json
Normal file
16
gamedata/gloves.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"CAttributeList_SetOrAddAttributeValueByName": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x40\\x53\\x41\\x56\\x41\\x57\\x48\\x81\\xEC\\x90\\x00\\x00\\x00\\x0F\\x29\\x74\\x24\\x70",
|
||||
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x57\\x41\\x56\\x49\\x89\\xFE\\x41\\x55\\x41\\x54\\x49\\x89\\xF4\\x53\\x48\\x83\\xEC\\x78"
|
||||
}
|
||||
},
|
||||
"CBaseModelEntity_SetBodygroup": {
|
||||
"signatures": {
|
||||
"library": "server",
|
||||
"windows": "\\x48\\x89\\x5C\\x24\\x08\\x48\\x89\\x74\\x24\\x10\\x57\\x48\\x83\\xEC\\x20\\x41\\x8B\\xF8\\x48\\x8B\\xF2\\x48\\x8B\\xD9\\xE8\\x2A\\x2A\\x2A\\x2A",
|
||||
"linux": "\\x55\\x48\\x89\\xE5\\x41\\x56\\x49\\x89\\xF6\\x41\\x55\\x41\\x89\\xD5\\x41\\x54\\x49\\x89\\xFC\\x48\\x83\\xEC\\x08"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
10
lang/en.json
10
lang/en.json
@@ -4,8 +4,6 @@
|
||||
"wp_info_refresh": "Type {lime}!wp{default} to synchronize chosen skins",
|
||||
"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_refresh_done": "{lime}Refreshing weapon paints",
|
||||
"wp_knife_menu_select": "You have chosen {lime}{0}{default} as your knife",
|
||||
@@ -13,13 +11,7 @@
|
||||
"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_skin_title": "Select skin for {lime}{0}{default}",
|
||||
"wp_skin_menu_select": "You have chosen {lime}{0}{default} as your skin",
|
||||
|
||||
"None": "None"
|
||||
"wp_skin_menu_select": "You have chosen {lime}{0}{default} as your skin"
|
||||
}
|
||||
24
lang/lv.json
24
lang/lv.json
@@ -1,25 +1,17 @@
|
||||
{
|
||||
"wp_prefix": "{lightblue}[WeaponPaints] {default}",
|
||||
"wp_info_website": "Apmeklē {lime}{0}{default}, kur varat mainīt ādas",
|
||||
"wp_info_website": "Apmeklējiet {lime}{0}{default}, kur varat mainīt ādas",
|
||||
"wp_info_refresh": "Ievadiet {lime}!wp{default}, lai sinhronizētu izvēlētās ādas",
|
||||
"wp_info_knife": "Ievadiet {lime}!knife{default}, lai atvērtu nazis izvēlni",
|
||||
"wp_info_glove": "Ievadiet {lime}!gloves{default}, lai atvērtu cimdi izvēlni",
|
||||
"wp_info_agent": "Ievadiet {lime}!agents{default}, lai atvērtu aģentu izvēlni",
|
||||
"wp_info_music": "Ievadiet {lime}!music{default}, lai atvērtu mūzikas izvēlni",
|
||||
"wp_command_cooldown": "{lightred}Šobrīd nevarat atsvaidzināt ieroča krāsas",
|
||||
"wp_command_refresh_done": "{lime}Atsvaidzinot ieroča krāsas",
|
||||
"wp_command_cooldown": "{lightred}Šobrīd jūs nevarat atjaunot ieroču ādas",
|
||||
"wp_command_refresh_done": "{lime}Atjauno ieroču ādas",
|
||||
"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_knife_menu_title": "Nazis Izvēlne",
|
||||
"wp_glove_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savus cimdus",
|
||||
"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_skin_title": "Izvēlieties ādu priekš {lime}{0}{default}",
|
||||
"wp_skin_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu ādu",
|
||||
|
||||
"None": "Nav"
|
||||
}
|
||||
"wp_skin_menu_skin_title": "Izvēlieties ādu {lime}{0}{default}",
|
||||
"wp_skin_menu_select": "Jūs esat izvēlējies {lime}{0}{default} kā savu ādu"
|
||||
}
|
||||
|
||||
22
lang/pl.json
22
lang/pl.json
@@ -3,23 +3,15 @@
|
||||
"wp_info_website": "Odwiedź {lime}{0}{default}, gdzie możesz zmieniać skórki",
|
||||
"wp_info_refresh": "Wpisz {lime}!wp{default}, aby zsynchronizować wybrane skórki",
|
||||
"wp_info_knife": "Wpisz {lime}!knife{default}, aby otworzyć menu noży",
|
||||
"wp_info_glove": "Wpisz {lime}!gloves{default}, aby otworzyć menu rękawic",
|
||||
"wp_info_agent": "Wpisz {lime}!agents{default}, aby otworzyć menu agentów",
|
||||
"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_info_glove": "Wpisz {lime}!gloves{default}, aby otworzyć menu rękawiczek",
|
||||
"wp_command_cooldown": "{lightred}Nie możesz teraz odświeżyć skórek broni",
|
||||
"wp_command_refresh_done": "{lime}Odświeżanie skórek broni",
|
||||
"wp_knife_menu_select": "Wybrałeś {lime}{0}{default} jako swój nóż",
|
||||
"wp_knife_menu_kill": "",
|
||||
"wp_knife_menu_kill": "{lime}Wybrane skiny będą ustawione dopiero po ponownym wejściu na serwer lub wpisaniu komendy {orange}!kill",
|
||||
"wp_knife_menu_title": "Menu Noży",
|
||||
"wp_glove_menu_select": "Wybrałeś {lime}{0}{default} jako swoją rękawiczkę",
|
||||
"wp_glove_menu_select": "Wybrałeś {lime}{0}{default} jako swoje rękawiczki",
|
||||
"wp_glove_menu_title": "Menu Rękawiczek",
|
||||
"wp_agent_menu_select": "Wybrałeś {lime}{0}{default} jako swojego agenta",
|
||||
"wp_agent_menu_title": "Menu Agentów",
|
||||
"wp_music_menu_title": "Menu Muzyczne",
|
||||
"wp_music_menu_select": "Wybrałeś {lime}{0}{default} jako swój zestaw muzyczny",
|
||||
"wp_skin_menu_weapon_title": "Menu Broni",
|
||||
"wp_skin_menu_skin_title": "Wybierz skórkę dla {lime}{0}{default}",
|
||||
"wp_skin_menu_select": "Wybrałeś {lime}{0}{default} jako swoją skórkę",
|
||||
|
||||
"None": "Brak"
|
||||
}
|
||||
"wp_skin_menu_select": "Wybrałeś {lime}{0}{default} jako swoją skórkę"
|
||||
}
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
{
|
||||
"wp_prefix": "{lightblue}[WeaponPaints] {default}",
|
||||
"wp_info_website": "Visite {lime}{0}{default}, onde você pode alterar as skins",
|
||||
"wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins escolhidas",
|
||||
"wp_info_website": "Visite {lime}{0}{default}, onde você pode alterar skins",
|
||||
"wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins selecionadas",
|
||||
"wp_info_knife": "Digite {lime}!knife{default} para abrir o menu de facas",
|
||||
"wp_info_glove": "Digite {lime}!gloves{default} para abrir o menu de luvas",
|
||||
"wp_info_agent": "Digite {lime}!agents{default} para abrir o menu de agentes",
|
||||
"wp_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_command_cooldown": "{lightred}Você não pode atualizar as skins de arma agora",
|
||||
"wp_command_refresh_done": "{lime}Atualizando as skins de arma",
|
||||
"wp_knife_menu_select": "Você escolheu {lime}{0}{default} como sua faca",
|
||||
"wp_knife_menu_kill": "",
|
||||
"wp_knife_menu_title": "Menu de Facas",
|
||||
"wp_glove_menu_select": "Você escolheu {lime}{0}{default} como sua luva",
|
||||
"wp_glove_menu_select": "Você escolheu {lime}{0}{default} como suas luvas",
|
||||
"wp_glove_menu_title": "Menu de Luvas",
|
||||
"wp_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_skin_title": "Selecione a skin para {lime}{0}{default}",
|
||||
"wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin",
|
||||
|
||||
"None": "Nenhum"
|
||||
}
|
||||
"wp_skin_menu_skin_title": "Selecione uma skin para {lime}{0}{default}",
|
||||
"wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin"
|
||||
}
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
{
|
||||
"wp_prefix": "{lightblue}[WeaponPaints] {default}",
|
||||
"wp_info_website": "Visite {lime}{0}{default}, onde pode alterar as skins",
|
||||
"wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins escolhidas",
|
||||
"wp_info_website": "Visite {lime}{0}{default}, onde pode alterar skins",
|
||||
"wp_info_refresh": "Digite {lime}!wp{default} para sincronizar as skins selecionadas",
|
||||
"wp_info_knife": "Digite {lime}!knife{default} para abrir o menu de facas",
|
||||
"wp_info_glove": "Digite {lime}!gloves{default} para abrir o menu de luvas",
|
||||
"wp_info_agent": "Digite {lime}!agents{default} para abrir o menu de agentes",
|
||||
"wp_info_music": "Digite {lime}!music{default} para abrir o menu de música",
|
||||
"wp_command_cooldown": "{lightred}Não pode atualizar as skins de armas de momento",
|
||||
"wp_command_refresh_done": "{lime}Atualizando as skins de armas",
|
||||
"wp_knife_menu_select": "Escolheu {lime}{0}{default} como a sua faca",
|
||||
"wp_command_cooldown": "{lightred}Você não pode atualizar as skins de arma agora",
|
||||
"wp_command_refresh_done": "{lime}Atualizando as skins de arma",
|
||||
"wp_knife_menu_select": "Você escolheu {lime}{0}{default} como 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_select": "Você escolheu {lime}{0}{default} como suas luvas",
|
||||
"wp_glove_menu_title": "Menu de Luvas",
|
||||
"wp_agent_menu_select": "Escolheu {lime}{0}{default} como o seu agente",
|
||||
"wp_agent_menu_title": "Menu de Agentes",
|
||||
"wp_music_menu_title": "Menu de Música",
|
||||
"wp_music_menu_select": "Escolheu {lime}{0}{default} como o seu kit de música",
|
||||
"wp_skin_menu_weapon_title": "Menu de Armas",
|
||||
"wp_skin_menu_skin_title": "Selecione a skin para {lime}{0}{default}",
|
||||
"wp_skin_menu_select": "Escolheu {lime}{0}{default} como a sua skin",
|
||||
|
||||
"None": "Nenhum"
|
||||
}
|
||||
"wp_skin_menu_skin_title": "Selecione uma skin para {lime}{0}{default}",
|
||||
"wp_skin_menu_select": "Você escolheu {lime}{0}{default} como sua skin"
|
||||
}
|
||||
|
||||
14
lang/ru.json
14
lang/ru.json
@@ -4,22 +4,14 @@
|
||||
"wp_info_refresh": "Введите {lime}!wp{default}, чтобы синхронизировать выбранные скины",
|
||||
"wp_info_knife": "Введите {lime}!knife{default}, чтобы открыть меню ножей",
|
||||
"wp_info_glove": "Введите {lime}!gloves{default}, чтобы открыть меню перчаток",
|
||||
"wp_info_agent": "Введите {lime}!agents{default}, чтобы открыть меню агентов",
|
||||
"wp_info_music": "Введите {lime}!music{default}, чтобы открыть меню музыки",
|
||||
"wp_command_cooldown": "{lightred}Вы не можете обновить раскраску оружия сейчас",
|
||||
"wp_command_refresh_done": "{lime}Обновление раскраски оружия",
|
||||
"wp_command_cooldown": "{lightred}Вы не можете обновить скины оружия сейчас",
|
||||
"wp_command_refresh_done": "{lime}Обновление скинов оружия",
|
||||
"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_skin_title": "Выберите скин для {lime}{0}{default}",
|
||||
"wp_skin_menu_select": "Вы выбрали {lime}{0}{default} в качестве вашего скина",
|
||||
|
||||
"None": "Нет"
|
||||
"wp_skin_menu_select": "Вы выбрали {lime}{0}{default} в качестве вашего скина"
|
||||
}
|
||||
24
lang/tr.json
24
lang/tr.json
@@ -1,25 +1,17 @@
|
||||
{
|
||||
"wp_prefix": "{lightblue}[WeaponPaints] {default}",
|
||||
"wp_info_website": "Ziyaret edin {lime}{0}{default}, nerede derileri değiştirebilirsiniz",
|
||||
"wp_info_refresh": "Senkronize etmek için {lime}!wp{default} yazın seçilen deriler",
|
||||
"wp_info_website": "Ziyaret edin {lime}{0}{default}, burada skinleri değiştirebilirsiniz",
|
||||
"wp_info_refresh": "Senkronize edilen skinleri görmek için {lime}!wp{default} yazın",
|
||||
"wp_info_knife": "Bıçak menüsünü açmak için {lime}!knife{default} yazın",
|
||||
"wp_info_glove": "Handskar menüsünü açmak için {lime}!gloves{default} yazın",
|
||||
"wp_info_agent": "Ajan menüsünü açmak için {lime}!agents{default} yazın",
|
||||
"wp_info_music": "Müzik menüsünü açmak için {lime}!music{default} yazın",
|
||||
"wp_command_cooldown": "{lightred}Şu anda silah boyalarını yenileyemezsiniz",
|
||||
"wp_command_refresh_done": "{lime}Silah boyaları yenileniyor",
|
||||
"wp_info_glove": "Eldiven menüsünü açmak için {lime}!gloves{default} yazın",
|
||||
"wp_command_cooldown": "{lightred}Şu anda silah skinlerini yenileyemezsiniz",
|
||||
"wp_command_refresh_done": "{lime}Silah skinleri 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_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_skin_title": "{lime}{0}{default} için cilt seçin",
|
||||
"wp_skin_menu_select": "{lime}{0}{default} olarak cildinizi seçtiniz",
|
||||
|
||||
"None": "Hiçbiri"
|
||||
}
|
||||
"wp_skin_menu_skin_title": "{lime}{0}{default} için bir skin seçin",
|
||||
"wp_skin_menu_select": "{lime}{0}{default} olarak bir skin seçtiniz"
|
||||
}
|
||||
|
||||
18
lang/ua.json
18
lang/ua.json
@@ -4,22 +4,14 @@
|
||||
"wp_info_refresh": "Введіть {lime}!wp{default}, щоб синхронізувати обрані шкури",
|
||||
"wp_info_knife": "Введіть {lime}!knife{default}, щоб відкрити меню ножів",
|
||||
"wp_info_glove": "Введіть {lime}!gloves{default}, щоб відкрити меню рукавичок",
|
||||
"wp_info_agent": "Введіть {lime}!agents{default}, щоб відкрити меню агентів",
|
||||
"wp_info_music": "Введіть {lime}!music{default}, щоб відкрити меню музики",
|
||||
"wp_command_cooldown": "{lightred}Ви не можете оновити фарби зброї зараз",
|
||||
"wp_command_refresh_done": "{lime}Оновлення фарби зброї",
|
||||
"wp_knife_menu_select": "Ви обрали {lime}{0}{default} як свій ніж",
|
||||
"wp_command_cooldown": "{lightred}Наразі ви не можете оновлювати шкіри зброї",
|
||||
"wp_command_refresh_done": "{lime}Оновлення шкірок зброї",
|
||||
"wp_knife_menu_select": "Ви вибрали {lime}{0}{default} як ваш ніж",
|
||||
"wp_knife_menu_kill": "",
|
||||
"wp_knife_menu_title": "Меню Ножів",
|
||||
"wp_glove_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": "Немає"
|
||||
"wp_skin_menu_select": "Ви вибрали {lime}{0}{default} як вашу шкіру"
|
||||
}
|
||||
@@ -1,25 +1,17 @@
|
||||
{
|
||||
"wp_prefix": "{lightblue}[WeaponPaints] {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_glove": "输入 {lime}!gloves{default} 打开手套菜单",
|
||||
"wp_info_agent": "输入 {lime}!agents{default} 打开代理菜单",
|
||||
"wp_info_music": "输入 {lime}!music{default} 打开音乐菜单",
|
||||
"wp_command_cooldown": "{lightred}您现在无法刷新武器涂装",
|
||||
"wp_command_refresh_done": "{lime}正在刷新武器涂装",
|
||||
"wp_knife_menu_select": "您选择了 {lime}{0}{default} 作为您的刀具",
|
||||
"wp_command_cooldown": "{lightred}您现在无法刷新武器皮肤",
|
||||
"wp_command_refresh_done": "{lime}刷新武器皮肤",
|
||||
"wp_knife_menu_select": "您已选择 {lime}{0}{default} 作为您的刀具",
|
||||
"wp_knife_menu_kill": "",
|
||||
"wp_knife_menu_title": "刀具菜单",
|
||||
"wp_glove_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": "无"
|
||||
}
|
||||
"wp_skin_menu_skin_title": "为 {lime}{0}{default} 选择皮肤",
|
||||
"wp_skin_menu_select": "您已选择 {lime}{0}{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
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
website/data/gloves.json
Normal file
1
website/data/gloves.json
Normal file
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