using CounterStrikeSharp.API.Core; using CounterStrikeSharp.API; using CounterStrikeSharp.API.Modules.Memory.DynamicFunctions; namespace Porning; class SkinsManager { private readonly BasePlugin plugin; private static readonly ulong SpawnedWeaponId = 16384; // Randomly chosen private static readonly MemoryFunctionVoid CAttributeListSetOrAddAttributeValueByName = new(GameData.GetSignature("CAttributeList_SetOrAddAttributeValueByName")); public SkinsManager(BasePlugin plugin) { this.plugin = plugin; plugin.RegisterListener((entity) => { OnEntitySpawned(entity); }); plugin.RegisterListener((entity, parent) => { OnEntityParentChanged(entity, parent); }); plugin.RegisterEventHandler(OnItemEquip); plugin.RegisterEventHandler(OnPlayerSpawn); } public void OnEntitySpawned(CEntityInstance entity) { } public void OnEntityParentChanged(CEntityInstance entity, CEntityInstance parent) { } private HookResult OnPlayerSpawn(EventPlayerSpawn @event, GameEventInfo info) { var player = @event.Userid; if (player == null || !player.IsValid) return HookResult.Continue; // On attend la frame suivante pour s'assurer que le moteur du jeu a fini // d'attribuer le corps (Pawn) et l'équipement par défaut au joueur. Server.NextFrame(() => { if (player.IsValid && player.PlayerPawn.IsValid && player.PawnIsAlive) { ApplySportGlovesPandorasBox(player); ApplyAgentSirBloodyMiamiDarryl(player); } }); return HookResult.Continue; } private HookResult OnItemEquip(EventItemPickup @event, GameEventInfo info) { var player = @event.Userid; if (player == null || !player.IsValid) return HookResult.Continue; Server.NextFrame(() => { if (!player.IsValid || !player.PlayerPawn.IsValid) return; var pawn = player.PlayerPawn.Value; if (pawn == null) return; var myWeapons = pawn.WeaponServices?.MyWeapons; if (myWeapons == null) return; // Reproduction de la méthode GiveOnItemPickup du dépôt GitHub foreach (var handle in myWeapons) { var weapon = handle.Value; if (weapon == null || !weapon.IsValid) continue; if (weapon.DesignerName == "weapon_ak47") { ApplyAK47Skin1171(weapon, player); } else if (weapon.DesignerName.Contains("knife") || weapon.DesignerName.Contains("bayonet")) { ApplyButterflyKnifeFade(weapon, player); } } }); return HookResult.Continue; } public void ApplyAK47Skin1171(CBasePlayerWeapon weapon, CCSPlayerController player) { if (weapon.AttributeManager.Item.ItemDefinitionIndex != 7) return; weapon.AttributeManager.Item.AttributeList.Attributes.RemoveAll(); weapon.AttributeManager.Item.NetworkedDynamicAttributes.Attributes.RemoveAll(); weapon.AttributeManager.Item.ItemID = SpawnedWeaponId; weapon.AttributeManager.Item.ItemIDLow = (uint)(SpawnedWeaponId & 0xFFFFFFFF); weapon.AttributeManager.Item.ItemIDHigh = (uint)(SpawnedWeaponId >> 32); weapon.AttributeManager.Item.AccountID = (uint)player.SteamID; weapon.FallbackPaintKit = 1171; weapon.FallbackSeed = 0; weapon.FallbackWear = 0.01f; var handle = weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle; CAttributeListSetOrAddAttributeValueByName.Invoke(handle, "set item texture prefab", 1171); CAttributeListSetOrAddAttributeValueByName.Invoke(handle, "set item texture seed", 0); CAttributeListSetOrAddAttributeValueByName.Invoke(handle, "set item texture wear", 0.01f); weapon.AcceptInput("SetBodygroup", value: "body,0"); Utilities.SetStateChanged(weapon, "CEconEntity", "m_nFallbackPaintKit"); Utilities.SetStateChanged(weapon, "CEconEntity", "m_nFallbackSeed"); Utilities.SetStateChanged(weapon, "CEconEntity", "m_flFallbackWear"); } public void ApplyButterflyKnifeFade(CBasePlayerWeapon weapon, CCSPlayerController player) { // Vérifier si l'arme est un couteau (pour ne pas transformer un pistolet en couteau) if (!weapon.DesignerName.Contains("knife") && !weapon.DesignerName.Contains("bayonet")) return; ushort butterflyDefIndex = 515; // Changement de la sous-classe pour charger le bon modèle d'animation et de mesh if (weapon.AttributeManager.Item.ItemDefinitionIndex != butterflyDefIndex) { weapon.AcceptInput("ChangeSubclass", value: butterflyDefIndex.ToString()); } weapon.AttributeManager.Item.ItemDefinitionIndex = butterflyDefIndex; weapon.AttributeManager.Item.EntityQuality = 3; // Qualité "Étoile" / rare, obligatoire pour les couteaux weapon.AttributeManager.Item.AttributeList.Attributes.RemoveAll(); weapon.AttributeManager.Item.NetworkedDynamicAttributes.Attributes.RemoveAll(); weapon.AttributeManager.Item.ItemID = SpawnedWeaponId; weapon.AttributeManager.Item.ItemIDLow = (uint)(SpawnedWeaponId & 0xFFFFFFFF); weapon.AttributeManager.Item.ItemIDHigh = (uint)(SpawnedWeaponId >> 32); weapon.AttributeManager.Item.AccountID = (uint)player.SteamID; // Application du skin Fade (38) weapon.FallbackPaintKit = 38; weapon.FallbackSeed = 0; // Pattern 0, modifiable pour des variations de Fade weapon.FallbackWear = 0.01f; // Factory New var handle = weapon.AttributeManager.Item.NetworkedDynamicAttributes.Handle; CAttributeListSetOrAddAttributeValueByName.Invoke(handle, "set item texture prefab", 38); CAttributeListSetOrAddAttributeValueByName.Invoke(handle, "set item texture seed", 0); CAttributeListSetOrAddAttributeValueByName.Invoke(handle, "set item texture wear", 0.01f); weapon.AcceptInput("SetBodygroup", value: "body,0"); Utilities.SetStateChanged(weapon, "CEconEntity", "m_nFallbackPaintKit"); Utilities.SetStateChanged(weapon, "CEconEntity", "m_nFallbackSeed"); Utilities.SetStateChanged(weapon, "CEconEntity", "m_flFallbackWear"); } public void ApplySportGlovesPandorasBox(CCSPlayerController player) { var pawn = player.PlayerPawn.Value; if (pawn == null || !pawn.IsValid) return; CEconItemView item = pawn.EconGloves; item.NetworkedDynamicAttributes.Attributes.RemoveAll(); item.AttributeList.Attributes.RemoveAll(); // 5030 = Sport Gloves item.ItemDefinitionIndex = 5030; item.ItemID = SpawnedWeaponId; item.ItemIDLow = (uint)(SpawnedWeaponId & 0xFFFFFFFF); item.ItemIDHigh = (uint)(SpawnedWeaponId >> 32); item.AccountID = (uint)player.SteamID; // 10037 = Pandora's Box, 0.06f = Factory New (min float pour ce skin) int paintKit = 10037; float wear = 0.06f; CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture prefab", paintKit); CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture seed", 0); CAttributeListSetOrAddAttributeValueByName.Invoke(item.NetworkedDynamicAttributes.Handle, "set item texture wear", wear); CAttributeListSetOrAddAttributeValueByName.Invoke(item.AttributeList.Handle, "set item texture prefab", paintKit); CAttributeListSetOrAddAttributeValueByName.Invoke(item.AttributeList.Handle, "set item texture seed", 0); CAttributeListSetOrAddAttributeValueByName.Invoke(item.AttributeList.Handle, "set item texture wear", wear); item.Initialized = true; // Rafraîchissement visuel pour éviter les superpositions de modèles player.ExecuteClientCommand("lastinv"); Server.NextFrame(() => { if (pawn.IsValid) { pawn.AcceptInput("SetBodygroup", value: "first_or_third_person,0"); plugin.AddTimer(0.2f, () => { if (pawn.IsValid) pawn.AcceptInput("SetBodygroup", value: "first_or_third_person,1"); }); } }); } public void ApplyAgentSirBloodyMiamiDarryl(CCSPlayerController player) { // L'agent est exclusif à l'équipe Terroriste (TeamNum 2) if (player.TeamNum != 2) return; Server.NextFrame(() => { if (player.IsValid && player.PlayerPawn.IsValid && player.PawnIsAlive) { var pawn = player.PlayerPawn.Value; if (pawn != null) { pawn.SetModel("agents/models/tm_professional/tm_professional_varf.vmdl"); } } }); } }