From a21565971a04fe01b825250b8523294dcc374953 Mon Sep 17 00:00:00 2001 From: James Castle Date: Mon, 11 Aug 2025 12:45:44 -0700 Subject: [PATCH 01/10] initial commit --- vMenu/MpPedDataManager.cs | 26 ++- vMenu/menus/MpPedCustomization.cs | 328 +++++++++++++++++++++++++----- vMenu/vMenuClient.csproj | 5 +- 3 files changed, 305 insertions(+), 54 deletions(-) diff --git a/vMenu/MpPedDataManager.cs b/vMenu/MpPedDataManager.cs index fa2d41f79..14e446e30 100644 --- a/vMenu/MpPedDataManager.cs +++ b/vMenu/MpPedDataManager.cs @@ -8,12 +8,32 @@ public class MpPedDataManager : BaseScript { public struct DrawableVariations { - public Dictionary> clothes; + public Dictionary> clothes; // legacy + public Dictionary clothesWithCollection; // new + } + public class CharacterClothingData + { + public int ComponentIndex { get; set; } + public int Drawable { get; set; } + public int Texture { get; set; } + public string Collection { get; set; } + } + public static class ClothingCollections + { + public const string BaseGame = "base"; } public struct PropVariations { - public Dictionary> props; + public Dictionary> props; // legacy + public Dictionary propsWithCollection; // new + } + public class CharacterPropData + { + public int PropIndex { get; set; } + public int Drawable { get; set; } + public int Texture { get; set; } + public string Collection { get; set; } } public struct FaceShapeFeatures @@ -109,6 +129,8 @@ public struct MultiplayerPedData public PedAppearance PedAppearance; public PedTattoos PedTatttoos; public PedFacePaints PedFacePaints; + public Dictionary clothesWithCollection; + public Dictionary propsWithCollection; public bool IsMale; public uint ModelHash; public string SaveName; diff --git a/vMenu/menus/MpPedCustomization.cs b/vMenu/menus/MpPedCustomization.cs index 3396a33ec..835adbd08 100644 --- a/vMenu/menus/MpPedCustomization.cs +++ b/vMenu/menus/MpPedCustomization.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -14,6 +15,8 @@ using static CitizenFX.Core.Native.API; using static vMenuClient.CommonFunctions; using static vMenuClient.MpPedDataManager; +using static vMenuClient.MpPedDataManager.DrawableVariations; +using static vMenuClient.MpPedDataManager.PropVariations; using static vMenuShared.ConfigManager; namespace vMenuClient.menus @@ -145,7 +148,9 @@ private void MakeCreateCharacterMenu(bool male, bool editPed = false) { currentCharacter = new MultiplayerPedData(); currentCharacter.DrawableVariations.clothes = new Dictionary>(); + currentCharacter.DrawableVariations.clothesWithCollection = new Dictionary(); currentCharacter.PropVariations.props = new Dictionary>(); + currentCharacter.PropVariations.propsWithCollection = new Dictionary(); currentCharacter.PedHeadBlendData = Game.PlayerPed.GetHeadBlendData(); currentCharacter.Version = 1; currentCharacter.ModelHash = male ? (uint)GetHashKey("mp_m_freemode_01") : (uint)GetHashKey("mp_f_freemode_01"); @@ -154,7 +159,9 @@ private void MakeCreateCharacterMenu(bool male, bool editPed = false) SetPlayerClothing(); } currentCharacter.DrawableVariations.clothes ??= new Dictionary>(); + currentCharacter.DrawableVariations.clothesWithCollection ??= new Dictionary(); currentCharacter.PropVariations.props ??= new Dictionary>(); + currentCharacter.PropVariations.propsWithCollection ??= new Dictionary(); // Set the facial expression to default in case it doesn't exist yet, or keep the current one if it does. currentCharacter.FacialExpression ??= facial_expressions[0]; @@ -486,14 +493,46 @@ private void MakeCreateCharacterMenu(bool male, bool editPed = false) #region clothing options menu var clothingCategoryNames = new string[12] { "Unused (head)", "Masks", "Unused (hair)", "Upper Body", "Lower Body", "Bags & Parachutes", "Shoes", "Scarfs & Chains", "Shirt & Accessory", "Body Armor & Accessory 2", "Badges & Logos", "Shirt Overlay & Jackets" }; + for (var i = 0; i < 12; i++) { if (i is not 0 and not 2) { - var currentVariationIndex = editPed && currentCharacter.DrawableVariations.clothes.ContainsKey(i) ? currentCharacter.DrawableVariations.clothes[i].Key : GetPedDrawableVariation(Game.PlayerPed.Handle, i); - var currentVariationTextureIndex = editPed && currentCharacter.DrawableVariations.clothes.ContainsKey(i) ? currentCharacter.DrawableVariations.clothes[i].Value : GetPedTextureVariation(Game.PlayerPed.Handle, i); + int currentVariationIndex; + int currentVariationTextureIndex; + + if (editPed) + { + // New format + if (currentCharacter.DrawableVariations.clothesWithCollection != null && currentCharacter.DrawableVariations.clothesWithCollection.TryGetValue(i, out var cloth)) + { + if (cloth.Collection == "base") + currentVariationIndex = cloth.Drawable; // use local index for base + else + currentVariationIndex = GetPedDrawableGlobalIndexFromCollection(Game.PlayerPed.Handle, i, cloth.Collection, cloth.Drawable); + + currentVariationTextureIndex = cloth.Texture; + } + // Legacy fallback + else if (currentCharacter.DrawableVariations.clothes != null && currentCharacter.DrawableVariations.clothes.TryGetValue(i, out var legacyCloth)) + { + currentVariationIndex = legacyCloth.Key; + currentVariationTextureIndex = legacyCloth.Value; + } + else + { + currentVariationIndex = GetPedDrawableVariation(Game.PlayerPed.Handle, i); + currentVariationTextureIndex = GetPedTextureVariation(Game.PlayerPed.Handle, i); + } + } + else + { + currentVariationIndex = GetPedDrawableVariation(Game.PlayerPed.Handle, i); + currentVariationTextureIndex = GetPedTextureVariation(Game.PlayerPed.Handle, i); + } var maxDrawables = GetNumberOfPedDrawableVariations(Game.PlayerPed.Handle, i); + var maxTextures = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, i, currentVariationIndex); var items = new List(); for (var x = 0; x < maxDrawables; x++) @@ -501,8 +540,6 @@ private void MakeCreateCharacterMenu(bool male, bool editPed = false) items.Add($"Drawable #{x} (of {maxDrawables})"); } - var maxTextures = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, i, currentVariationIndex); - var listItem = new MenuListItem(clothingCategoryNames[i], items, currentVariationIndex, $"Select a drawable using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{currentVariationTextureIndex + 1} (of {maxTextures})."); clothesMenu.AddMenuItem(listItem); } @@ -511,26 +548,53 @@ private void MakeCreateCharacterMenu(bool male, bool editPed = false) #region props options menu var propNames = new string[5] { "Hats & Helmets", "Glasses", "Misc Props", "Watches", "Bracelets" }; + for (var x = 0; x < 5; x++) { - var propId = x; - if (x > 2) + var propId = x > 2 ? x + 3 : x; + + int currentProp; + int currentPropTexture; + + if (editPed) { - propId += 3; - } + // New format + if (currentCharacter.PropVariations.propsWithCollection != null && currentCharacter.PropVariations.propsWithCollection.TryGetValue(propId, out var prop)) + { + if (prop.Collection == "base") + currentProp = prop.Drawable; + else + currentProp = GetPedPropGlobalIndexFromCollection(Game.PlayerPed.Handle, propId, prop.Collection, prop.Drawable); - var currentProp = editPed && currentCharacter.PropVariations.props.ContainsKey(propId) ? currentCharacter.PropVariations.props[propId].Key : GetPedPropIndex(Game.PlayerPed.Handle, propId); - var currentPropTexture = editPed && currentCharacter.PropVariations.props.ContainsKey(propId) ? currentCharacter.PropVariations.props[propId].Value : GetPedPropTextureIndex(Game.PlayerPed.Handle, propId); + currentPropTexture = prop.Texture; + } + // Legacy fallback + else if (currentCharacter.PropVariations.props != null && currentCharacter.PropVariations.props.TryGetValue(propId, out var legacyProp)) + { + currentProp = legacyProp.Key; + currentPropTexture = legacyProp.Value; + } + else + { + currentProp = GetPedPropIndex(Game.PlayerPed.Handle, propId); + currentPropTexture = GetPedPropTextureIndex(Game.PlayerPed.Handle, propId); + } + } + else + { + currentProp = GetPedPropIndex(Game.PlayerPed.Handle, propId); + currentPropTexture = GetPedPropTextureIndex(Game.PlayerPed.Handle, propId); + } var propsList = new List(); - for (var i = 0; i < GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propId); i++) + var maxProps = GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propId); + for (var i = 0; i < maxProps; i++) { - propsList.Add($"Prop #{i} (of {GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propId)})"); + propsList.Add($"Prop #{i} (of {maxProps})"); } propsList.Add("No Prop"); - - if (GetPedPropIndex(Game.PlayerPed.Handle, propId) != -1) + if (currentProp != -1) { var maxPropTextures = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propId, currentProp); var propListItem = new MenuListItem($"{propNames[x]}", propsList, currentProp, $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{currentPropTexture + 1} (of {maxPropTextures})."); @@ -538,11 +602,9 @@ private void MakeCreateCharacterMenu(bool male, bool editPed = false) } else { - var propListItem = new MenuListItem($"{propNames[x]}", propsList, currentProp, "Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."); + var propListItem = new MenuListItem($"{propNames[x]}", propsList, 0, "Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."); propsMenu.AddMenuItem(propListItem); } - - } #endregion @@ -721,8 +783,13 @@ private void MakeCreateCharacterMenu(bool male, bool editPed = false) private async Task SavePed() { currentCharacter.PedHeadBlendData = Game.PlayerPed.GetHeadBlendData(); + currentCharacter = ReplacePedDataClothing(currentCharacter); if (isEdidtingPed) { + // Ensure SaveName is set + if (string.IsNullOrEmpty(currentCharacter.SaveName)) + currentCharacter.SaveName = "mp_ped_" + selectedSavedCharacterManageName; + var json = JsonConvert.SerializeObject(currentCharacter); if (StorageManager.SaveJsonData(currentCharacter.SaveName, json, true)) { @@ -761,7 +828,6 @@ private async Task SavePed() } } } - } /// @@ -1295,20 +1361,31 @@ int UnclampMix(float value) #region clothes clothesMenu.OnListIndexChange += (_menu, listItem, oldSelectionIndex, newSelectionIndex, realIndex) => { - var componentIndex = realIndex + 1; + int componentIndex = realIndex + 1; if (realIndex > 0) { componentIndex += 1; } - var textureIndex = GetPedTextureVariation(Game.PlayerPed.Handle, componentIndex); - var newTextureIndex = 0; - SetPedComponentVariation(Game.PlayerPed.Handle, componentIndex, newSelectionIndex, newTextureIndex, 0); - currentCharacter.DrawableVariations.clothes ??= new Dictionary>(); + int newTextureIndex = 0; - var maxTextures = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, newSelectionIndex); + SetPedComponentVariation(Game.PlayerPed.Handle, componentIndex, newSelectionIndex, newTextureIndex, 0); + currentCharacter.DrawableVariations.clothes ??= new Dictionary>(); currentCharacter.DrawableVariations.clothes[componentIndex] = new KeyValuePair(newSelectionIndex, newTextureIndex); + + string collectionName = GetPedDrawableVariationCollectionName(Game.PlayerPed.Handle, componentIndex) ?? "base"; + + currentCharacter.DrawableVariations.clothesWithCollection ??= new(); + currentCharacter.DrawableVariations.clothesWithCollection[componentIndex] = new CharacterClothingData + { + ComponentIndex = componentIndex, + Drawable = GetPedDrawableVariationCollectionLocalIndex(componentIndex, newSelectionIndex), + Texture = newTextureIndex, + Collection = collectionName + }; + + int maxTextures = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, newSelectionIndex); listItem.Description = $"Select a drawable using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; }; @@ -1320,14 +1397,27 @@ int UnclampMix(float value) componentIndex += 1; } - var textureIndex = GetPedTextureVariation(Game.PlayerPed.Handle, componentIndex); - var newTextureIndex = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, listIndex) - 1 < textureIndex + 1 ? 0 : textureIndex + 1; + int textureIndex = GetPedTextureVariation(Game.PlayerPed.Handle, componentIndex); + int newTextureIndex = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, listIndex) - 1 < textureIndex + 1 ? 0 : textureIndex + 1; + SetPedComponentVariation(Game.PlayerPed.Handle, componentIndex, listIndex, newTextureIndex, 0); + currentCharacter.DrawableVariations.clothes ??= new Dictionary>(); + currentCharacter.DrawableVariations.clothes[componentIndex] = new KeyValuePair(listIndex, newTextureIndex); - var maxTextures = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, listIndex); + string collectionName = GetPedDrawableVariationCollectionName(Game.PlayerPed.Handle, componentIndex); + if (string.IsNullOrEmpty(collectionName)) collectionName = "base"; - currentCharacter.DrawableVariations.clothes[componentIndex] = new KeyValuePair(listIndex, newTextureIndex); + currentCharacter.DrawableVariations.clothesWithCollection ??= new Dictionary(); + currentCharacter.DrawableVariations.clothesWithCollection[componentIndex] = new CharacterClothingData + { + ComponentIndex = componentIndex, + Drawable = GetPedDrawableVariationCollectionLocalIndex(componentIndex, listIndex), + Texture = newTextureIndex, + Collection = collectionName + }; + + int maxTextures = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, listIndex); listItem.Description = $"Select a drawable using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; }; #endregion @@ -1335,7 +1425,7 @@ int UnclampMix(float value) #region props propsMenu.OnListIndexChange += (_menu, listItem, oldSelectionIndex, newSelectionIndex, realIndex) => { - var propIndex = realIndex; + int propIndex = realIndex; if (realIndex == 3) { propIndex = 6; @@ -1344,28 +1434,56 @@ int UnclampMix(float value) { propIndex = 7; } + int textureIndex = 0; + + int maxDrawables = GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex); + bool isNoProp = newSelectionIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex); + + currentCharacter.PropVariations.props ??= new Dictionary>(); + currentCharacter.PropVariations.propsWithCollection ??= new Dictionary(); - var textureIndex = 0; - if (newSelectionIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) + if (isNoProp) { SetPedPropIndex(Game.PlayerPed.Handle, propIndex, -1, -1, false); ClearPedProp(Game.PlayerPed.Handle, propIndex); currentCharacter.PropVariations.props ??= new Dictionary>(); currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(-1, -1); - listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."; + currentCharacter.PropVariations.propsWithCollection ??= new Dictionary(); + currentCharacter.PropVariations.propsWithCollection[propIndex] = new CharacterPropData + { + PropIndex = propIndex, + Drawable = -1, + Texture = -1, + Collection = "base" + }; + + listItem.Description = "Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."; } else { SetPedPropIndex(Game.PlayerPed.Handle, propIndex, newSelectionIndex, textureIndex, true); currentCharacter.PropVariations.props ??= new Dictionary>(); currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(newSelectionIndex, textureIndex); + + string collectionName = GetPedCollectionNameFromProp(Game.PlayerPed.Handle, propIndex, newSelectionIndex); + if (string.IsNullOrEmpty(collectionName)) collectionName = "base"; + + currentCharacter.PropVariations.propsWithCollection ??= new Dictionary(); + currentCharacter.PropVariations.propsWithCollection[propIndex] = new CharacterPropData + { + PropIndex = propIndex, + Drawable = GetPedCollectionLocalIndexFromProp(Game.PlayerPed.Handle, propIndex, newSelectionIndex), + Texture = textureIndex, + Collection = collectionName + }; + if (GetPedPropIndex(Game.PlayerPed.Handle, propIndex) == -1) { listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."; } else { - var maxPropTextures = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, newSelectionIndex); + int maxPropTextures = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, newSelectionIndex); listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{textureIndex + 1} (of {maxPropTextures})."; } } @@ -1383,14 +1501,22 @@ int UnclampMix(float value) propIndex = 7; } - var textureIndex = GetPedPropTextureIndex(Game.PlayerPed.Handle, propIndex); - var newTextureIndex = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, listIndex) - 1 < textureIndex + 1 ? 0 : textureIndex + 1; - if (textureIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) + int textureIndex = GetPedPropTextureIndex(Game.PlayerPed.Handle, propIndex); + int newTextureIndex = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, listIndex) - 1 < textureIndex + 1 ? 0 : textureIndex + 1; + if (listIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) { SetPedPropIndex(Game.PlayerPed.Handle, propIndex, -1, -1, false); ClearPedProp(Game.PlayerPed.Handle, propIndex); currentCharacter.PropVariations.props ??= new Dictionary>(); currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(-1, -1); + currentCharacter.PropVariations.propsWithCollection ??= new Dictionary(); + currentCharacter.PropVariations.propsWithCollection[propIndex] = new CharacterPropData + { + PropIndex = propIndex, + Drawable = -1, + Texture = -1, + Collection = "base" + }; listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."; } else @@ -1398,6 +1524,18 @@ int UnclampMix(float value) SetPedPropIndex(Game.PlayerPed.Handle, propIndex, listIndex, newTextureIndex, true); currentCharacter.PropVariations.props ??= new Dictionary>(); currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(listIndex, newTextureIndex); + + string collectionName = GetPedCollectionNameFromProp(Game.PlayerPed.Handle, propIndex, listIndex); + if (string.IsNullOrEmpty(collectionName)) collectionName = "base"; + + currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(listIndex, newTextureIndex); + currentCharacter.PropVariations.propsWithCollection[propIndex] = new CharacterPropData + { + PropIndex = propIndex, + Drawable = GetPedCollectionLocalIndexFromProp(Game.PlayerPed.Handle, propIndex, listIndex), + Texture = newTextureIndex, + Collection = collectionName + }; if (GetPedPropIndex(Game.PlayerPed.Handle, propIndex) == -1) { listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."; @@ -2977,21 +3115,82 @@ private MultiplayerPedData ReplacePedDataClothing(MultiplayerPedData character) { int handle = Game.PlayerPed.Handle; - // Drawables + // Init if null + character.DrawableVariations.clothes ??= new Dictionary>(); + character.DrawableVariations.clothesWithCollection ??= new Dictionary(); + character.PropVariations.props ??= new Dictionary>(); + character.PropVariations.propsWithCollection ??= new Dictionary(); + for (int i = 0; i < 12; i++) { - int drawable = GetPedDrawableVariation(handle, i); int texture = GetPedTextureVariation(handle, i); + string collectionName = GetPedDrawableVariationCollectionName(handle, i); + + int drawable; + if (string.IsNullOrEmpty(collectionName) || collectionName == "base") + { + drawable = GetPedDrawableVariation(handle, i); + collectionName = "base"; + } + else + { + drawable = GetPedDrawableVariationCollectionLocalIndex(handle, i); + } + character.DrawableVariations.clothes[i] = new KeyValuePair(drawable, texture); + character.DrawableVariations.clothesWithCollection[i] = new CharacterClothingData + { + ComponentIndex = i, + Drawable = drawable, + Texture = texture, + Collection = collectionName + }; } for (int i = 0; i < 8; i++) { - int prop = GetPedPropIndex(handle, i); int texture = GetPedPropTextureIndex(handle, i); - character.PropVariations.props[i] = new KeyValuePair(prop, texture); + string collectionName = GetPedCollectionNameFromProp(handle, i, GetPedPropIndex(handle, i)); + + + int drawable; + if (string.IsNullOrEmpty(collectionName) || collectionName == "base") + { + drawable = GetPedPropIndex(handle, i); + collectionName = "base"; + } + else + { + drawable = GetPedCollectionLocalIndexFromProp(handle, i, GetPedPropIndex(handle, i)); + } + + + if (drawable == -1) // no prop at this index + { + character.PropVariations.props[i] = new KeyValuePair(-1, -1); + character.PropVariations.propsWithCollection[i] = new CharacterPropData + { + PropIndex = i, + Drawable = -1, + Texture = -1, + Collection = "base" + }; + continue; + } + + character.PropVariations.props[i] = new KeyValuePair(drawable, texture); + character.PropVariations.propsWithCollection[i] = new CharacterPropData + { + PropIndex = i, + Drawable = drawable, + Texture = texture, + Collection = collectionName + }; } + /*character.clothesWithCollection = character.DrawableVariations.clothesWithCollection; + character.propsWithCollection = character.PropVariations.propsWithCollection;*/ + return character; } @@ -3182,24 +3381,57 @@ internal async Task AppySavedDataToPed(MultiplayerPedData character, int pedHand #endregion #region Clothing Data - if (character.DrawableVariations.clothes != null && character.DrawableVariations.clothes.Count > 0) + // Apply clothing (supports old KeyValuePair and new object structure) + if (character.DrawableVariations.clothesWithCollection != null && character.DrawableVariations.clothesWithCollection?.Count > 0) + { + foreach (var item in character.DrawableVariations.clothesWithCollection.Values) + { + if (item.Collection == "base") + { + SetPedComponentVariation(pedHandle, item.ComponentIndex, item.Drawable, item.Texture, 0); + } + else + { + SetPedCollectionComponentVariation(pedHandle, item.ComponentIndex, item.Collection, item.Drawable, item.Texture, 0); + } + } + } + else if (character.DrawableVariations.clothes != null && character.DrawableVariations.clothes.Count > 0) { - foreach (var cd in character.DrawableVariations.clothes) + foreach (var kvp in character.DrawableVariations.clothes) { - SetPedComponentVariation(pedHandle, cd.Key, cd.Value.Key, cd.Value.Value, 0); + SetPedComponentVariation(pedHandle, kvp.Key, kvp.Value.Key, kvp.Value.Value, 0); } } #endregion #region Props Data - if (character.PropVariations.props != null && character.PropVariations.props.Count > 0) + // Apply props (supports old KeyValuePair and new object structure) + if (character.PropVariations.propsWithCollection != null && character.PropVariations.propsWithCollection.Count > 0) + { + foreach (var item in character.PropVariations.propsWithCollection.Values) + { + if (item.Drawable > -1) + { + if (item.Collection == "base") + { + SetPedPropIndex(pedHandle, item.PropIndex, item.Drawable, item.Texture, true); + } + else + { + SetPedCollectionPropIndex(pedHandle, item.PropIndex, item.Collection, item.Drawable, item.Texture, true); + } + } + } + } + else if (character.PropVariations.props != null && character.PropVariations.props.Count > 0) { - foreach (var cd in character.PropVariations.props) + foreach (var kvp in character.PropVariations.props) { - if (cd.Value.Key > -1) + if (kvp.Value.Key > -1) { - int textureIndex = cd.Value.Value > -1 ? cd.Value.Value : 0; - SetPedPropIndex(pedHandle, cd.Key, cd.Value.Key, textureIndex, true); + int texture = kvp.Value.Value > -1 ? kvp.Value.Value : 0; + SetPedPropIndex(pedHandle, kvp.Key, kvp.Value.Key, texture, true); } } } diff --git a/vMenu/vMenuClient.csproj b/vMenu/vMenuClient.csproj index b2d0084be..c9e0e8f2f 100644 --- a/vMenu/vMenuClient.csproj +++ b/vMenu/vMenuClient.csproj @@ -16,14 +16,11 @@ + - - runtime - - ..\dependencies\shared\Newtonsoft.Json.dll true From ddbc5b2173708b2be444bd1711abaf057697d4f6 Mon Sep 17 00:00:00 2001 From: James Castle Date: Mon, 11 Aug 2025 12:45:44 -0700 Subject: [PATCH 02/10] Save MP Clothes by Collection --- vMenu/MpPedDataManager.cs | 26 ++- vMenu/menus/MpPedCustomization.cs | 328 +++++++++++++++++++++++++----- vMenu/vMenuClient.csproj | 5 +- 3 files changed, 305 insertions(+), 54 deletions(-) diff --git a/vMenu/MpPedDataManager.cs b/vMenu/MpPedDataManager.cs index fa2d41f79..14e446e30 100644 --- a/vMenu/MpPedDataManager.cs +++ b/vMenu/MpPedDataManager.cs @@ -8,12 +8,32 @@ public class MpPedDataManager : BaseScript { public struct DrawableVariations { - public Dictionary> clothes; + public Dictionary> clothes; // legacy + public Dictionary clothesWithCollection; // new + } + public class CharacterClothingData + { + public int ComponentIndex { get; set; } + public int Drawable { get; set; } + public int Texture { get; set; } + public string Collection { get; set; } + } + public static class ClothingCollections + { + public const string BaseGame = "base"; } public struct PropVariations { - public Dictionary> props; + public Dictionary> props; // legacy + public Dictionary propsWithCollection; // new + } + public class CharacterPropData + { + public int PropIndex { get; set; } + public int Drawable { get; set; } + public int Texture { get; set; } + public string Collection { get; set; } } public struct FaceShapeFeatures @@ -109,6 +129,8 @@ public struct MultiplayerPedData public PedAppearance PedAppearance; public PedTattoos PedTatttoos; public PedFacePaints PedFacePaints; + public Dictionary clothesWithCollection; + public Dictionary propsWithCollection; public bool IsMale; public uint ModelHash; public string SaveName; diff --git a/vMenu/menus/MpPedCustomization.cs b/vMenu/menus/MpPedCustomization.cs index 3396a33ec..835adbd08 100644 --- a/vMenu/menus/MpPedCustomization.cs +++ b/vMenu/menus/MpPedCustomization.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; @@ -14,6 +15,8 @@ using static CitizenFX.Core.Native.API; using static vMenuClient.CommonFunctions; using static vMenuClient.MpPedDataManager; +using static vMenuClient.MpPedDataManager.DrawableVariations; +using static vMenuClient.MpPedDataManager.PropVariations; using static vMenuShared.ConfigManager; namespace vMenuClient.menus @@ -145,7 +148,9 @@ private void MakeCreateCharacterMenu(bool male, bool editPed = false) { currentCharacter = new MultiplayerPedData(); currentCharacter.DrawableVariations.clothes = new Dictionary>(); + currentCharacter.DrawableVariations.clothesWithCollection = new Dictionary(); currentCharacter.PropVariations.props = new Dictionary>(); + currentCharacter.PropVariations.propsWithCollection = new Dictionary(); currentCharacter.PedHeadBlendData = Game.PlayerPed.GetHeadBlendData(); currentCharacter.Version = 1; currentCharacter.ModelHash = male ? (uint)GetHashKey("mp_m_freemode_01") : (uint)GetHashKey("mp_f_freemode_01"); @@ -154,7 +159,9 @@ private void MakeCreateCharacterMenu(bool male, bool editPed = false) SetPlayerClothing(); } currentCharacter.DrawableVariations.clothes ??= new Dictionary>(); + currentCharacter.DrawableVariations.clothesWithCollection ??= new Dictionary(); currentCharacter.PropVariations.props ??= new Dictionary>(); + currentCharacter.PropVariations.propsWithCollection ??= new Dictionary(); // Set the facial expression to default in case it doesn't exist yet, or keep the current one if it does. currentCharacter.FacialExpression ??= facial_expressions[0]; @@ -486,14 +493,46 @@ private void MakeCreateCharacterMenu(bool male, bool editPed = false) #region clothing options menu var clothingCategoryNames = new string[12] { "Unused (head)", "Masks", "Unused (hair)", "Upper Body", "Lower Body", "Bags & Parachutes", "Shoes", "Scarfs & Chains", "Shirt & Accessory", "Body Armor & Accessory 2", "Badges & Logos", "Shirt Overlay & Jackets" }; + for (var i = 0; i < 12; i++) { if (i is not 0 and not 2) { - var currentVariationIndex = editPed && currentCharacter.DrawableVariations.clothes.ContainsKey(i) ? currentCharacter.DrawableVariations.clothes[i].Key : GetPedDrawableVariation(Game.PlayerPed.Handle, i); - var currentVariationTextureIndex = editPed && currentCharacter.DrawableVariations.clothes.ContainsKey(i) ? currentCharacter.DrawableVariations.clothes[i].Value : GetPedTextureVariation(Game.PlayerPed.Handle, i); + int currentVariationIndex; + int currentVariationTextureIndex; + + if (editPed) + { + // New format + if (currentCharacter.DrawableVariations.clothesWithCollection != null && currentCharacter.DrawableVariations.clothesWithCollection.TryGetValue(i, out var cloth)) + { + if (cloth.Collection == "base") + currentVariationIndex = cloth.Drawable; // use local index for base + else + currentVariationIndex = GetPedDrawableGlobalIndexFromCollection(Game.PlayerPed.Handle, i, cloth.Collection, cloth.Drawable); + + currentVariationTextureIndex = cloth.Texture; + } + // Legacy fallback + else if (currentCharacter.DrawableVariations.clothes != null && currentCharacter.DrawableVariations.clothes.TryGetValue(i, out var legacyCloth)) + { + currentVariationIndex = legacyCloth.Key; + currentVariationTextureIndex = legacyCloth.Value; + } + else + { + currentVariationIndex = GetPedDrawableVariation(Game.PlayerPed.Handle, i); + currentVariationTextureIndex = GetPedTextureVariation(Game.PlayerPed.Handle, i); + } + } + else + { + currentVariationIndex = GetPedDrawableVariation(Game.PlayerPed.Handle, i); + currentVariationTextureIndex = GetPedTextureVariation(Game.PlayerPed.Handle, i); + } var maxDrawables = GetNumberOfPedDrawableVariations(Game.PlayerPed.Handle, i); + var maxTextures = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, i, currentVariationIndex); var items = new List(); for (var x = 0; x < maxDrawables; x++) @@ -501,8 +540,6 @@ private void MakeCreateCharacterMenu(bool male, bool editPed = false) items.Add($"Drawable #{x} (of {maxDrawables})"); } - var maxTextures = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, i, currentVariationIndex); - var listItem = new MenuListItem(clothingCategoryNames[i], items, currentVariationIndex, $"Select a drawable using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{currentVariationTextureIndex + 1} (of {maxTextures})."); clothesMenu.AddMenuItem(listItem); } @@ -511,26 +548,53 @@ private void MakeCreateCharacterMenu(bool male, bool editPed = false) #region props options menu var propNames = new string[5] { "Hats & Helmets", "Glasses", "Misc Props", "Watches", "Bracelets" }; + for (var x = 0; x < 5; x++) { - var propId = x; - if (x > 2) + var propId = x > 2 ? x + 3 : x; + + int currentProp; + int currentPropTexture; + + if (editPed) { - propId += 3; - } + // New format + if (currentCharacter.PropVariations.propsWithCollection != null && currentCharacter.PropVariations.propsWithCollection.TryGetValue(propId, out var prop)) + { + if (prop.Collection == "base") + currentProp = prop.Drawable; + else + currentProp = GetPedPropGlobalIndexFromCollection(Game.PlayerPed.Handle, propId, prop.Collection, prop.Drawable); - var currentProp = editPed && currentCharacter.PropVariations.props.ContainsKey(propId) ? currentCharacter.PropVariations.props[propId].Key : GetPedPropIndex(Game.PlayerPed.Handle, propId); - var currentPropTexture = editPed && currentCharacter.PropVariations.props.ContainsKey(propId) ? currentCharacter.PropVariations.props[propId].Value : GetPedPropTextureIndex(Game.PlayerPed.Handle, propId); + currentPropTexture = prop.Texture; + } + // Legacy fallback + else if (currentCharacter.PropVariations.props != null && currentCharacter.PropVariations.props.TryGetValue(propId, out var legacyProp)) + { + currentProp = legacyProp.Key; + currentPropTexture = legacyProp.Value; + } + else + { + currentProp = GetPedPropIndex(Game.PlayerPed.Handle, propId); + currentPropTexture = GetPedPropTextureIndex(Game.PlayerPed.Handle, propId); + } + } + else + { + currentProp = GetPedPropIndex(Game.PlayerPed.Handle, propId); + currentPropTexture = GetPedPropTextureIndex(Game.PlayerPed.Handle, propId); + } var propsList = new List(); - for (var i = 0; i < GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propId); i++) + var maxProps = GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propId); + for (var i = 0; i < maxProps; i++) { - propsList.Add($"Prop #{i} (of {GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propId)})"); + propsList.Add($"Prop #{i} (of {maxProps})"); } propsList.Add("No Prop"); - - if (GetPedPropIndex(Game.PlayerPed.Handle, propId) != -1) + if (currentProp != -1) { var maxPropTextures = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propId, currentProp); var propListItem = new MenuListItem($"{propNames[x]}", propsList, currentProp, $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{currentPropTexture + 1} (of {maxPropTextures})."); @@ -538,11 +602,9 @@ private void MakeCreateCharacterMenu(bool male, bool editPed = false) } else { - var propListItem = new MenuListItem($"{propNames[x]}", propsList, currentProp, "Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."); + var propListItem = new MenuListItem($"{propNames[x]}", propsList, 0, "Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."); propsMenu.AddMenuItem(propListItem); } - - } #endregion @@ -721,8 +783,13 @@ private void MakeCreateCharacterMenu(bool male, bool editPed = false) private async Task SavePed() { currentCharacter.PedHeadBlendData = Game.PlayerPed.GetHeadBlendData(); + currentCharacter = ReplacePedDataClothing(currentCharacter); if (isEdidtingPed) { + // Ensure SaveName is set + if (string.IsNullOrEmpty(currentCharacter.SaveName)) + currentCharacter.SaveName = "mp_ped_" + selectedSavedCharacterManageName; + var json = JsonConvert.SerializeObject(currentCharacter); if (StorageManager.SaveJsonData(currentCharacter.SaveName, json, true)) { @@ -761,7 +828,6 @@ private async Task SavePed() } } } - } /// @@ -1295,20 +1361,31 @@ int UnclampMix(float value) #region clothes clothesMenu.OnListIndexChange += (_menu, listItem, oldSelectionIndex, newSelectionIndex, realIndex) => { - var componentIndex = realIndex + 1; + int componentIndex = realIndex + 1; if (realIndex > 0) { componentIndex += 1; } - var textureIndex = GetPedTextureVariation(Game.PlayerPed.Handle, componentIndex); - var newTextureIndex = 0; - SetPedComponentVariation(Game.PlayerPed.Handle, componentIndex, newSelectionIndex, newTextureIndex, 0); - currentCharacter.DrawableVariations.clothes ??= new Dictionary>(); + int newTextureIndex = 0; - var maxTextures = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, newSelectionIndex); + SetPedComponentVariation(Game.PlayerPed.Handle, componentIndex, newSelectionIndex, newTextureIndex, 0); + currentCharacter.DrawableVariations.clothes ??= new Dictionary>(); currentCharacter.DrawableVariations.clothes[componentIndex] = new KeyValuePair(newSelectionIndex, newTextureIndex); + + string collectionName = GetPedDrawableVariationCollectionName(Game.PlayerPed.Handle, componentIndex) ?? "base"; + + currentCharacter.DrawableVariations.clothesWithCollection ??= new(); + currentCharacter.DrawableVariations.clothesWithCollection[componentIndex] = new CharacterClothingData + { + ComponentIndex = componentIndex, + Drawable = GetPedDrawableVariationCollectionLocalIndex(componentIndex, newSelectionIndex), + Texture = newTextureIndex, + Collection = collectionName + }; + + int maxTextures = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, newSelectionIndex); listItem.Description = $"Select a drawable using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; }; @@ -1320,14 +1397,27 @@ int UnclampMix(float value) componentIndex += 1; } - var textureIndex = GetPedTextureVariation(Game.PlayerPed.Handle, componentIndex); - var newTextureIndex = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, listIndex) - 1 < textureIndex + 1 ? 0 : textureIndex + 1; + int textureIndex = GetPedTextureVariation(Game.PlayerPed.Handle, componentIndex); + int newTextureIndex = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, listIndex) - 1 < textureIndex + 1 ? 0 : textureIndex + 1; + SetPedComponentVariation(Game.PlayerPed.Handle, componentIndex, listIndex, newTextureIndex, 0); + currentCharacter.DrawableVariations.clothes ??= new Dictionary>(); + currentCharacter.DrawableVariations.clothes[componentIndex] = new KeyValuePair(listIndex, newTextureIndex); - var maxTextures = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, listIndex); + string collectionName = GetPedDrawableVariationCollectionName(Game.PlayerPed.Handle, componentIndex); + if (string.IsNullOrEmpty(collectionName)) collectionName = "base"; - currentCharacter.DrawableVariations.clothes[componentIndex] = new KeyValuePair(listIndex, newTextureIndex); + currentCharacter.DrawableVariations.clothesWithCollection ??= new Dictionary(); + currentCharacter.DrawableVariations.clothesWithCollection[componentIndex] = new CharacterClothingData + { + ComponentIndex = componentIndex, + Drawable = GetPedDrawableVariationCollectionLocalIndex(componentIndex, listIndex), + Texture = newTextureIndex, + Collection = collectionName + }; + + int maxTextures = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, listIndex); listItem.Description = $"Select a drawable using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; }; #endregion @@ -1335,7 +1425,7 @@ int UnclampMix(float value) #region props propsMenu.OnListIndexChange += (_menu, listItem, oldSelectionIndex, newSelectionIndex, realIndex) => { - var propIndex = realIndex; + int propIndex = realIndex; if (realIndex == 3) { propIndex = 6; @@ -1344,28 +1434,56 @@ int UnclampMix(float value) { propIndex = 7; } + int textureIndex = 0; + + int maxDrawables = GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex); + bool isNoProp = newSelectionIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex); + + currentCharacter.PropVariations.props ??= new Dictionary>(); + currentCharacter.PropVariations.propsWithCollection ??= new Dictionary(); - var textureIndex = 0; - if (newSelectionIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) + if (isNoProp) { SetPedPropIndex(Game.PlayerPed.Handle, propIndex, -1, -1, false); ClearPedProp(Game.PlayerPed.Handle, propIndex); currentCharacter.PropVariations.props ??= new Dictionary>(); currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(-1, -1); - listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."; + currentCharacter.PropVariations.propsWithCollection ??= new Dictionary(); + currentCharacter.PropVariations.propsWithCollection[propIndex] = new CharacterPropData + { + PropIndex = propIndex, + Drawable = -1, + Texture = -1, + Collection = "base" + }; + + listItem.Description = "Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."; } else { SetPedPropIndex(Game.PlayerPed.Handle, propIndex, newSelectionIndex, textureIndex, true); currentCharacter.PropVariations.props ??= new Dictionary>(); currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(newSelectionIndex, textureIndex); + + string collectionName = GetPedCollectionNameFromProp(Game.PlayerPed.Handle, propIndex, newSelectionIndex); + if (string.IsNullOrEmpty(collectionName)) collectionName = "base"; + + currentCharacter.PropVariations.propsWithCollection ??= new Dictionary(); + currentCharacter.PropVariations.propsWithCollection[propIndex] = new CharacterPropData + { + PropIndex = propIndex, + Drawable = GetPedCollectionLocalIndexFromProp(Game.PlayerPed.Handle, propIndex, newSelectionIndex), + Texture = textureIndex, + Collection = collectionName + }; + if (GetPedPropIndex(Game.PlayerPed.Handle, propIndex) == -1) { listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."; } else { - var maxPropTextures = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, newSelectionIndex); + int maxPropTextures = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, newSelectionIndex); listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{textureIndex + 1} (of {maxPropTextures})."; } } @@ -1383,14 +1501,22 @@ int UnclampMix(float value) propIndex = 7; } - var textureIndex = GetPedPropTextureIndex(Game.PlayerPed.Handle, propIndex); - var newTextureIndex = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, listIndex) - 1 < textureIndex + 1 ? 0 : textureIndex + 1; - if (textureIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) + int textureIndex = GetPedPropTextureIndex(Game.PlayerPed.Handle, propIndex); + int newTextureIndex = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, listIndex) - 1 < textureIndex + 1 ? 0 : textureIndex + 1; + if (listIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) { SetPedPropIndex(Game.PlayerPed.Handle, propIndex, -1, -1, false); ClearPedProp(Game.PlayerPed.Handle, propIndex); currentCharacter.PropVariations.props ??= new Dictionary>(); currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(-1, -1); + currentCharacter.PropVariations.propsWithCollection ??= new Dictionary(); + currentCharacter.PropVariations.propsWithCollection[propIndex] = new CharacterPropData + { + PropIndex = propIndex, + Drawable = -1, + Texture = -1, + Collection = "base" + }; listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."; } else @@ -1398,6 +1524,18 @@ int UnclampMix(float value) SetPedPropIndex(Game.PlayerPed.Handle, propIndex, listIndex, newTextureIndex, true); currentCharacter.PropVariations.props ??= new Dictionary>(); currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(listIndex, newTextureIndex); + + string collectionName = GetPedCollectionNameFromProp(Game.PlayerPed.Handle, propIndex, listIndex); + if (string.IsNullOrEmpty(collectionName)) collectionName = "base"; + + currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(listIndex, newTextureIndex); + currentCharacter.PropVariations.propsWithCollection[propIndex] = new CharacterPropData + { + PropIndex = propIndex, + Drawable = GetPedCollectionLocalIndexFromProp(Game.PlayerPed.Handle, propIndex, listIndex), + Texture = newTextureIndex, + Collection = collectionName + }; if (GetPedPropIndex(Game.PlayerPed.Handle, propIndex) == -1) { listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."; @@ -2977,21 +3115,82 @@ private MultiplayerPedData ReplacePedDataClothing(MultiplayerPedData character) { int handle = Game.PlayerPed.Handle; - // Drawables + // Init if null + character.DrawableVariations.clothes ??= new Dictionary>(); + character.DrawableVariations.clothesWithCollection ??= new Dictionary(); + character.PropVariations.props ??= new Dictionary>(); + character.PropVariations.propsWithCollection ??= new Dictionary(); + for (int i = 0; i < 12; i++) { - int drawable = GetPedDrawableVariation(handle, i); int texture = GetPedTextureVariation(handle, i); + string collectionName = GetPedDrawableVariationCollectionName(handle, i); + + int drawable; + if (string.IsNullOrEmpty(collectionName) || collectionName == "base") + { + drawable = GetPedDrawableVariation(handle, i); + collectionName = "base"; + } + else + { + drawable = GetPedDrawableVariationCollectionLocalIndex(handle, i); + } + character.DrawableVariations.clothes[i] = new KeyValuePair(drawable, texture); + character.DrawableVariations.clothesWithCollection[i] = new CharacterClothingData + { + ComponentIndex = i, + Drawable = drawable, + Texture = texture, + Collection = collectionName + }; } for (int i = 0; i < 8; i++) { - int prop = GetPedPropIndex(handle, i); int texture = GetPedPropTextureIndex(handle, i); - character.PropVariations.props[i] = new KeyValuePair(prop, texture); + string collectionName = GetPedCollectionNameFromProp(handle, i, GetPedPropIndex(handle, i)); + + + int drawable; + if (string.IsNullOrEmpty(collectionName) || collectionName == "base") + { + drawable = GetPedPropIndex(handle, i); + collectionName = "base"; + } + else + { + drawable = GetPedCollectionLocalIndexFromProp(handle, i, GetPedPropIndex(handle, i)); + } + + + if (drawable == -1) // no prop at this index + { + character.PropVariations.props[i] = new KeyValuePair(-1, -1); + character.PropVariations.propsWithCollection[i] = new CharacterPropData + { + PropIndex = i, + Drawable = -1, + Texture = -1, + Collection = "base" + }; + continue; + } + + character.PropVariations.props[i] = new KeyValuePair(drawable, texture); + character.PropVariations.propsWithCollection[i] = new CharacterPropData + { + PropIndex = i, + Drawable = drawable, + Texture = texture, + Collection = collectionName + }; } + /*character.clothesWithCollection = character.DrawableVariations.clothesWithCollection; + character.propsWithCollection = character.PropVariations.propsWithCollection;*/ + return character; } @@ -3182,24 +3381,57 @@ internal async Task AppySavedDataToPed(MultiplayerPedData character, int pedHand #endregion #region Clothing Data - if (character.DrawableVariations.clothes != null && character.DrawableVariations.clothes.Count > 0) + // Apply clothing (supports old KeyValuePair and new object structure) + if (character.DrawableVariations.clothesWithCollection != null && character.DrawableVariations.clothesWithCollection?.Count > 0) + { + foreach (var item in character.DrawableVariations.clothesWithCollection.Values) + { + if (item.Collection == "base") + { + SetPedComponentVariation(pedHandle, item.ComponentIndex, item.Drawable, item.Texture, 0); + } + else + { + SetPedCollectionComponentVariation(pedHandle, item.ComponentIndex, item.Collection, item.Drawable, item.Texture, 0); + } + } + } + else if (character.DrawableVariations.clothes != null && character.DrawableVariations.clothes.Count > 0) { - foreach (var cd in character.DrawableVariations.clothes) + foreach (var kvp in character.DrawableVariations.clothes) { - SetPedComponentVariation(pedHandle, cd.Key, cd.Value.Key, cd.Value.Value, 0); + SetPedComponentVariation(pedHandle, kvp.Key, kvp.Value.Key, kvp.Value.Value, 0); } } #endregion #region Props Data - if (character.PropVariations.props != null && character.PropVariations.props.Count > 0) + // Apply props (supports old KeyValuePair and new object structure) + if (character.PropVariations.propsWithCollection != null && character.PropVariations.propsWithCollection.Count > 0) + { + foreach (var item in character.PropVariations.propsWithCollection.Values) + { + if (item.Drawable > -1) + { + if (item.Collection == "base") + { + SetPedPropIndex(pedHandle, item.PropIndex, item.Drawable, item.Texture, true); + } + else + { + SetPedCollectionPropIndex(pedHandle, item.PropIndex, item.Collection, item.Drawable, item.Texture, true); + } + } + } + } + else if (character.PropVariations.props != null && character.PropVariations.props.Count > 0) { - foreach (var cd in character.PropVariations.props) + foreach (var kvp in character.PropVariations.props) { - if (cd.Value.Key > -1) + if (kvp.Value.Key > -1) { - int textureIndex = cd.Value.Value > -1 ? cd.Value.Value : 0; - SetPedPropIndex(pedHandle, cd.Key, cd.Value.Key, textureIndex, true); + int texture = kvp.Value.Value > -1 ? kvp.Value.Value : 0; + SetPedPropIndex(pedHandle, kvp.Key, kvp.Value.Key, texture, true); } } } diff --git a/vMenu/vMenuClient.csproj b/vMenu/vMenuClient.csproj index b2d0084be..c9e0e8f2f 100644 --- a/vMenu/vMenuClient.csproj +++ b/vMenu/vMenuClient.csproj @@ -16,14 +16,11 @@ + - - runtime - - ..\dependencies\shared\Newtonsoft.Json.dll true From 21fb48a0a0540b007ff33dd5af234236870e9d2b Mon Sep 17 00:00:00 2001 From: James Castle Date: Mon, 11 Aug 2025 12:53:05 -0700 Subject: [PATCH 03/10] Fix Package Reference --- vMenu/vMenuClient.csproj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vMenu/vMenuClient.csproj b/vMenu/vMenuClient.csproj index c9e0e8f2f..9208decf7 100644 --- a/vMenu/vMenuClient.csproj +++ b/vMenu/vMenuClient.csproj @@ -16,7 +16,9 @@ - + + runtime + From b56f5a53b8801b92e8c2e5619d4372229318aee0 Mon Sep 17 00:00:00 2001 From: James Castle Date: Mon, 11 Aug 2025 13:33:38 -0700 Subject: [PATCH 04/10] Compatibility fixes --- vMenu/menus/MpPedCustomization.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/vMenu/menus/MpPedCustomization.cs b/vMenu/menus/MpPedCustomization.cs index 835adbd08..9b3b33262 100644 --- a/vMenu/menus/MpPedCustomization.cs +++ b/vMenu/menus/MpPedCustomization.cs @@ -3131,13 +3131,14 @@ private MultiplayerPedData ReplacePedDataClothing(MultiplayerPedData character) { drawable = GetPedDrawableVariation(handle, i); collectionName = "base"; + character.DrawableVariations.clothes[i] = new KeyValuePair(drawable, texture); } else { drawable = GetPedDrawableVariationCollectionLocalIndex(handle, i); + character.DrawableVariations.clothes[i] = new KeyValuePair(GetPedDrawableGlobalIndexFromCollection(handle, i, collectionName, drawable), texture); } - character.DrawableVariations.clothes[i] = new KeyValuePair(drawable, texture); character.DrawableVariations.clothesWithCollection[i] = new CharacterClothingData { ComponentIndex = i, @@ -3158,10 +3159,12 @@ private MultiplayerPedData ReplacePedDataClothing(MultiplayerPedData character) { drawable = GetPedPropIndex(handle, i); collectionName = "base"; + character.PropVariations.props[i] = new KeyValuePair(drawable, texture); } else { drawable = GetPedCollectionLocalIndexFromProp(handle, i, GetPedPropIndex(handle, i)); + character.PropVariations.props[i] = new KeyValuePair(GetPedPropGlobalIndexFromCollection(handle, i, collectionName, drawable), texture); } @@ -3178,7 +3181,6 @@ private MultiplayerPedData ReplacePedDataClothing(MultiplayerPedData character) continue; } - character.PropVariations.props[i] = new KeyValuePair(drawable, texture); character.PropVariations.propsWithCollection[i] = new CharacterPropData { PropIndex = i, From c50cca5e2230e227f2a6a43e3437c7c45d530758 Mon Sep 17 00:00:00 2001 From: "Christopher M." Date: Wed, 12 Nov 2025 10:31:49 +1100 Subject: [PATCH 05/10] Adds `Left Ctrl` + `Enter` keybind to MP Ped Customization Menu --- vMenu/menus/MpPedCustomization.cs | 142 ++++++++++++++++++++++-------- 1 file changed, 105 insertions(+), 37 deletions(-) diff --git a/vMenu/menus/MpPedCustomization.cs b/vMenu/menus/MpPedCustomization.cs index 61e55e77e..f608fd02d 100644 --- a/vMenu/menus/MpPedCustomization.cs +++ b/vMenu/menus/MpPedCustomization.cs @@ -1380,27 +1380,29 @@ int UnclampMix(float value) #endregion #region clothes - clothesMenu.OnListIndexChange += (_menu, listItem, oldSelectionIndex, newSelectionIndex, realIndex) => + clothesMenu.OnListIndexChange += (_menu, listItem, oldSelectionIndex, newSelectionIndex, realIndex) => ChangeClothingListItem(realIndex, newSelectionIndex, listItem); + + clothesMenu.OnListItemSelect += async (sender, listItem, listIndex, realIndex) => { - var componentIndex = realIndex + 1; - if (realIndex > 0) + int controlIndex = 0; + bool isCtrlPressed = Game.IsControlPressed(controlIndex, Control.Duck); + + if (isCtrlPressed) { - componentIndex += 1; - } + string userInput = await GetUserInput("Enter Drawable ID", 5); - var textureIndex = GetPedTextureVariation(Game.PlayerPed.Handle, componentIndex); - var newTextureIndex = 0; - SetPedComponentVariation(Game.PlayerPed.Handle, componentIndex, newSelectionIndex, newTextureIndex, 0); - currentCharacter.DrawableVariations.clothes ??= new Dictionary>(); + if (string.IsNullOrEmpty(userInput) || !int.TryParse(userInput, out int drawableId) || drawableId < 0 || drawableId > listItem.ItemsCount) + { + Notify.Error("Invalid input"); + return; + } - var maxTextures = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, newSelectionIndex); + listItem.ListIndex = drawableId; - currentCharacter.DrawableVariations.clothes[componentIndex] = new KeyValuePair(newSelectionIndex, newTextureIndex); - listItem.Description = $"Select a drawable using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; - }; + ChangeClothingListItem(realIndex, drawableId, listItem); + return; + } - clothesMenu.OnListItemSelect += (sender, listItem, listIndex, realIndex) => - { var componentIndex = realIndex + 1; // skip face options as that fucks up with inheritance faces if (realIndex > 0) // skip hair features as that is done in the appeareance menu { @@ -1417,11 +1419,57 @@ int UnclampMix(float value) currentCharacter.DrawableVariations.clothes[componentIndex] = new KeyValuePair(listIndex, newTextureIndex); listItem.Description = $"Select a drawable using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; }; + + void ChangeClothingListItem(int itemIndex, int newSelectionIndex, MenuListItem listItem) + { + var componentIndex = itemIndex + 1; + if (itemIndex > 0) + { + componentIndex += 1; + } + + var textureIndex = GetPedTextureVariation(Game.PlayerPed.Handle, componentIndex); + var newTextureIndex = 0; + SetPedComponentVariation(Game.PlayerPed.Handle, componentIndex, newSelectionIndex, newTextureIndex, 0); + currentCharacter.DrawableVariations.clothes ??= new Dictionary>(); + + var maxTextures = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, newSelectionIndex); + + currentCharacter.DrawableVariations.clothes[componentIndex] = new KeyValuePair(newSelectionIndex, newTextureIndex); + listItem.Description = $"Select a drawable using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; + } #endregion #region props - propsMenu.OnListIndexChange += (_menu, listItem, oldSelectionIndex, newSelectionIndex, realIndex) => + propsMenu.OnListIndexChange += (_menu, listItem, oldSelectionIndex, newSelectionIndex, realIndex) => ChangePropListItem(realIndex, newSelectionIndex, listItem); + + propsMenu.OnListItemSelect += async (sender, listItem, listIndex, realIndex) => { + int controlIndex = 0; + bool isCtrlPressed = Game.IsControlPressed(controlIndex, Control.Duck); + + if (isCtrlPressed) + { + string userInput = await GetUserInput("Enter Prop ID", 5); + + if (string.IsNullOrEmpty(userInput) || !int.TryParse(userInput, out int drawableId) || drawableId < -1 || drawableId > listItem.ItemsCount) + { + Notify.Error("Invalid input"); + return; + } + + listItem.ListIndex = drawableId; + + if (drawableId == -1) + { + ClearPedProp(Game.PlayerPed.Handle, realIndex); + return; + } + + ChangePropListItem(realIndex, drawableId, listItem); + return; + } + var propIndex = realIndex; if (realIndex == 3) { @@ -1432,8 +1480,9 @@ int UnclampMix(float value) propIndex = 7; } - var textureIndex = 0; - if (newSelectionIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) + var textureIndex = GetPedPropTextureIndex(Game.PlayerPed.Handle, propIndex); + var newTextureIndex = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, listIndex) - 1 < textureIndex + 1 ? 0 : textureIndex + 1; + if (textureIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) { SetPedPropIndex(Game.PlayerPed.Handle, propIndex, -1, -1, false); ClearPedProp(Game.PlayerPed.Handle, propIndex); @@ -1443,36 +1492,35 @@ int UnclampMix(float value) } else { - SetPedPropIndex(Game.PlayerPed.Handle, propIndex, newSelectionIndex, textureIndex, true); + SetPedPropIndex(Game.PlayerPed.Handle, propIndex, listIndex, newTextureIndex, true); currentCharacter.PropVariations.props ??= new Dictionary>(); - currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(newSelectionIndex, textureIndex); + currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(listIndex, newTextureIndex); if (GetPedPropIndex(Game.PlayerPed.Handle, propIndex) == -1) { listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."; } else { - var maxPropTextures = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, newSelectionIndex); - listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{textureIndex + 1} (of {maxPropTextures})."; + var maxPropTextures = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, listIndex); + listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{newTextureIndex + 1} (of {maxPropTextures})."; } } }; - propsMenu.OnListItemSelect += (sender, listItem, listIndex, realIndex) => + void ChangePropListItem(int itemIndex, int newSelectionIndex, MenuListItem listItem) { - var propIndex = realIndex; - if (realIndex == 3) + var propIndex = itemIndex; + if (itemIndex == 3) { propIndex = 6; } - if (realIndex == 4) + if (itemIndex == 4) { propIndex = 7; } - var textureIndex = GetPedPropTextureIndex(Game.PlayerPed.Handle, propIndex); - var newTextureIndex = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, listIndex) - 1 < textureIndex + 1 ? 0 : textureIndex + 1; - if (textureIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) + var textureIndex = 0; + if (newSelectionIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) { SetPedPropIndex(Game.PlayerPed.Handle, propIndex, -1, -1, false); ClearPedProp(Game.PlayerPed.Handle, propIndex); @@ -1482,21 +1530,20 @@ int UnclampMix(float value) } else { - SetPedPropIndex(Game.PlayerPed.Handle, propIndex, listIndex, newTextureIndex, true); + SetPedPropIndex(Game.PlayerPed.Handle, propIndex, newSelectionIndex, textureIndex, true); currentCharacter.PropVariations.props ??= new Dictionary>(); - currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(listIndex, newTextureIndex); + currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(newSelectionIndex, textureIndex); if (GetPedPropIndex(Game.PlayerPed.Handle, propIndex) == -1) { listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."; } else { - var maxPropTextures = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, listIndex); - listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{newTextureIndex + 1} (of {maxPropTextures})."; + var maxPropTextures = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, newSelectionIndex); + listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{textureIndex + 1} (of {maxPropTextures})."; } } - //propsMenu.UpdateScaleform(); - }; + } #endregion #region face shape data @@ -1692,10 +1739,31 @@ void ApplySavedTattoos() } }; - tattoosMenu.OnListItemSelect += (sender, item, tattooIndex, menuIndex) => + tattoosMenu.OnListItemSelect += async (sender, item, tattooIndex, menuIndex) => { CreateListsIfNull(); + int controlIndex = 0; + bool isBadgesItem = menuIndex == 6; + bool isCtrlPressed = Game.IsControlPressed(controlIndex, Control.Duck); + + if (isCtrlPressed) + { + string userInput = await GetUserInput($"Enter {(isBadgesItem ? "Badge" : "Tattoo")} ID", 5); + + if (string.IsNullOrEmpty(userInput) || !int.TryParse(userInput, out int drawableId) || drawableId < 1 || drawableId > item.ItemsCount) + { + Notify.Error("Invalid input"); + return; + } + + drawableId--; + + item.ListIndex = drawableId; + + tattooIndex = drawableId; + } + if (menuIndex == 0) // head { var Tattoo = currentCharacter.IsMale ? MaleTattoosCollection.HEAD.ElementAt(tattooIndex) : FemaleTattoosCollection.HEAD.ElementAt(tattooIndex); @@ -1786,7 +1854,7 @@ void ApplySavedTattoos() currentCharacter.PedTatttoos.RightLegTattoos.Add(tat); } } - else if (menuIndex == 6) // badges + else if (isBadgesItem) { var Tattoo = currentCharacter.IsMale ? MaleTattoosCollection.BADGES.ElementAt(tattooIndex) : FemaleTattoosCollection.BADGES.ElementAt(tattooIndex); var tat = new KeyValuePair(Tattoo.collectionName, Tattoo.name); From da9230cb1aca88356c6a97bc30bde817a430fe1c Mon Sep 17 00:00:00 2001 From: "Christopher M." Date: Wed, 12 Nov 2025 10:38:26 +1100 Subject: [PATCH 06/10] Adds `Left Ctrl` + `Enter` keybind to Player Appearance Menu --- vMenu/menus/PlayerAppearance.cs | 74 ++++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 25 deletions(-) diff --git a/vMenu/menus/PlayerAppearance.cs b/vMenu/menus/PlayerAppearance.cs index ed7dada5a..1e4f1f867 100644 --- a/vMenu/menus/PlayerAppearance.cs +++ b/vMenu/menus/PlayerAppearance.cs @@ -540,37 +540,34 @@ async void SpawnPed(Menu m, MenuItem item, int index) #region ped drawable list changes // Manage list changes. - pedCustomizationMenu.OnListIndexChange += (sender, item, oldListIndex, newListIndex, itemIndex) => + pedCustomizationMenu.OnListIndexChange += (sender, item, oldListIndex, newListIndex, itemIndex) => ChangeListItem(item, newListIndex); + + // Manage list selections. + pedCustomizationMenu.OnListItemSelect += async (sender, item, listIndex, itemIndex) => { - if (drawablesMenuListItems.ContainsKey(item)) - { - var drawableID = drawablesMenuListItems[item]; - SetPedComponentVariation(Game.PlayerPed.Handle, drawableID, newListIndex, 0, 0); - } - else if (propsMenuListItems.ContainsKey(item)) + int controlIndex = 0; + bool isDrawable = drawablesMenuListItems.ContainsKey(item); + bool isCtrlPressed = Game.IsControlPressed(controlIndex, Control.Duck); + + if (isCtrlPressed) { - var propID = propsMenuListItems[item]; - if (newListIndex == 0) - { - SetPedPropIndex(Game.PlayerPed.Handle, propID, newListIndex - 1, 0, false); - ClearPedProp(Game.PlayerPed.Handle, propID); - } - else - { - SetPedPropIndex(Game.PlayerPed.Handle, propID, newListIndex - 1, 0, true); - } - if (propID == 0) + string userInput = await GetUserInput($"Enter {(isDrawable ? "Drawable" : "Prop")} ID", 5); + + if (string.IsNullOrEmpty(userInput) || !int.TryParse(userInput, out int drawableId) || drawableId < 1 || drawableId > item.ItemsCount) { - ShowVisorText(Game.PlayerPed.Handle); + Notify.Error("Invalid input"); + return; } + drawableId--; + + item.ListIndex = drawableId; + + ChangeListItem(item, drawableId); + return; } - }; - // Manage list selections. - pedCustomizationMenu.OnListItemSelect += (sender, item, listIndex, itemIndex) => - { - if (drawablesMenuListItems.ContainsKey(item)) // drawable + if (isDrawable) { var currentDrawableID = drawablesMenuListItems[item]; var currentTextureIndex = GetPedTextureVariation(Game.PlayerPed.Handle, currentDrawableID); @@ -585,7 +582,7 @@ async void SpawnPed(Menu m, MenuItem item, int index) SetPedComponentVariation(Game.PlayerPed.Handle, currentDrawableID, listIndex, newTexture, 0); } - else if (propsMenuListItems.ContainsKey(item)) // prop + else // prop { var currentPropIndex = propsMenuListItems[item]; var currentPropVariationIndex = GetPedPropIndex(Game.PlayerPed.Handle, currentPropIndex); @@ -596,6 +593,33 @@ async void SpawnPed(Menu m, MenuItem item, int index) SetPedPropIndex(Game.PlayerPed.Handle, currentPropIndex, currentPropVariationIndex, newPropTextureVariationIndex, true); } }; + + void ChangeListItem(MenuListItem item, int newListIndex) + { + if (drawablesMenuListItems.ContainsKey(item)) + { + var drawableID = drawablesMenuListItems[item]; + SetPedComponentVariation(Game.PlayerPed.Handle, drawableID, newListIndex, 0, 0); + } + else if (propsMenuListItems.ContainsKey(item)) + { + var propID = propsMenuListItems[item]; + if (newListIndex == 0) + { + SetPedPropIndex(Game.PlayerPed.Handle, propID, newListIndex - 1, 0, false); + ClearPedProp(Game.PlayerPed.Handle, propID); + } + else + { + SetPedPropIndex(Game.PlayerPed.Handle, propID, newListIndex - 1, 0, true); + } + if (propID == 0) + { + ShowVisorText(Game.PlayerPed.Handle); + } + + } + } #endregion pedCollectionsCustomizationMenu.OnListIndexChange += (_, item, oldListIndex, newListIndex, ___) => From 90b5c9788bef7edf044843ce350a3bdefa5ed52a Mon Sep 17 00:00:00 2001 From: Christopher <10535902+cm8263@users.noreply.github.com> Date: Wed, 12 Nov 2025 10:46:00 +1100 Subject: [PATCH 07/10] Update vMenu/menus/PlayerAppearance.cs --- vMenu/menus/PlayerAppearance.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/vMenu/menus/PlayerAppearance.cs b/vMenu/menus/PlayerAppearance.cs index 1e4f1f867..cb37a8547 100644 --- a/vMenu/menus/PlayerAppearance.cs +++ b/vMenu/menus/PlayerAppearance.cs @@ -617,7 +617,6 @@ void ChangeListItem(MenuListItem item, int newListIndex) { ShowVisorText(Game.PlayerPed.Handle); } - } } #endregion From a7e9aa84a3f26e6ab32cba7f12da72b720c6d6e0 Mon Sep 17 00:00:00 2001 From: blackfirefly000 <50463948+blackfirefly000@users.noreply.github.com> Date: Tue, 17 Feb 2026 00:05:07 -0800 Subject: [PATCH 08/10] Update MpPedCustomization.cs --- vMenu/menus/MpPedCustomization.cs | 481 ++++++++++++++---------------- 1 file changed, 232 insertions(+), 249 deletions(-) diff --git a/vMenu/menus/MpPedCustomization.cs b/vMenu/menus/MpPedCustomization.cs index dcf220114..d0910f23a 100644 --- a/vMenu/menus/MpPedCustomization.cs +++ b/vMenu/menus/MpPedCustomization.cs @@ -1452,116 +1452,108 @@ int UnclampMix(float value) { int componentIndex = realIndex + 1; -// --- CTRL Manual Drawable Selection --- -int controlIndex = 0; -bool isCtrlPressed = Game.IsControlPressed(controlIndex, Control.Duck); - -if (isCtrlPressed) -{ - string userInput = await GetUserInput("Enter Drawable ID", 5); - - if (string.IsNullOrEmpty(userInput) || - !int.TryParse(userInput, out int drawableId) || - drawableId < 0 || - drawableId >= listItem.ItemsCount) - { - Notify.Error("Invalid input"); - return; - } - - listItem.ListIndex = drawableId; - newSelectionIndex = drawableId; -} -else -{ - newSelectionIndex = listItem.ListIndex; -} - -// --- Apply Component --- -int newTextureIndex = 0; - -SetPedComponentVariation(Game.PlayerPed.Handle, componentIndex, newSelectionIndex, newTextureIndex, 0); - -// Ensure dictionary exists -currentCharacter.DrawableVariations.clothes ??= new Dictionary>(); -currentCharacter.DrawableVariations.clothes[componentIndex] = - new KeyValuePair(newSelectionIndex, newTextureIndex); - -// --- Save With Collection --- -string collectionName = - GetPedDrawableVariationCollectionName(Game.PlayerPed.Handle, componentIndex) ?? "base"; - -currentCharacter.DrawableVariations.clothesWithCollection ??= new(); + int controlIndex = 0; + bool isCtrlPressed = Game.IsControlPressed(controlIndex, Control.Duck); + + if (isCtrlPressed) + { + string userInput = await GetUserInput("Enter Drawable ID", 5); + + if (string.IsNullOrEmpty(userInput) || + !int.TryParse(userInput, out int drawableId) || + drawableId < 0 || + drawableId >= listItem.ItemsCount) + { + Notify.Error("Invalid input"); + return; + } + + listItem.ListIndex = drawableId; + newSelectionIndex = drawableId; + } + else + { + newSelectionIndex = listItem.ListIndex; + } + + int newTextureIndex = 0; + + SetPedComponentVariation(Game.PlayerPed.Handle, componentIndex, newSelectionIndex, newTextureIndex, 0); + + currentCharacter.DrawableVariations.clothes ??= new Dictionary>(); + currentCharacter.DrawableVariations.clothes[componentIndex] = + new KeyValuePair(newSelectionIndex, newTextureIndex); + + string collectionName = + GetPedDrawableVariationCollectionName(Game.PlayerPed.Handle, componentIndex) ?? "base"; + + currentCharacter.DrawableVariations.clothesWithCollection ??= new(); + + currentCharacter.DrawableVariations.clothesWithCollection[componentIndex] = + new CharacterClothingData + { + ComponentIndex = componentIndex, + Drawable = GetPedDrawableVariationCollectionLocalIndex(componentIndex, newSelectionIndex), + Texture = newTextureIndex, + Collection = collectionName + }; -currentCharacter.DrawableVariations.clothesWithCollection[componentIndex] = - new CharacterClothingData - { - ComponentIndex = componentIndex, - Drawable = GetPedDrawableVariationCollectionLocalIndex(componentIndex, newSelectionIndex), - Texture = newTextureIndex, - Collection = collectionName - }; - -// --- Update UI Description --- -int maxTextures = - GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, newSelectionIndex); - -listItem.Description = - $"Select a drawable using arrow keys.\n" + - $"Press ~o~CTRL~s~ to manually enter ID.\n" + - $"Press ~o~ENTER~s~ to cycle textures.\n" + - $"Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; + int maxTextures = + GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, newSelectionIndex); + + listItem.Description = + $"Select a drawable using arrow keys.\n" + + $"Press ~o~CTRL~s~ to manually enter ID.\n" + + $"Press ~o~ENTER~s~ to cycle textures.\n" + + $"Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; }; void ChangeClothingListItem(int itemIndex, int newSelectionIndex, MenuListItem listItem) { int componentIndex = itemIndex + 1; - if (newSelectionIndex < 0 || - newSelectionIndex >= GetNumberOfPedDrawableVariations(Game.PlayerPed.Handle, componentIndex)) - return; - - int newTextureIndex = 0; - - // Apply to ped (client-sided preview) - SetPedComponentVariation( - Game.PlayerPed.Handle, - componentIndex, - newSelectionIndex, - newTextureIndex, - 0 - ); - - // Ensure dictionary exists - currentCharacter.DrawableVariations.clothes ??= - new Dictionary>(); - - currentCharacter.DrawableVariations.clothes[componentIndex] = - new KeyValuePair(newSelectionIndex, newTextureIndex); - - // ---- COLLECTION SUPPORT ---- - string collectionName = - GetPedDrawableVariationCollectionName(Game.PlayerPed.Handle, componentIndex) ?? "base"; - - currentCharacter.DrawableVariations.clothesWithCollection ??= new(); - - currentCharacter.DrawableVariations.clothesWithCollection[componentIndex] = - new CharacterClothingData - { - ComponentIndex = componentIndex, - Drawable = - GetPedDrawableVariationCollectionLocalIndex(componentIndex, newSelectionIndex), - Texture = newTextureIndex, - Collection = collectionName - }; - - int maxTextures = - GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, newSelectionIndex); + if (newSelectionIndex < 0 || + newSelectionIndex >= GetNumberOfPedDrawableVariations(Game.PlayerPed.Handle, componentIndex)) + return; + + int newTextureIndex = 0; - listItem.Description = - $"Press ~o~CTRL~s~ to enter ID.\n" + - $"Press ~o~ENTER~s~ to cycle textures.\n" + - $"Current texture: #{newTextureIndex + 1} (of {maxTextures})."; + SetPedComponentVariation( + Game.PlayerPed.Handle, + componentIndex, + newSelectionIndex, + newTextureIndex, + 0 + ); + + currentCharacter.DrawableVariations.clothes ??= + new Dictionary>(); + + currentCharacter.DrawableVariations.clothes[componentIndex] = + new KeyValuePair(newSelectionIndex, newTextureIndex); + + string collectionName = + GetPedDrawableVariationCollectionName(Game.PlayerPed.Handle, componentIndex) ?? "base"; + + currentCharacter.DrawableVariations.clothesWithCollection ??= new(); + + currentCharacter.DrawableVariations.clothesWithCollection[componentIndex] = + new CharacterClothingData + { + ComponentIndex = componentIndex, + Drawable = + GetPedDrawableVariationCollectionLocalIndex(componentIndex, newSelectionIndex), + Texture = newTextureIndex, + Collection = collectionName + }; + + int maxTextures = + GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, newSelectionIndex); + + listItem.Description = + $"Press ~o~CTRL~s~ to enter ID.\n" + + $"Press ~o~ENTER~s~ to cycle textures.\n" + + $"Current texture: #{newTextureIndex + 1} (of {maxTextures})."; } #endregion @@ -1579,88 +1571,82 @@ void ChangeClothingListItem(int itemIndex, int newSelectionIndex, MenuListItem l { propIndex = 7; } - // --- CTRL Manual Drawable Selection --- -int controlIndex = 0; -bool isCtrlPressed = Game.IsControlPressed(controlIndex, Control.Duck); - -if (isCtrlPressed) -{ - string userInput = await GetUserInput("Enter Prop ID (-1 to clear)", 5); - - if (string.IsNullOrEmpty(userInput) || - !int.TryParse(userInput, out int drawableId) || - drawableId < -1 || - drawableId >= listItem.ItemsCount) - { - Notify.Error("Invalid input"); - return; - } - - listItem.ListIndex = drawableId; - - if (drawableId == -1) - { - ClearPedProp(Game.PlayerPed.Handle, propIndex); - currentCharacter.PropVariations.props[propIndex] = - new KeyValuePair(-1, 0); - return; - } -} - -// --- Normal Selection --- -int listIndex = listItem.ListIndex; - -if (listIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) - return; - -int textureIndex = - GetPedPropTextureIndex(Game.PlayerPed.Handle, propIndex); - -int maxTextures = - GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, listIndex); - -int newTextureIndex = - (textureIndex + 1 >= maxTextures) ? 0 : textureIndex + 1; - -// Apply Prop -SetPedPropIndex(Game.PlayerPed.Handle, propIndex, listIndex, newTextureIndex, true); - -// Ensure dictionary exists -currentCharacter.PropVariations.props ??= - new Dictionary>(); - -currentCharacter.PropVariations.props[propIndex] = - new KeyValuePair(listIndex, newTextureIndex); - -// --- Save With Collection --- -string collectionName = - GetPedCollectionNameFromProp(Game.PlayerPed.Handle, propIndex, listIndex); - -if (string.IsNullOrEmpty(collectionName)) - collectionName = "base"; - -currentCharacter.PropVariations.propsWithCollection ??= - new Dictionary(); - -currentCharacter.PropVariations.propsWithCollection[propIndex] = - new CharacterPropData - { - PropIndex = propIndex, - Drawable = - GetPedCollectionLocalIndexFromProp( - Game.PlayerPed.Handle, - propIndex, - listIndex), - Texture = newTextureIndex, - Collection = collectionName - }; - -// --- Update UI Description --- -listItem.Description = - $"Select a prop using arrow keys.\n" + - $"Press ~o~CTRL~s~ to manually enter ID.\n" + - $"Press ~o~ENTER~s~ to cycle textures.\n" + - $"Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; + int controlIndex = 0; + bool isCtrlPressed = Game.IsControlPressed(controlIndex, Control.Duck); + + if (isCtrlPressed) + { + string userInput = await GetUserInput("Enter Prop ID (-1 to clear)", 5); + + if (string.IsNullOrEmpty(userInput) || + !int.TryParse(userInput, out int drawableId) || + drawableId < -1 || + drawableId >= listItem.ItemsCount) + { + Notify.Error("Invalid input"); + return; + } + + listItem.ListIndex = drawableId; + + if (drawableId == -1) + { + ClearPedProp(Game.PlayerPed.Handle, propIndex); + currentCharacter.PropVariations.props[propIndex] = + new KeyValuePair(-1, 0); + return; + } + } + + int listIndex = listItem.ListIndex; + + if (listIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) + return; + + int textureIndex = + GetPedPropTextureIndex(Game.PlayerPed.Handle, propIndex); + + int maxTextures = + GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, listIndex); + + int newTextureIndex = + (textureIndex + 1 >= maxTextures) ? 0 : textureIndex + 1; + + SetPedPropIndex(Game.PlayerPed.Handle, propIndex, listIndex, newTextureIndex, true); + + currentCharacter.PropVariations.props ??= + new Dictionary>(); + + currentCharacter.PropVariations.props[propIndex] = + new KeyValuePair(listIndex, newTextureIndex); + + string collectionName = + GetPedCollectionNameFromProp(Game.PlayerPed.Handle, propIndex, listIndex); + + if (string.IsNullOrEmpty(collectionName)) + collectionName = "base"; + + currentCharacter.PropVariations.propsWithCollection ??= + new Dictionary(); + + currentCharacter.PropVariations.propsWithCollection[propIndex] = + new CharacterPropData + { + PropIndex = propIndex, + Drawable = + GetPedCollectionLocalIndexFromProp( + Game.PlayerPed.Handle, + propIndex, + listIndex), + Texture = newTextureIndex, + Collection = collectionName + }; + + listItem.Description = + $"Select a prop using arrow keys.\n" + + $"Press ~o~CTRL~s~ to manually enter ID.\n" + + $"Press ~o~ENTER~s~ to cycle textures.\n" + + $"Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; }; void ChangePropListItem(int itemIndex, int newSelectionIndex, MenuListItem listItem) @@ -1677,75 +1663,72 @@ void ChangePropListItem(int itemIndex, int newSelectionIndex, MenuListItem listI int propIndex = itemIndex; - if (newSelectionIndex == -1) - { - ClearPedProp(Game.PlayerPed.Handle, propIndex); - - currentCharacter.PropVariations.props[propIndex] = - new KeyValuePair(-1, 0); - - return; - } - - if (newSelectionIndex < 0 || - newSelectionIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) - return; - - int newTextureIndex = 0; - - // Apply prop - SetPedPropIndex( - Game.PlayerPed.Handle, - propIndex, - newSelectionIndex, - newTextureIndex, - true - ); - - // Ensure dictionary exists - currentCharacter.PropVariations.props ??= - new Dictionary>(); - - currentCharacter.PropVariations.props[propIndex] = - new KeyValuePair(newSelectionIndex, newTextureIndex); - - // ---- COLLECTION SUPPORT ---- - string collectionName = - GetPedCollectionNameFromProp( - Game.PlayerPed.Handle, - propIndex, - newSelectionIndex - ); - - if (string.IsNullOrEmpty(collectionName)) - collectionName = "base"; - - currentCharacter.PropVariations.propsWithCollection ??= - new Dictionary(); + if (newSelectionIndex == -1) + { + ClearPedProp(Game.PlayerPed.Handle, propIndex); + + currentCharacter.PropVariations.props[propIndex] = + new KeyValuePair(-1, 0); + + return; + } + + if (newSelectionIndex < 0 || + newSelectionIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) + return; + + int newTextureIndex = 0; - currentCharacter.PropVariations.propsWithCollection[propIndex] = - new CharacterPropData - { - PropIndex = propIndex, - Drawable = - GetPedCollectionLocalIndexFromProp( + SetPedPropIndex( Game.PlayerPed.Handle, propIndex, - newSelectionIndex), - Texture = newTextureIndex, - Collection = collectionName - }; - - int maxTextures = - GetNumberOfPedPropTextureVariations( - Game.PlayerPed.Handle, - propIndex, - newSelectionIndex); + newSelectionIndex, + newTextureIndex, + true + ); - listItem.Description = - $"Press ~o~CTRL~s~ to enter ID (-1 clears).\n" + - $"Press ~o~ENTER~s~ to cycle textures.\n" + - $"Current texture: #{newTextureIndex + 1} (of {maxTextures})."; + currentCharacter.PropVariations.props ??= + new Dictionary>(); + + currentCharacter.PropVariations.props[propIndex] = + new KeyValuePair(newSelectionIndex, newTextureIndex); + + string collectionName = + GetPedCollectionNameFromProp( + Game.PlayerPed.Handle, + propIndex, + newSelectionIndex + ); + + if (string.IsNullOrEmpty(collectionName)) + collectionName = "base"; + + currentCharacter.PropVariations.propsWithCollection ??= + new Dictionary(); + + currentCharacter.PropVariations.propsWithCollection[propIndex] = + new CharacterPropData + { + PropIndex = propIndex, + Drawable = + GetPedCollectionLocalIndexFromProp( + Game.PlayerPed.Handle, + propIndex, + newSelectionIndex), + Texture = newTextureIndex, + Collection = collectionName + }; + + int maxTextures = + GetNumberOfPedPropTextureVariations( + Game.PlayerPed.Handle, + propIndex, + newSelectionIndex); + + listItem.Description = + $"Press ~o~CTRL~s~ to enter ID (-1 clears).\n" + + $"Press ~o~ENTER~s~ to cycle textures.\n" + + $"Current texture: #{newTextureIndex + 1} (of {maxTextures})."; } #endregion From cbd6518b4e44fd3f52595c881a91520b8f6eff9d Mon Sep 17 00:00:00 2001 From: blackfirefly000 <50463948+blackfirefly000@users.noreply.github.com> Date: Tue, 17 Feb 2026 00:24:47 -0800 Subject: [PATCH 09/10] Revert "Update MpPedCustomization.cs" This reverts commit a7e9aa84a3f26e6ab32cba7f12da72b720c6d6e0. --- vMenu/menus/MpPedCustomization.cs | 481 ++++++++++++++++-------------- 1 file changed, 249 insertions(+), 232 deletions(-) diff --git a/vMenu/menus/MpPedCustomization.cs b/vMenu/menus/MpPedCustomization.cs index d0910f23a..dcf220114 100644 --- a/vMenu/menus/MpPedCustomization.cs +++ b/vMenu/menus/MpPedCustomization.cs @@ -1452,108 +1452,116 @@ int UnclampMix(float value) { int componentIndex = realIndex + 1; - int controlIndex = 0; - bool isCtrlPressed = Game.IsControlPressed(controlIndex, Control.Duck); - - if (isCtrlPressed) - { - string userInput = await GetUserInput("Enter Drawable ID", 5); - - if (string.IsNullOrEmpty(userInput) || - !int.TryParse(userInput, out int drawableId) || - drawableId < 0 || - drawableId >= listItem.ItemsCount) - { - Notify.Error("Invalid input"); - return; - } - - listItem.ListIndex = drawableId; - newSelectionIndex = drawableId; - } - else - { - newSelectionIndex = listItem.ListIndex; - } - - int newTextureIndex = 0; - - SetPedComponentVariation(Game.PlayerPed.Handle, componentIndex, newSelectionIndex, newTextureIndex, 0); - - currentCharacter.DrawableVariations.clothes ??= new Dictionary>(); - currentCharacter.DrawableVariations.clothes[componentIndex] = - new KeyValuePair(newSelectionIndex, newTextureIndex); - - string collectionName = - GetPedDrawableVariationCollectionName(Game.PlayerPed.Handle, componentIndex) ?? "base"; - - currentCharacter.DrawableVariations.clothesWithCollection ??= new(); - - currentCharacter.DrawableVariations.clothesWithCollection[componentIndex] = - new CharacterClothingData - { - ComponentIndex = componentIndex, - Drawable = GetPedDrawableVariationCollectionLocalIndex(componentIndex, newSelectionIndex), - Texture = newTextureIndex, - Collection = collectionName - }; +// --- CTRL Manual Drawable Selection --- +int controlIndex = 0; +bool isCtrlPressed = Game.IsControlPressed(controlIndex, Control.Duck); - int maxTextures = - GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, newSelectionIndex); - - listItem.Description = - $"Select a drawable using arrow keys.\n" + - $"Press ~o~CTRL~s~ to manually enter ID.\n" + - $"Press ~o~ENTER~s~ to cycle textures.\n" + - $"Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; +if (isCtrlPressed) +{ + string userInput = await GetUserInput("Enter Drawable ID", 5); + + if (string.IsNullOrEmpty(userInput) || + !int.TryParse(userInput, out int drawableId) || + drawableId < 0 || + drawableId >= listItem.ItemsCount) + { + Notify.Error("Invalid input"); + return; + } + + listItem.ListIndex = drawableId; + newSelectionIndex = drawableId; +} +else +{ + newSelectionIndex = listItem.ListIndex; +} + +// --- Apply Component --- +int newTextureIndex = 0; + +SetPedComponentVariation(Game.PlayerPed.Handle, componentIndex, newSelectionIndex, newTextureIndex, 0); + +// Ensure dictionary exists +currentCharacter.DrawableVariations.clothes ??= new Dictionary>(); +currentCharacter.DrawableVariations.clothes[componentIndex] = + new KeyValuePair(newSelectionIndex, newTextureIndex); + +// --- Save With Collection --- +string collectionName = + GetPedDrawableVariationCollectionName(Game.PlayerPed.Handle, componentIndex) ?? "base"; + +currentCharacter.DrawableVariations.clothesWithCollection ??= new(); + +currentCharacter.DrawableVariations.clothesWithCollection[componentIndex] = + new CharacterClothingData + { + ComponentIndex = componentIndex, + Drawable = GetPedDrawableVariationCollectionLocalIndex(componentIndex, newSelectionIndex), + Texture = newTextureIndex, + Collection = collectionName + }; + +// --- Update UI Description --- +int maxTextures = + GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, newSelectionIndex); + +listItem.Description = + $"Select a drawable using arrow keys.\n" + + $"Press ~o~CTRL~s~ to manually enter ID.\n" + + $"Press ~o~ENTER~s~ to cycle textures.\n" + + $"Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; }; void ChangeClothingListItem(int itemIndex, int newSelectionIndex, MenuListItem listItem) { int componentIndex = itemIndex + 1; - if (newSelectionIndex < 0 || - newSelectionIndex >= GetNumberOfPedDrawableVariations(Game.PlayerPed.Handle, componentIndex)) - return; - - int newTextureIndex = 0; + if (newSelectionIndex < 0 || + newSelectionIndex >= GetNumberOfPedDrawableVariations(Game.PlayerPed.Handle, componentIndex)) + return; - SetPedComponentVariation( - Game.PlayerPed.Handle, - componentIndex, - newSelectionIndex, - newTextureIndex, - 0 - ); - - currentCharacter.DrawableVariations.clothes ??= - new Dictionary>(); - - currentCharacter.DrawableVariations.clothes[componentIndex] = - new KeyValuePair(newSelectionIndex, newTextureIndex); - - string collectionName = - GetPedDrawableVariationCollectionName(Game.PlayerPed.Handle, componentIndex) ?? "base"; - - currentCharacter.DrawableVariations.clothesWithCollection ??= new(); - - currentCharacter.DrawableVariations.clothesWithCollection[componentIndex] = - new CharacterClothingData - { - ComponentIndex = componentIndex, - Drawable = - GetPedDrawableVariationCollectionLocalIndex(componentIndex, newSelectionIndex), - Texture = newTextureIndex, - Collection = collectionName - }; - - int maxTextures = - GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, newSelectionIndex); - - listItem.Description = - $"Press ~o~CTRL~s~ to enter ID.\n" + - $"Press ~o~ENTER~s~ to cycle textures.\n" + - $"Current texture: #{newTextureIndex + 1} (of {maxTextures})."; + int newTextureIndex = 0; + + // Apply to ped (client-sided preview) + SetPedComponentVariation( + Game.PlayerPed.Handle, + componentIndex, + newSelectionIndex, + newTextureIndex, + 0 + ); + + // Ensure dictionary exists + currentCharacter.DrawableVariations.clothes ??= + new Dictionary>(); + + currentCharacter.DrawableVariations.clothes[componentIndex] = + new KeyValuePair(newSelectionIndex, newTextureIndex); + + // ---- COLLECTION SUPPORT ---- + string collectionName = + GetPedDrawableVariationCollectionName(Game.PlayerPed.Handle, componentIndex) ?? "base"; + + currentCharacter.DrawableVariations.clothesWithCollection ??= new(); + + currentCharacter.DrawableVariations.clothesWithCollection[componentIndex] = + new CharacterClothingData + { + ComponentIndex = componentIndex, + Drawable = + GetPedDrawableVariationCollectionLocalIndex(componentIndex, newSelectionIndex), + Texture = newTextureIndex, + Collection = collectionName + }; + + int maxTextures = + GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, newSelectionIndex); + + listItem.Description = + $"Press ~o~CTRL~s~ to enter ID.\n" + + $"Press ~o~ENTER~s~ to cycle textures.\n" + + $"Current texture: #{newTextureIndex + 1} (of {maxTextures})."; } #endregion @@ -1571,82 +1579,88 @@ void ChangeClothingListItem(int itemIndex, int newSelectionIndex, MenuListItem l { propIndex = 7; } - int controlIndex = 0; - bool isCtrlPressed = Game.IsControlPressed(controlIndex, Control.Duck); - - if (isCtrlPressed) - { - string userInput = await GetUserInput("Enter Prop ID (-1 to clear)", 5); - - if (string.IsNullOrEmpty(userInput) || - !int.TryParse(userInput, out int drawableId) || - drawableId < -1 || - drawableId >= listItem.ItemsCount) - { - Notify.Error("Invalid input"); - return; - } - - listItem.ListIndex = drawableId; - - if (drawableId == -1) - { - ClearPedProp(Game.PlayerPed.Handle, propIndex); - currentCharacter.PropVariations.props[propIndex] = - new KeyValuePair(-1, 0); - return; - } - } - - int listIndex = listItem.ListIndex; - - if (listIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) - return; - - int textureIndex = - GetPedPropTextureIndex(Game.PlayerPed.Handle, propIndex); - - int maxTextures = - GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, listIndex); - - int newTextureIndex = - (textureIndex + 1 >= maxTextures) ? 0 : textureIndex + 1; - - SetPedPropIndex(Game.PlayerPed.Handle, propIndex, listIndex, newTextureIndex, true); - - currentCharacter.PropVariations.props ??= - new Dictionary>(); - - currentCharacter.PropVariations.props[propIndex] = - new KeyValuePair(listIndex, newTextureIndex); - - string collectionName = - GetPedCollectionNameFromProp(Game.PlayerPed.Handle, propIndex, listIndex); - - if (string.IsNullOrEmpty(collectionName)) - collectionName = "base"; - - currentCharacter.PropVariations.propsWithCollection ??= - new Dictionary(); - - currentCharacter.PropVariations.propsWithCollection[propIndex] = - new CharacterPropData - { - PropIndex = propIndex, - Drawable = - GetPedCollectionLocalIndexFromProp( - Game.PlayerPed.Handle, - propIndex, - listIndex), - Texture = newTextureIndex, - Collection = collectionName - }; - - listItem.Description = - $"Select a prop using arrow keys.\n" + - $"Press ~o~CTRL~s~ to manually enter ID.\n" + - $"Press ~o~ENTER~s~ to cycle textures.\n" + - $"Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; + // --- CTRL Manual Drawable Selection --- +int controlIndex = 0; +bool isCtrlPressed = Game.IsControlPressed(controlIndex, Control.Duck); + +if (isCtrlPressed) +{ + string userInput = await GetUserInput("Enter Prop ID (-1 to clear)", 5); + + if (string.IsNullOrEmpty(userInput) || + !int.TryParse(userInput, out int drawableId) || + drawableId < -1 || + drawableId >= listItem.ItemsCount) + { + Notify.Error("Invalid input"); + return; + } + + listItem.ListIndex = drawableId; + + if (drawableId == -1) + { + ClearPedProp(Game.PlayerPed.Handle, propIndex); + currentCharacter.PropVariations.props[propIndex] = + new KeyValuePair(-1, 0); + return; + } +} + +// --- Normal Selection --- +int listIndex = listItem.ListIndex; + +if (listIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) + return; + +int textureIndex = + GetPedPropTextureIndex(Game.PlayerPed.Handle, propIndex); + +int maxTextures = + GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, listIndex); + +int newTextureIndex = + (textureIndex + 1 >= maxTextures) ? 0 : textureIndex + 1; + +// Apply Prop +SetPedPropIndex(Game.PlayerPed.Handle, propIndex, listIndex, newTextureIndex, true); + +// Ensure dictionary exists +currentCharacter.PropVariations.props ??= + new Dictionary>(); + +currentCharacter.PropVariations.props[propIndex] = + new KeyValuePair(listIndex, newTextureIndex); + +// --- Save With Collection --- +string collectionName = + GetPedCollectionNameFromProp(Game.PlayerPed.Handle, propIndex, listIndex); + +if (string.IsNullOrEmpty(collectionName)) + collectionName = "base"; + +currentCharacter.PropVariations.propsWithCollection ??= + new Dictionary(); + +currentCharacter.PropVariations.propsWithCollection[propIndex] = + new CharacterPropData + { + PropIndex = propIndex, + Drawable = + GetPedCollectionLocalIndexFromProp( + Game.PlayerPed.Handle, + propIndex, + listIndex), + Texture = newTextureIndex, + Collection = collectionName + }; + +// --- Update UI Description --- +listItem.Description = + $"Select a prop using arrow keys.\n" + + $"Press ~o~CTRL~s~ to manually enter ID.\n" + + $"Press ~o~ENTER~s~ to cycle textures.\n" + + $"Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; }; void ChangePropListItem(int itemIndex, int newSelectionIndex, MenuListItem listItem) @@ -1663,72 +1677,75 @@ void ChangePropListItem(int itemIndex, int newSelectionIndex, MenuListItem listI int propIndex = itemIndex; - if (newSelectionIndex == -1) - { - ClearPedProp(Game.PlayerPed.Handle, propIndex); - - currentCharacter.PropVariations.props[propIndex] = - new KeyValuePair(-1, 0); - - return; - } - - if (newSelectionIndex < 0 || - newSelectionIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) - return; - - int newTextureIndex = 0; + if (newSelectionIndex == -1) + { + ClearPedProp(Game.PlayerPed.Handle, propIndex); + + currentCharacter.PropVariations.props[propIndex] = + new KeyValuePair(-1, 0); + + return; + } + + if (newSelectionIndex < 0 || + newSelectionIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) + return; + + int newTextureIndex = 0; + + // Apply prop + SetPedPropIndex( + Game.PlayerPed.Handle, + propIndex, + newSelectionIndex, + newTextureIndex, + true + ); + + // Ensure dictionary exists + currentCharacter.PropVariations.props ??= + new Dictionary>(); + + currentCharacter.PropVariations.props[propIndex] = + new KeyValuePair(newSelectionIndex, newTextureIndex); - SetPedPropIndex( + // ---- COLLECTION SUPPORT ---- + string collectionName = + GetPedCollectionNameFromProp( + Game.PlayerPed.Handle, + propIndex, + newSelectionIndex + ); + + if (string.IsNullOrEmpty(collectionName)) + collectionName = "base"; + + currentCharacter.PropVariations.propsWithCollection ??= + new Dictionary(); + + currentCharacter.PropVariations.propsWithCollection[propIndex] = + new CharacterPropData + { + PropIndex = propIndex, + Drawable = + GetPedCollectionLocalIndexFromProp( Game.PlayerPed.Handle, propIndex, - newSelectionIndex, - newTextureIndex, - true - ); + newSelectionIndex), + Texture = newTextureIndex, + Collection = collectionName + }; - currentCharacter.PropVariations.props ??= - new Dictionary>(); - - currentCharacter.PropVariations.props[propIndex] = - new KeyValuePair(newSelectionIndex, newTextureIndex); - - string collectionName = - GetPedCollectionNameFromProp( - Game.PlayerPed.Handle, - propIndex, - newSelectionIndex - ); - - if (string.IsNullOrEmpty(collectionName)) - collectionName = "base"; - - currentCharacter.PropVariations.propsWithCollection ??= - new Dictionary(); - - currentCharacter.PropVariations.propsWithCollection[propIndex] = - new CharacterPropData - { - PropIndex = propIndex, - Drawable = - GetPedCollectionLocalIndexFromProp( - Game.PlayerPed.Handle, - propIndex, - newSelectionIndex), - Texture = newTextureIndex, - Collection = collectionName - }; - - int maxTextures = - GetNumberOfPedPropTextureVariations( - Game.PlayerPed.Handle, - propIndex, - newSelectionIndex); - - listItem.Description = - $"Press ~o~CTRL~s~ to enter ID (-1 clears).\n" + - $"Press ~o~ENTER~s~ to cycle textures.\n" + - $"Current texture: #{newTextureIndex + 1} (of {maxTextures})."; + int maxTextures = + GetNumberOfPedPropTextureVariations( + Game.PlayerPed.Handle, + propIndex, + newSelectionIndex); + + listItem.Description = + $"Press ~o~CTRL~s~ to enter ID (-1 clears).\n" + + $"Press ~o~ENTER~s~ to cycle textures.\n" + + $"Current texture: #{newTextureIndex + 1} (of {maxTextures})."; } #endregion From dad2631e47d63b74f00c8a983895a7b43df4a5ac Mon Sep 17 00:00:00 2001 From: blackfirefly000 <50463948+blackfirefly000@users.noreply.github.com> Date: Tue, 17 Feb 2026 00:24:51 -0800 Subject: [PATCH 10/10] Revert "Merge branch 'custom-install-4' into Clothing-Collections" This reverts commit 4678023f54984f318ff247c8c6d9c36cd07bb7c0, reversing changes made to 347cac1a1fd0f604116d1b7d67e7148d13bc0170. --- vMenu/menus/MpPedCustomization.cs | 419 ++++++++++-------------------- vMenu/menus/PlayerAppearance.cs | 73 ++---- 2 files changed, 168 insertions(+), 324 deletions(-) diff --git a/vMenu/menus/MpPedCustomization.cs b/vMenu/menus/MpPedCustomization.cs index dcf220114..86ec017c6 100644 --- a/vMenu/menus/MpPedCustomization.cs +++ b/vMenu/menus/MpPedCustomization.cs @@ -1446,129 +1446,71 @@ int UnclampMix(float value) #endregion #region clothes - clothesMenu.OnListIndexChange += (_menu, listItem, oldSelectionIndex, newSelectionIndex, realIndex) => ChangeClothingListItem(realIndex, newSelectionIndex, listItem); - - clothesMenu.OnListItemSelect += async (sender, listItem, listIndex, realIndex) => + clothesMenu.OnListIndexChange += (_menu, listItem, oldSelectionIndex, newSelectionIndex, realIndex) => { int componentIndex = realIndex + 1; + if (realIndex > 0) + { + componentIndex += 1; + } -// --- CTRL Manual Drawable Selection --- -int controlIndex = 0; -bool isCtrlPressed = Game.IsControlPressed(controlIndex, Control.Duck); - -if (isCtrlPressed) -{ - string userInput = await GetUserInput("Enter Drawable ID", 5); - - if (string.IsNullOrEmpty(userInput) || - !int.TryParse(userInput, out int drawableId) || - drawableId < 0 || - drawableId >= listItem.ItemsCount) - { - Notify.Error("Invalid input"); - return; - } - - listItem.ListIndex = drawableId; - newSelectionIndex = drawableId; -} -else -{ - newSelectionIndex = listItem.ListIndex; -} - -// --- Apply Component --- -int newTextureIndex = 0; + int newTextureIndex = 0; -SetPedComponentVariation(Game.PlayerPed.Handle, componentIndex, newSelectionIndex, newTextureIndex, 0); + SetPedComponentVariation(Game.PlayerPed.Handle, componentIndex, newSelectionIndex, newTextureIndex, 0); -// Ensure dictionary exists -currentCharacter.DrawableVariations.clothes ??= new Dictionary>(); -currentCharacter.DrawableVariations.clothes[componentIndex] = - new KeyValuePair(newSelectionIndex, newTextureIndex); + currentCharacter.DrawableVariations.clothes ??= new Dictionary>(); + currentCharacter.DrawableVariations.clothes[componentIndex] = new KeyValuePair(newSelectionIndex, newTextureIndex); -// --- Save With Collection --- -string collectionName = - GetPedDrawableVariationCollectionName(Game.PlayerPed.Handle, componentIndex) ?? "base"; + string collectionName = GetPedDrawableVariationCollectionName(Game.PlayerPed.Handle, componentIndex) ?? "base"; -currentCharacter.DrawableVariations.clothesWithCollection ??= new(); + currentCharacter.DrawableVariations.clothesWithCollection ??= new(); + currentCharacter.DrawableVariations.clothesWithCollection[componentIndex] = new CharacterClothingData + { + ComponentIndex = componentIndex, + Drawable = GetPedDrawableVariationCollectionLocalIndex(componentIndex, newSelectionIndex), + Texture = newTextureIndex, + Collection = collectionName + }; -currentCharacter.DrawableVariations.clothesWithCollection[componentIndex] = - new CharacterClothingData - { - ComponentIndex = componentIndex, - Drawable = GetPedDrawableVariationCollectionLocalIndex(componentIndex, newSelectionIndex), - Texture = newTextureIndex, - Collection = collectionName - }; - -// --- Update UI Description --- -int maxTextures = - GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, newSelectionIndex); - -listItem.Description = - $"Select a drawable using arrow keys.\n" + - $"Press ~o~CTRL~s~ to manually enter ID.\n" + - $"Press ~o~ENTER~s~ to cycle textures.\n" + - $"Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; + int maxTextures = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, newSelectionIndex); + listItem.Description = $"Select a drawable using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; }; - void ChangeClothingListItem(int itemIndex, int newSelectionIndex, MenuListItem listItem) + clothesMenu.OnListItemSelect += (sender, listItem, listIndex, realIndex) => { - int componentIndex = itemIndex + 1; - - if (newSelectionIndex < 0 || - newSelectionIndex >= GetNumberOfPedDrawableVariations(Game.PlayerPed.Handle, componentIndex)) - return; - - int newTextureIndex = 0; - - // Apply to ped (client-sided preview) - SetPedComponentVariation( - Game.PlayerPed.Handle, - componentIndex, - newSelectionIndex, - newTextureIndex, - 0 - ); - - // Ensure dictionary exists - currentCharacter.DrawableVariations.clothes ??= - new Dictionary>(); - - currentCharacter.DrawableVariations.clothes[componentIndex] = - new KeyValuePair(newSelectionIndex, newTextureIndex); + var componentIndex = realIndex + 1; // skip face options as that fucks up with inheritance faces + if (realIndex > 0) // skip hair features as that is done in the appeareance menu + { + componentIndex += 1; + } - // ---- COLLECTION SUPPORT ---- - string collectionName = - GetPedDrawableVariationCollectionName(Game.PlayerPed.Handle, componentIndex) ?? "base"; + int textureIndex = GetPedTextureVariation(Game.PlayerPed.Handle, componentIndex); + int newTextureIndex = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, listIndex) - 1 < textureIndex + 1 ? 0 : textureIndex + 1; + + SetPedComponentVariation(Game.PlayerPed.Handle, componentIndex, listIndex, newTextureIndex, 0); - currentCharacter.DrawableVariations.clothesWithCollection ??= new(); + currentCharacter.DrawableVariations.clothes ??= new Dictionary>(); + currentCharacter.DrawableVariations.clothes[componentIndex] = new KeyValuePair(listIndex, newTextureIndex); - currentCharacter.DrawableVariations.clothesWithCollection[componentIndex] = - new CharacterClothingData - { - ComponentIndex = componentIndex, - Drawable = - GetPedDrawableVariationCollectionLocalIndex(componentIndex, newSelectionIndex), - Texture = newTextureIndex, - Collection = collectionName - }; + string collectionName = GetPedDrawableVariationCollectionName(Game.PlayerPed.Handle, componentIndex); + if (string.IsNullOrEmpty(collectionName)) collectionName = "base"; - int maxTextures = - GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, newSelectionIndex); + currentCharacter.DrawableVariations.clothesWithCollection ??= new Dictionary(); + currentCharacter.DrawableVariations.clothesWithCollection[componentIndex] = new CharacterClothingData + { + ComponentIndex = componentIndex, + Drawable = GetPedDrawableVariationCollectionLocalIndex(componentIndex, listIndex), + Texture = newTextureIndex, + Collection = collectionName + }; - listItem.Description = - $"Press ~o~CTRL~s~ to enter ID.\n" + - $"Press ~o~ENTER~s~ to cycle textures.\n" + - $"Current texture: #{newTextureIndex + 1} (of {maxTextures})."; - } + int maxTextures = GetNumberOfPedTextureVariations(Game.PlayerPed.Handle, componentIndex, listIndex); + listItem.Description = $"Select a drawable using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; + }; #endregion #region props - propsMenu.OnListIndexChange += (_menu, listItem, oldSelectionIndex, newSelectionIndex, realIndex) => ChangePropListItem(realIndex, newSelectionIndex, listItem); - - propsMenu.OnListItemSelect += async (sender, listItem, listIndex, realIndex) => + propsMenu.OnListIndexChange += (_menu, listItem, oldSelectionIndex, newSelectionIndex, realIndex) => { int propIndex = realIndex; if (realIndex == 3) @@ -1579,174 +1521,120 @@ void ChangeClothingListItem(int itemIndex, int newSelectionIndex, MenuListItem l { propIndex = 7; } - // --- CTRL Manual Drawable Selection --- -int controlIndex = 0; -bool isCtrlPressed = Game.IsControlPressed(controlIndex, Control.Duck); - -if (isCtrlPressed) -{ - string userInput = await GetUserInput("Enter Prop ID (-1 to clear)", 5); - - if (string.IsNullOrEmpty(userInput) || - !int.TryParse(userInput, out int drawableId) || - drawableId < -1 || - drawableId >= listItem.ItemsCount) - { - Notify.Error("Invalid input"); - return; - } - - listItem.ListIndex = drawableId; - - if (drawableId == -1) - { - ClearPedProp(Game.PlayerPed.Handle, propIndex); - currentCharacter.PropVariations.props[propIndex] = - new KeyValuePair(-1, 0); - return; - } -} - -// --- Normal Selection --- -int listIndex = listItem.ListIndex; + int textureIndex = 0; -if (listIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) - return; + int maxDrawables = GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex); + bool isNoProp = newSelectionIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex); -int textureIndex = - GetPedPropTextureIndex(Game.PlayerPed.Handle, propIndex); + currentCharacter.PropVariations.props ??= new Dictionary>(); + currentCharacter.PropVariations.propsWithCollection ??= new Dictionary(); -int maxTextures = - GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, listIndex); - -int newTextureIndex = - (textureIndex + 1 >= maxTextures) ? 0 : textureIndex + 1; - -// Apply Prop -SetPedPropIndex(Game.PlayerPed.Handle, propIndex, listIndex, newTextureIndex, true); - -// Ensure dictionary exists -currentCharacter.PropVariations.props ??= - new Dictionary>(); - -currentCharacter.PropVariations.props[propIndex] = - new KeyValuePair(listIndex, newTextureIndex); + if (isNoProp) + { + SetPedPropIndex(Game.PlayerPed.Handle, propIndex, -1, -1, false); + ClearPedProp(Game.PlayerPed.Handle, propIndex); + currentCharacter.PropVariations.props ??= new Dictionary>(); + currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(-1, -1); + currentCharacter.PropVariations.propsWithCollection ??= new Dictionary(); + currentCharacter.PropVariations.propsWithCollection[propIndex] = new CharacterPropData + { + PropIndex = propIndex, + Drawable = -1, + Texture = -1, + Collection = "base" + }; -// --- Save With Collection --- -string collectionName = - GetPedCollectionNameFromProp(Game.PlayerPed.Handle, propIndex, listIndex); + listItem.Description = "Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."; + } + else + { + SetPedPropIndex(Game.PlayerPed.Handle, propIndex, newSelectionIndex, textureIndex, true); + currentCharacter.PropVariations.props ??= new Dictionary>(); + currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(newSelectionIndex, textureIndex); -if (string.IsNullOrEmpty(collectionName)) - collectionName = "base"; + string collectionName = GetPedCollectionNameFromProp(Game.PlayerPed.Handle, propIndex, newSelectionIndex); + if (string.IsNullOrEmpty(collectionName)) collectionName = "base"; -currentCharacter.PropVariations.propsWithCollection ??= - new Dictionary(); + currentCharacter.PropVariations.propsWithCollection ??= new Dictionary(); + currentCharacter.PropVariations.propsWithCollection[propIndex] = new CharacterPropData + { + PropIndex = propIndex, + Drawable = GetPedCollectionLocalIndexFromProp(Game.PlayerPed.Handle, propIndex, newSelectionIndex), + Texture = textureIndex, + Collection = collectionName + }; -currentCharacter.PropVariations.propsWithCollection[propIndex] = - new CharacterPropData - { - PropIndex = propIndex, - Drawable = - GetPedCollectionLocalIndexFromProp( - Game.PlayerPed.Handle, - propIndex, - listIndex), - Texture = newTextureIndex, - Collection = collectionName - }; - -// --- Update UI Description --- -listItem.Description = - $"Select a prop using arrow keys.\n" + - $"Press ~o~CTRL~s~ to manually enter ID.\n" + - $"Press ~o~ENTER~s~ to cycle textures.\n" + - $"Currently selected texture: #{newTextureIndex + 1} (of {maxTextures})."; + if (GetPedPropIndex(Game.PlayerPed.Handle, propIndex) == -1) + { + listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."; + } + else + { + int maxPropTextures = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, newSelectionIndex); + listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{textureIndex + 1} (of {maxPropTextures})."; + } + } }; - void ChangePropListItem(int itemIndex, int newSelectionIndex, MenuListItem listItem) + propsMenu.OnListItemSelect += (sender, listItem, listIndex, realIndex) => { - int propIndex = itemIndex; - if (itemIndex == 3) + var propIndex = realIndex; + if (realIndex == 3) { propIndex = 6; } - if (itemIndex == 4) + if (realIndex == 4) { propIndex = 7; } - int propIndex = itemIndex; - - if (newSelectionIndex == -1) - { - ClearPedProp(Game.PlayerPed.Handle, propIndex); - - currentCharacter.PropVariations.props[propIndex] = - new KeyValuePair(-1, 0); - - return; - } - - if (newSelectionIndex < 0 || - newSelectionIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) - return; - - int newTextureIndex = 0; - - // Apply prop - SetPedPropIndex( - Game.PlayerPed.Handle, - propIndex, - newSelectionIndex, - newTextureIndex, - true - ); - - // Ensure dictionary exists - currentCharacter.PropVariations.props ??= - new Dictionary>(); - - currentCharacter.PropVariations.props[propIndex] = - new KeyValuePair(newSelectionIndex, newTextureIndex); - - // ---- COLLECTION SUPPORT ---- - string collectionName = - GetPedCollectionNameFromProp( - Game.PlayerPed.Handle, - propIndex, - newSelectionIndex - ); - - if (string.IsNullOrEmpty(collectionName)) - collectionName = "base"; - - currentCharacter.PropVariations.propsWithCollection ??= - new Dictionary(); - - currentCharacter.PropVariations.propsWithCollection[propIndex] = - new CharacterPropData - { - PropIndex = propIndex, - Drawable = - GetPedCollectionLocalIndexFromProp( - Game.PlayerPed.Handle, - propIndex, - newSelectionIndex), - Texture = newTextureIndex, - Collection = collectionName - }; + int textureIndex = GetPedPropTextureIndex(Game.PlayerPed.Handle, propIndex); + int newTextureIndex = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, listIndex) - 1 < textureIndex + 1 ? 0 : textureIndex + 1; + if (listIndex >= GetNumberOfPedPropDrawableVariations(Game.PlayerPed.Handle, propIndex)) + { + SetPedPropIndex(Game.PlayerPed.Handle, propIndex, -1, -1, false); + ClearPedProp(Game.PlayerPed.Handle, propIndex); + currentCharacter.PropVariations.props ??= new Dictionary>(); + currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(-1, -1); + currentCharacter.PropVariations.propsWithCollection ??= new Dictionary(); + currentCharacter.PropVariations.propsWithCollection[propIndex] = new CharacterPropData + { + PropIndex = propIndex, + Drawable = -1, + Texture = -1, + Collection = "base" + }; + listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."; + } + else + { + SetPedPropIndex(Game.PlayerPed.Handle, propIndex, listIndex, newTextureIndex, true); + currentCharacter.PropVariations.props ??= new Dictionary>(); + currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(listIndex, newTextureIndex); - int maxTextures = - GetNumberOfPedPropTextureVariations( - Game.PlayerPed.Handle, - propIndex, - newSelectionIndex); + string collectionName = GetPedCollectionNameFromProp(Game.PlayerPed.Handle, propIndex, listIndex); + if (string.IsNullOrEmpty(collectionName)) collectionName = "base"; - listItem.Description = - $"Press ~o~CTRL~s~ to enter ID (-1 clears).\n" + - $"Press ~o~ENTER~s~ to cycle textures.\n" + - $"Current texture: #{newTextureIndex + 1} (of {maxTextures})."; - } + currentCharacter.PropVariations.props[propIndex] = new KeyValuePair(listIndex, newTextureIndex); + currentCharacter.PropVariations.propsWithCollection[propIndex] = new CharacterPropData + { + PropIndex = propIndex, + Drawable = GetPedCollectionLocalIndexFromProp(Game.PlayerPed.Handle, propIndex, listIndex), + Texture = newTextureIndex, + Collection = collectionName + }; + if (GetPedPropIndex(Game.PlayerPed.Handle, propIndex) == -1) + { + listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures."; + } + else + { + var maxPropTextures = GetNumberOfPedPropTextureVariations(Game.PlayerPed.Handle, propIndex, listIndex); + listItem.Description = $"Select a prop using the arrow keys and press ~o~enter~s~ to cycle through all available textures. Currently selected texture: #{newTextureIndex + 1} (of {maxPropTextures})."; + } + } + //propsMenu.UpdateScaleform(); + }; #endregion #region face shape data @@ -1942,31 +1830,10 @@ void ApplySavedTattoos() } }; - tattoosMenu.OnListItemSelect += async (sender, item, tattooIndex, menuIndex) => + tattoosMenu.OnListItemSelect += (sender, item, tattooIndex, menuIndex) => { CreateListsIfNull(); - int controlIndex = 0; - bool isBadgesItem = menuIndex == 6; - bool isCtrlPressed = Game.IsControlPressed(controlIndex, Control.Duck); - - if (isCtrlPressed) - { - string userInput = await GetUserInput($"Enter {(isBadgesItem ? "Badge" : "Tattoo")} ID", 5); - - if (string.IsNullOrEmpty(userInput) || !int.TryParse(userInput, out int drawableId) || drawableId < 1 || drawableId > item.ItemsCount) - { - Notify.Error("Invalid input"); - return; - } - - drawableId--; - - item.ListIndex = drawableId; - - tattooIndex = drawableId; - } - if (menuIndex == 0) // head { var Tattoo = currentCharacter.IsMale ? MaleTattoosCollection.HEAD.ElementAt(tattooIndex) : FemaleTattoosCollection.HEAD.ElementAt(tattooIndex); @@ -2057,7 +1924,7 @@ void ApplySavedTattoos() currentCharacter.PedTatttoos.RightLegTattoos.Add(tat); } } - else if (isBadgesItem) + else if (menuIndex == 6) // badges { var Tattoo = currentCharacter.IsMale ? MaleTattoosCollection.BADGES.ElementAt(tattooIndex) : FemaleTattoosCollection.BADGES.ElementAt(tattooIndex); var tat = new KeyValuePair(Tattoo.collectionName, Tattoo.name); diff --git a/vMenu/menus/PlayerAppearance.cs b/vMenu/menus/PlayerAppearance.cs index cb37a8547..ed7dada5a 100644 --- a/vMenu/menus/PlayerAppearance.cs +++ b/vMenu/menus/PlayerAppearance.cs @@ -540,34 +540,37 @@ async void SpawnPed(Menu m, MenuItem item, int index) #region ped drawable list changes // Manage list changes. - pedCustomizationMenu.OnListIndexChange += (sender, item, oldListIndex, newListIndex, itemIndex) => ChangeListItem(item, newListIndex); - - // Manage list selections. - pedCustomizationMenu.OnListItemSelect += async (sender, item, listIndex, itemIndex) => + pedCustomizationMenu.OnListIndexChange += (sender, item, oldListIndex, newListIndex, itemIndex) => { - int controlIndex = 0; - bool isDrawable = drawablesMenuListItems.ContainsKey(item); - bool isCtrlPressed = Game.IsControlPressed(controlIndex, Control.Duck); - - if (isCtrlPressed) + if (drawablesMenuListItems.ContainsKey(item)) { - string userInput = await GetUserInput($"Enter {(isDrawable ? "Drawable" : "Prop")} ID", 5); - - if (string.IsNullOrEmpty(userInput) || !int.TryParse(userInput, out int drawableId) || drawableId < 1 || drawableId > item.ItemsCount) + var drawableID = drawablesMenuListItems[item]; + SetPedComponentVariation(Game.PlayerPed.Handle, drawableID, newListIndex, 0, 0); + } + else if (propsMenuListItems.ContainsKey(item)) + { + var propID = propsMenuListItems[item]; + if (newListIndex == 0) { - Notify.Error("Invalid input"); - return; + SetPedPropIndex(Game.PlayerPed.Handle, propID, newListIndex - 1, 0, false); + ClearPedProp(Game.PlayerPed.Handle, propID); + } + else + { + SetPedPropIndex(Game.PlayerPed.Handle, propID, newListIndex - 1, 0, true); + } + if (propID == 0) + { + ShowVisorText(Game.PlayerPed.Handle); } - drawableId--; - - item.ListIndex = drawableId; - - ChangeListItem(item, drawableId); - return; } + }; - if (isDrawable) + // Manage list selections. + pedCustomizationMenu.OnListItemSelect += (sender, item, listIndex, itemIndex) => + { + if (drawablesMenuListItems.ContainsKey(item)) // drawable { var currentDrawableID = drawablesMenuListItems[item]; var currentTextureIndex = GetPedTextureVariation(Game.PlayerPed.Handle, currentDrawableID); @@ -582,7 +585,7 @@ async void SpawnPed(Menu m, MenuItem item, int index) SetPedComponentVariation(Game.PlayerPed.Handle, currentDrawableID, listIndex, newTexture, 0); } - else // prop + else if (propsMenuListItems.ContainsKey(item)) // prop { var currentPropIndex = propsMenuListItems[item]; var currentPropVariationIndex = GetPedPropIndex(Game.PlayerPed.Handle, currentPropIndex); @@ -593,32 +596,6 @@ async void SpawnPed(Menu m, MenuItem item, int index) SetPedPropIndex(Game.PlayerPed.Handle, currentPropIndex, currentPropVariationIndex, newPropTextureVariationIndex, true); } }; - - void ChangeListItem(MenuListItem item, int newListIndex) - { - if (drawablesMenuListItems.ContainsKey(item)) - { - var drawableID = drawablesMenuListItems[item]; - SetPedComponentVariation(Game.PlayerPed.Handle, drawableID, newListIndex, 0, 0); - } - else if (propsMenuListItems.ContainsKey(item)) - { - var propID = propsMenuListItems[item]; - if (newListIndex == 0) - { - SetPedPropIndex(Game.PlayerPed.Handle, propID, newListIndex - 1, 0, false); - ClearPedProp(Game.PlayerPed.Handle, propID); - } - else - { - SetPedPropIndex(Game.PlayerPed.Handle, propID, newListIndex - 1, 0, true); - } - if (propID == 0) - { - ShowVisorText(Game.PlayerPed.Handle); - } - } - } #endregion pedCollectionsCustomizationMenu.OnListIndexChange += (_, item, oldListIndex, newListIndex, ___) =>