// **************************************************************************************** // PresetPrepare Plugin by dynms1337 // Licensed under CC BY // **************************************************************************************** using System; using UnityEngine; using System.Collections.Generic; using SimpleJSON; using System.Linq; using MacGruber; using MeshVR; using System.Text.RegularExpressions; namespace dynms1337 { public partial class CharacterFusioner : MVRScript { protected JSONStorableBool isFusionSkin; protected JSONStorableBool isFusionClothing; protected JSONStorableBool isFusionHair; protected JSONStorableBool isFusionGlutePhysics; protected JSONStorableBool isFusionBreastPhysics; protected JSONStorableStringChooser skinChooser; protected JSONStorableStringChooser clothingChooser; protected JSONStorableStringChooser hairChooser; protected JSONStorableStringChooser glutePhysicsChooser; protected JSONStorableStringChooser breastPhysicsChooser; protected Dictionary presetChooserDic = new Dictionary(); public void Init_Preset(bool isRightSide) { clothingChooser = dUtil.CreateChooser(this, "Clothing", characters.Select(c => c.name).ToList(), startingValue: null, rightSide: isRightSide, callback: s => { load_presetCache("ClothingPresets", s.val); }); hairChooser = dUtil.CreateChooser(this, "Hair", characters.Select(c => c.name).ToList(), startingValue: null, rightSide: isRightSide, callback: s => { load_presetCache("HairPresets", s.val); }); skinChooser = dUtil.CreateChooser(this, "Skin", characters.Select(c => c.name).ToList(), startingValue: null, rightSide: isRightSide, callback: s => { load_presetCache("SkinPresets", s.val); }); breastPhysicsChooser = dUtil.CreateChooser(this, "F BreastPhysics", characters.Select(c => c.name).ToList(), startingValue: null, rightSide: isRightSide, callback: s => { load_presetCache("FemaleBreastPhysicsPresets", s.val); }); glutePhysicsChooser = dUtil.CreateChooser(this, "F GlutePhysics", characters.Select(c => c.name).ToList(), startingValue: null, rightSide: isRightSide, callback: s => { load_presetCache("FemaleGlutePhysicsPresets", s.val); }); } public void Init_PresetSwitch() { isFusionClothing = dUtil.CreateToggle(this, "Clothing", true, callback: (bool b) => { }); isFusionHair = dUtil.CreateToggle(this, "Hair", true, callback: (bool b) => { }); isFusionSkin = dUtil.CreateToggle(this, "Skin", true, callback: (bool b) => { }); // Physics Presets default off isFusionBreastPhysics = dUtil.CreateToggle(this, "BreastPhysics", false, callback: (bool b) => { }); isFusionGlutePhysics = dUtil.CreateToggle(this, "GlutePhysics", false, callback: (bool b) => { }); } /// /// Initialize preset fields. Call after init functions. /// public void Init_PresetPost() { presetChooserDic = new Dictionary(){ { clothingChooser, isFusionClothing }, { hairChooser, isFusionHair }, { skinChooser, isFusionSkin }, { breastPhysicsChooser, isFusionBreastPhysics }, { glutePhysicsChooser, isFusionGlutePhysics }, }; } public void update_Preset() { presetChooserDic.ToList().ForEach(kvp => kvp.Key.choices = allCharacters.Select(c => c.name).ToList()); } // ********************* Preset ********************* /// /// /// /// "AppearancePresets", "SkinPresets","ClothingPresets" /// public void saveCurrentPreset(string preset, string characterName, bool doScreenshot=false) { PresetManager pm = person.GetStorableByID(preset).GetComponentInChildren(); string tmpName = pm.presetName; pm.presetName = $"{characterName}"; string tmpFolder = pm.storeFolderName; pm.storeFolderName = "Appearance"; pm.StorePreset(doScreenshot: doScreenshot); pm.storeFolderName = tmpFolder; pm.presetName = tmpName; } private void load_presetCache(string preset, string characterName) { if (person == null) return; // for RestoreJSON // FIXED: Look up the character to get the full appearance path CharacterProfile character = allCharacters.FirstOrDefault(c => c.name == characterName); if (character == null) { dUtil.error($"load_presetCache: Character '{characterName}' not found"); return; } var pm = person.GetStorableByID(preset).GetComponentInChildren(); string tmp = pm.storeFolderName; pm.storeFolderName = "Appearance"; // Use the stored appearance path string path = character.appearancePath; string pkgname = ""; // Extract package name if present (for VAR packages) if (path.Contains(":/") && !path.StartsWith("C:") && !path.StartsWith("D:") && !path.StartsWith("E:") && !path.StartsWith("F:")) { int colonIndex = path.IndexOf(":/"); pkgname = path.Substring(0, colonIndex + 1) + ":"; path = path.Substring(colonIndex + 2); } // Remove the VaM base directory and "Custom/Atom/Person/Appearance/" to get the relative path // Find the appearance directory in the path (not just at the start) string appearancePrefix = "Custom/Atom/Person/Appearance/"; string appearancePrefix2 = "Custom\\Atom\\Person\\Appearance\\"; int prefixIndex = path.IndexOf(appearancePrefix); if (prefixIndex < 0) prefixIndex = path.IndexOf(appearancePrefix2); if (prefixIndex >= 0) { // Extract everything after "Custom/Atom/Person/Appearance/" int startIndex = prefixIndex + (path[prefixIndex + 6] == '/' ? appearancePrefix.Length : appearancePrefix2.Length); path = path.Substring(startIndex); } // Remove .vap extension if present if (path.EndsWith(".vap")) path = path.Substring(0, path.Length - 4); // Remove "Preset_" prefix from the filename while preserving directory structure int lastSlash = path.LastIndexOfAny(new char[] { '/', '\\' }); string filename = lastSlash >= 0 ? path.Substring(lastSlash + 1) : path; string directory = lastSlash >= 0 ? path.Substring(0, lastSlash) : ""; if (filename.StartsWith("Preset_")) { filename = filename.Substring(7); // Remove "Preset_" path = string.IsNullOrEmpty(directory) ? filename : directory + "/" + filename; } // The preset name should be: {package}:{relativePath from Appearance folder} pm.presetName = $"{pkgname}{path}"; person.GetStorableByID(preset).CallAction("LoadPreset"); pm.storeFolderName = tmp; } } }