using System; using System.Collections; using System.Collections.Generic; using System.Linq; using MacGruber; using MVR.FileManagementSecure; using SimpleJSON; using UnityEngine; using UnityEngine.UI; namespace CheesyFX { public class PoseMe : MVRScript { public static PoseMe singleton; public static string libraryPath = $"Saves/PluginData/CheesyFX/BodyLanguage/PoseMe/Library/"; public static string backUpStorePath = $"Saves/PluginData/CheesyFX/BodyLanguage/PoseMe/Backups/"; public static string tmpPath = "Saves/PluginData/CheesyFX/BodyLanguage/PoseMe/tmp/"; public static Pose currentPose; // private static string scenename; public static JSONStorableBool manageForces = new JSONStorableBool("Store & Restore Forces", true); public static JSONStorableBool restoreRoot = new JSONStorableBool("Restore Root", true); public static JSONStorableBool restoreHeadRotation = new JSONStorableBool("Restore Head Rotation", true); public static JSONStorableBool applyForces = new JSONStorableBool("Apply Idle Forces", false, val => { currentPose.poseForce.enabled = val; }); public static JSONStorableStringChooser camMode = new JSONStorableStringChooser("camMode", new List{"None", "Snap", "Linear", "Exponential"}, "Exponential", "Cam Mode"); public static JSONStorableFloat smoothCamSpeed = new JSONStorableFloat("Smooth Cam Speed", 1.5f, .2f, 4f, true); private static JSONStorableBool autoBackUpDeleted = new JSONStorableBool("Auto BackUp Deleted", false); public static JSONStorableBool showButtons = new JSONStorableBool("Show Buttons", true, val => canvas.gameObject.SetActive(val)); public static JSONStorableBool showThumbnails = new JSONStorableBool("Show Thumbnails", true, OnShowThumbnailChanged); public static List poses = new List(); public static JSONStorableStringChooser poseChooser = new JSONStorableStringChooser("Apply Pose", new List(), "", "Pose", ApplyPose); private static JSONStorableAction reapplyCurrent; private static JSONStorableAction nextPose; private static JSONStorableAction previousPose; private Transform confirmCanvas; public static List dildos = new List(); public static string sceneType; public static IEnumerator applyPosePost; public static List softPhysicsStates = new List(); public static List persons => FillMeUp.persons; private UnityEventsListener uiListener; private float screenWidth; private float screenHeight; private static UIDynamicLabelInput sceneNameInput; private IOSystem ioSystem = new IOSystem(); public static Atom atom; public static UIDynamicButton onPoseEnterTriggerButton; public static UIDynamicButton onPoseExitTriggerButton; public static GameObject canvas; private JSONStorableFloat posX = new JSONStorableFloat("Pos X", 1f, 0f, 1f); private JSONStorableFloat posY = new JSONStorableFloat("Pos Y", 1f, 0f, 1f); public static JSONStorableFloat buttonSizeJ = new JSONStorableFloat("Button Size", 300f, SyncButtonSize,0f, 1000f); public static JSONStorableFloat buttonTransparency = new JSONStorableFloat("Button Transparency", .5f, val => { Pose.deselectedColor.a = val; poses.ForEach(x => x.selected = x.selected); }, 0f, 1f); private static float buttonSize; public static JSONStorableFloat maxRows = new JSONStorableFloat("MaxRows", 10f, (float val) => LayoutButtons(), 1f, 50f, false); private static float buttonWidth = buttonSizeJ.defaultVal; private static float buttonHeight = buttonSizeJ.defaultVal; private static float buttonSpacing = 20f; public static Image previewImage; public static JSONStorableString actorsJ = new JSONStorableString("actors", ""); public static List forceTargets = new List(); public static Transform cameraRig; public static Transform camera; public static bool isVR; private void OnEnable() { if(canvas != null) canvas.gameObject.SetActive(true); if(currentPose != null) currentPose.selected = true; } public override void Init() { try { singleton = this; atom = containingAtom; // scenename = GetSceneName(); UIManager.SetScript(this, CreateUIElement, leftUIElements, rightUIElements); Utils.OnInitUI(CreateUIElement); // FileManagerSecure.CreateDirectory(managedStorePath); FileManagerSecure.CreateDirectory(libraryPath); FileManagerSecure.CreateDirectory(backUpStorePath); FileManagerSecure.CreateDirectory(tmpPath); this.SetupButton("Add Pose", false, () => AddPose()); this.SetupButton("Update Pose", false, UpdatePose); this.SetupButton("Move Pose Up", false, () => MovePose(true)); this.SetupButton("Move Pose Down", false, () => MovePose(false)); poseChooser.CreateUI(rightSide: true, chooserType: 0); camMode.CreateUI(chooserType:0); smoothCamSpeed.CreateUI(); applyForces.CreateUI(); manageForces.CreateUI(); restoreHeadRotation.CreateUI(); restoreRoot.CreateUI(); showButtons.CreateUI(); showThumbnails.CreateUI(); autoBackUpDeleted.CreateUI(); ((UIDynamicSlider)maxRows.CreateUI(rightSide: true)).slider.wholeNumbers = true; buttonSizeJ.CreateUI(rightSide:true); buttonTransparency.CreateUI(rightSide: true); posX.CreateUI(rightSide: false); // posY.CreateUI(rightSide: true); onPoseEnterTriggerButton = CreateButton("On Pose Enter", true); onPoseEnterTriggerButton.buttonColor = new Color(0.45f, 1f, 0.45f); onPoseExitTriggerButton = CreateButton("On Pose Exit", true); onPoseExitTriggerButton.buttonColor = new Color(0.45f, 1f, 0.45f); var button = this.SetupButton("", true, () => currentPose.Apply()); button.ForceHeight(512f); var cb = button.button.colors; cb.pressedColor = cb.highlightedColor = cb.normalColor; button.button.colors = cb; previewImage = button.GetComponentInChildren(); previewImage.color = Color.white; sceneType = GetSceneContent(true); posX.setCallbackFunction += val => PositionCanvas(posX.val, posY.val); posY.setCallbackFunction += val => PositionCanvas(posX.val, posY.val); SetCameraRig(); if (isVR) camMode.val = "Snap"; // SuperController.singleton.foc // SuperController.singleton.FocusOnController(containingAtom.mainController); SuperController.singleton.onAtomAddedHandlers += OnAtomsChanged; SuperController.singleton.onAtomRemovedHandlers += OnAtomsChanged; SuperController.singleton.onAtomUIDRenameHandlers += OnAtomRename; screenWidth = Screen.width; screenHeight = Screen.height; InitCanvas(); ioSystem.saveDir = libraryPath; // ioSystem.defaultName = $"/{sceneType}/{scenename}/MyPose"; ioSystem.store = () => { currentPose.UpdateTriggers(); return currentPose.pose; }; ioSystem.onSaved += val => { currentPose.StoreImage(val); }; ioSystem.onLoaded += (path, jc) => { LoadPoseFromJSON(jc); }; this.SetupButton("Save Pose To Library", false, () => { if(currentPose == null) return; var scenename = sceneName; if(scenename == "") ioSystem.defaultName = $"/{sceneType}/MyScene/{currentPose.id:000}"; else ioSystem.defaultName = $"/{sceneType}/{scenename}/{currentPose.id:000}"; ioSystem.UISaveJSONDialog(); }); ioSystem.RegisterLoadButton(this.SetupButton("Import Pose From Library").button); this.SetupButton("Reload From Last Saved Scene", false, () => ReloadFromScene()); var twinButton = Utils.SetupTwinButton(this, "Delete Pose", DeletePose, "Clear All Poses", ClearPoses, false); twinButton.buttonLeft.GetComponentInChildren().color = new Color(1f, 0.36f, 0.32f); twinButton.buttonRight.GetComponentInChildren().color = new Color(1f, 0.36f, 0.32f); // this.SetupButton("Delete Pose", false, DeletePose).button.GetComponentInChildren().color = new Color(1f, 0.36f, 0.32f); // this.SetupButton("Clear All Poses", false, ClearPoses).button.GetComponentInChildren().color = new Color(1f, 0.36f, 0.32f);; CreateTextField(actorsJ).ForceHeight(75f); CreateTextField(new JSONStorableString("warning", "Use ONLY ONE (1) instance of PoseMe per scene! All actors will be posed automatically."), true) .ForceHeight(100f); reapplyCurrent = new JSONStorableAction("Reapply Current Pose", () => currentPose.Apply()); nextPose = new JSONStorableAction("Next Pose", () => { var pose = poses.FirstOrDefault(x => x.id > currentPose.id); pose?.Apply(); }); previousPose = new JSONStorableAction("Previous Pose", () => { var pose = poses.LastOrDefault(x => x.id < currentPose.id); pose?.Apply(); }); foreach (var rb in atom.rigidbodies) { if( rb.name == "hip" || rb.name == "chest" || rb.name == "head" || rb.name == "lThigh" || rb.name == "rThigh" || rb.name == "lShin" || rb.name == "rShin" || rb.name == "lShldr" || rb.name == "rShldr" || rb.name == "lHand" || rb.name == "rHand" || // rb.name == "lForeArm" || // rb.name == "rForeArm" || rb.name == "lFoot" || rb.name == "rFoot" ) forceTargets.Add(rb); } // forceTargets.ForEach(x => x.Print()); RegisterStringChooser(poseChooser); RegisterStringChooser(camMode); RegisterFloat(smoothCamSpeed); RegisterFloat(maxRows); RegisterFloat(buttonSizeJ); RegisterFloat(buttonTransparency); RegisterBool(restoreRoot); RegisterBool(manageForces); RegisterBool(restoreHeadRotation); RegisterBool(showButtons); RegisterBool(showThumbnails); RegisterAction(reapplyCurrent); RegisterAction(nextPose); RegisterAction(previousPose); // ReloadFromScene(); DeferredLoadFromScene().Start(); } catch (Exception e) { SuperController.LogError(e.ToString()); } } private IEnumerator DeferredLoadFromScene() { yield return FillMeUp.waitForPose; ReloadFromScene(true); } public override void InitUI() { base.InitUI(); if(UITransform == null || uiListener != null) return; uiListener = UITransform.gameObject.AddComponent(); uiListener.onEnabled.AddListener(() => UIManager.SetScript(this, CreateUIElement, leftUIElements, rightUIElements)); uiListener.onEnabled.AddListener(() => Utils.OnInitUI(CreateUIElement)); } private void Update() { if (poses.Count > 0) { if(screenWidth != Screen.width || screenHeight != Screen.height) { PositionCanvas(posX.val, posY.val); screenWidth = Screen.width; screenHeight = Screen.height; } for (int i = 0; i < poses.Count; i++) { var pose = poses[i]; pose.onPoseEnter.Update(); pose.onPoseExit.Update(); } if (Pose.smoothCamRunning && (Input.GetKeyDown(KeyCode.Mouse1) || Input.GetKeyDown(KeyCode.Mouse2) || Input.GetKeyDown(KeyCode.W) || Input.GetKeyDown(KeyCode.A) || Input.GetKeyDown(KeyCode.S) || Input.GetKeyDown(KeyCode.D))) { Pose.smoothCam.Stop(); Pose.smoothCamRunning = false; } } } private void OnDisable() { if(canvas != null) canvas.SetActive(false); Pose.smoothCam.Stop(); currentPose.selected = false; } private void OnDestroy() { Destroy(uiListener); SuperController.singleton.onAtomAddedHandlers -= OnAtomsChanged; SuperController.singleton.onAtomRemovedHandlers -= OnAtomsChanged; SuperController.singleton.onAtomUIDRenameHandlers -= OnAtomRename; Destroy(canvas); poses.ForEach(x => { x.onPoseEnter.Remove(); x.onPoseExit.Remove(); }); } private JSONArray Store() { var ja = new JSONArray(); for (int i = 0; i < poses.Count; i++) { poses[i].UpdateTriggers(); ja.Add(poses[i].pose); } return ja; } public override JSONClass GetJSON(bool includePhysical = true, bool includeAppearance = true, bool forceStore = false) { JSONClass jc = base.GetJSON(includePhysical, includeAppearance, true); jc["poses"] = Store(); return jc; } public override void LateRestoreFromJSON(JSONClass jc, bool restorePhysical = true, bool restoreAppearance = true, bool setMissingToDefault = true) { base.LateRestoreFromJSON(jc, restorePhysical, restoreAppearance, setMissingToDefault); if (!physicalLocked && restorePhysical && !IsCustomPhysicalParamLocked("trigger")) { needsStore = true; if (!jc.HasKey("poses")) return; var ja = jc["poses"].AsArray; foreach (JSONClass poseJc in ja.Childs) { LoadPoseFromJSON(poseJc, false); } camMode.Load(jc); smoothCamSpeed.Load(jc); restoreRoot.Load(jc); manageForces.Load(jc); restoreHeadRotation.Load(jc); showButtons.Load(jc); maxRows.Load(jc); buttonSizeJ.Load(jc); buttonTransparency.Load(jc); showThumbnails.Load(jc); OnShowThumbnailChanged(showThumbnails.val); if(poses.Count > 0) ApplyPose(jc["Apply Pose"].Value); } } private void ReloadFromScene(bool init = false) { var sceneName = SuperController.singleton.LoadedSceneName; if (sceneName == null) { if(!init) "Scene not loaded from a json.".Print(); return; } var sceneJson = SuperController.singleton.LoadJSON(sceneName)["atoms"].Childs .FirstOrDefault(x => x["id"].Value == containingAtom.uid); if (sceneJson == null) { if(!init) $"Atom {containingAtom.uid} not found in the loaded scene json. Did you rename the person?".Print(); return; } sceneJson = sceneJson["storables"].Childs. FirstOrDefault(x => x["id"].Value.EndsWith("CheesyFX.PoseMe")); if (sceneJson == null) { if(!init) $"Atom {containingAtom.uid} doesn't have PoseMe stored in the loaded scene json.".Print(); return; } // SuperController.singleton.LoadedSceneName.Print(); var pluginJSON = sceneJson.AsObject; if (!pluginJSON.HasKey("poses") || !pluginJSON["poses"].Childs.Any()) { if(!init) $"Scene {sceneName} doesn't have poses.".Print(); return; } ClearPoses(); LateRestoreFromJSON(pluginJSON, true, true, true); } public void OnAtomRename(string oldUid, string newUid) { poses.ForEach(x => { x.SyncAtomNames(oldUid, newUid); }); } private void OnAtomsChanged(Atom atom) { sceneType = GetSceneContent(); ioSystem.defaultName = $"/{sceneType}/MyPose"; } private static string GetSceneContent(bool init = false) { int femCount = 0; int futaCount = 0; int maleCount = 0; dildos.Clear(); foreach (var atom in SuperController.singleton.GetAtoms()) { if (atom.type == "Person") { var person = persons.First(x => x.uid == atom.uid); if (person.genderListener.dcs.gender == DAZCharacterSelector.Gender.Female) { if (person.genderListener.isFuta) futaCount++; else femCount++; } else maleCount++; } else if (atom.type == "Dildo") { dildos.Add(atom); } } var newsceneType = new string('F', femCount)+new string('M', maleCount)+new string('D', dildos.Count); if (!init && newsceneType != sceneType && poses.Count > 0) { "PoeseMe: Actors have changed. Please update your poses.".Print(); } return newsceneType; } private void LoadPoseFromJSON(JSONClass jc, bool apply = true) { var pose = AddPose(jc); if(apply) pose.Apply(); } private Pose AddPose(JSONClass jc = null) { int id; if (poses.Count == 0) id = 0; else id = poses.Select(x => x.id).Max()+1; Pose pose; if (jc != null) pose = new Pose(id, jc); else pose = new Pose(id); poses.Add(pose); poseChooser.choices = poses.Select(x => x.id.ToString()).ToList(); poseChooser.valNoCallback = poseChooser.choices.Last(); LayoutButtons(); return pose; } private static void MovePose(bool up) { int i; if (!int.TryParse(poseChooser.val, out i)) return; var pose = poses.FirstOrDefault(x => x.id == i); if (up) { if (i == 0) return; var upperPose = poses.LastOrDefault(x => x.id < pose.id); if (upperPose != null) { poses[i-1] = pose; poses[i] = upperPose; upperPose.id++; pose.id--; } poseChooser.valNoCallback = (i - 1).ToString(); } else { if(i == poses.Count - 1) return; var lowerPose = poses.FirstOrDefault(x => x.id > pose.id); if (lowerPose != null) { poses[i+1] = pose; poses[i] = lowerPose; lowerPose.id--; pose.id++; } } RenamePoses(); LayoutButtons(); poseChooser.valNoCallback = pose.id.ToString(); } private void ClearPoses() { foreach (var pose in poses) { pose.RemoveButton(); Destroy(pose.poseForce); } poseChooser.valNoCallback = ""; poseChooser.choices = new List(); previewImage.sprite = null; currentPose = null; poses.Clear(); } private static void DeletePose() { if (currentPose == null) return; poses.Remove(currentPose); currentPose.RemoveButton(); Destroy(currentPose.poseForce); poseChooser.choices = poses.Select(x => x.id.ToString()).ToList(); poseChooser.valNoCallback = ""; if(autoBackUpDeleted.val) currentPose.StoreBackup(); RenamePoses(); LayoutButtons(); previewImage.sprite = null; currentPose = null; } private static void RenamePoses() { for (int i = 0; i < poses.Count; i++) { poses[i].id = i; } } private static void UpdatePose() { var pose = poses[int.Parse(poseChooser.val)]; pose.Update(); } private static void ApplyPose(string id) { int i; if(int.TryParse(id, out i) && i < poses.Count){ var pose = poses[i]; pose.Apply(); } } private static void OnShowThumbnailChanged(bool val) { for (int i = 0; i < poses.Count; i++) { poses[i].SetButtonImage(val); } SyncButtonSize(buttonSizeJ.val); } private static void SyncButtonSize(float val) { buttonWidth = buttonHeight = showThumbnails.val? val : val * .5f; LayoutButtons(); } private static string sceneName { get { var loadedScenePath = SuperController.singleton.LoadedSceneName; if (loadedScenePath == null) return ""; var idx = loadedScenePath.LastIndexOf("/", StringComparison.Ordinal); if (idx == -1) return ""; return loadedScenePath.Substring(idx + 1).Replace(".json", ""); } } private void SetCameraRig() { if (SuperController.singleton.isOpenVR) { camera = SuperController.singleton.ViveCenterCamera.transform; isVR = true; } else if (SuperController.singleton.isOVR) { camera = SuperController.singleton.OVRCenterCamera.transform; isVR = true; } else { camera = SuperController.singleton.MonitorCenterCamera.transform; } cameraRig = camera.parent.parent; // cameraRig.Draw(); // // (SuperController.singleton.navigationRig == cameraRig).Print(); // cameraRig.transform.position.Print(); // camera.transform.position.Print(); } private static string GetScenePackageUid() { var loadedScenePath = SuperController.singleton.LoadedSceneName; if (loadedScenePath == null) return ""; var idx = loadedScenePath.IndexOf(":/", StringComparison.Ordinal); if (idx == -1) return ""; return loadedScenePath.Substring(0, idx+2); } public static IEnumerator CreateAtomCo(string type, string basename, Action onAtomCreated) { string uid = NewUID(basename); yield return SuperController.singleton.AddAtomByType(type, uid, true); var atom = SuperController.singleton.GetAtomByUid(uid); if(atom == null) { throw new NullReferenceException("atom was not created"); } SuperController.singleton.RenameAtom(atom, basename); onAtomCreated(atom); } public static string NewUID(string basename) { var uids = new HashSet(SuperController.singleton.GetAtomUIDs()); if(!uids.Contains(basename)) { return basename; } for(int i = 2; i < 1000; i++) { string uid = $"{basename}#{i}"; if(!uids.Contains(uid)) { return uid; } } // you don't really want to be here return basename + Guid.NewGuid(); } public void ForceSaveJSON(JSONClass jc, string path) { var confirmButtonTransform = SuperController.singleton.transform .Find("WorldScaleAdjust/HUDForLogs/UserConfirmCanvas/UserConfirmPluginActionPanel(Clone)/Panel/ConfirmButton"); if(confirmButtonTransform == null) return; confirmButtonTransform.GetComponent