using System; using UnityEngine; using UnityEngine.UI; using System.Collections; using System.Collections.Generic; using SimpleJSON; using System.Linq; // Auto Bulger Plugin by Saking55 // // A modified version of the Belly Bulger Plugin by Captain Varghoss. // // It basically works like the Captain Varghoss plugin, but it // selects the target automatically. // // Changes have also been made to the way distances are calculated. // Distances are calculated from the Rigid Body that is colliding // rather than the position of the main object or controller. // // Morphings are only applied if the corresponding triggers are in true state. // // Added a smoothing filter to reduce flickering of morphs. // // Morphs not included. // // // All credit goes to the original creators of these plugins: // VRAdultFun and further enhanced by Captain Varghoss. // namespace Saking55 { public class AutoBellyBulger : MVRScript { protected JSONStorableBool isActive; protected JSONStorableString uiFocusTarget; protected JSONStorableFloat uiTargetOffsetX; protected JSONStorableFloat uiTargetOffsetY; protected JSONStorableFloat uiTargetOffsetZ; protected JSONStorableFloat uiBulgeMult; protected JSONStorableFloat uiMinBulgeDistMult; protected JSONStorableFloat uiBulge1Mult; protected JSONStorableFloat uiBulge2Mult; protected JSONStorableFloat uiBulge3Mult; protected JSONStorableFloat uiBulge4Mult; protected JSONStorableFloat uiBulge5Mult; protected JSONStorableFloat uiBulge6Mult; protected JSONStorableFloat uiBulge7Mult; protected JSONStorableFloat uiBulge8Mult; protected JSONStorableFloat uiCurrentDepth; protected JSONStorableString helpText; protected JSONStorableString helpText2; protected JSONStorableString helpText3; protected JSONStorableString helpText4; protected JSONStorableString helpText_Offset; protected JSONStorableFloat smoothingFactor; protected JSONStorableString filterName; protected JSONStorableBool filterNameActive; private static float tempFloat = 1.0f; private static FreeControllerV3 pelvisController; private static DAZMorph morphDeepBulgeBelly1; private static DAZMorph morphDeepBulgeBelly2; private static DAZMorph morphDeepBulgeBelly3; private static DAZMorph morphDeepBulgeBelly4; private static DAZMorph morphDeepBulgeBelly5; private static DAZMorph morphDeepBulgeBelly6; private static DAZMorph morphDeepBulgeBelly7; private static DAZMorph morphDeepBulgeBelly8; private static Atom person; CollisionTriggerEventHandler triggerEvtHdl1; CollisionTriggerEventHandler triggerEvtHdl2; CollisionTriggerEventHandler triggerEvtHdl3; String targetAtomName; String selectedTarget; private CollisionTrigger triggerVag; private CollisionTrigger triggerVagD; private CollisionTrigger triggerVagDD; private Collider temp; Collider colDistTarget = null; float yVelocity = 0.0f; float distance, oldDist, tempDist, minDist = 10f; bool notReset = true; public override void Init() { try { // Active Toggle isActive = new JSONStorableBool("isActive", true); RegisterBool(isActive); var toggle = CreateToggle(isActive); toggle.label = "Active"; JSONStorableAction ToggleActive = new JSONStorableAction("Toggle Belly Bulger", () => { isActive.val = !isActive.val; }); RegisterAction(ToggleActive); helpText = new JSONStorableString("Helptext", "\nAuto Belly Bulger\n\nAutomatically selects the collinding object and apply morphs to Person.\n"); UIDynamic helpTextfield = CreateTextField(helpText, false); helpTextfield.height = 210f; uiFocusTarget = new JSONStorableString("TargetObject", "\nTarget: None"); UIDynamic targetText = CreateTextField(uiFocusTarget, true); targetText.height = 100f; uiBulgeMult = _createSlider("Belly Bulge Multiplier", 1f, 0.0f, 3.0f, false, true,false); uiMinBulgeDistMult = _createSlider("Min Bulge Dist Multiplier", 5.0f, 0.0f, 5.0f, false, true, false); uiCurrentDepth = _createSlider("Current Depth", 0.0f, 0f, 1.0f, true, false,false); smoothingFactor = _createSlider("Morph Smoothing", 0.02f, 0f, 1.0f, rightSide:false); var btn = CreateButton("Reset Morphs", false); btn.button.onClick.AddListener(() => { ResetMorphs(); }); // Target Atom name filtering helpText3 = new JSONStorableString("Helptext3", "\nTarget Atom Name Filtering\n\nUse the input below to filter which Atom should be used by the plugin. i.e. Person, Dildo...\nOptional, use if the plugin doesn't select the desired target automatically."); UIDynamic helpTextfield3 = CreateTextField(helpText3, false); helpTextfield3.height = 280f; filterNameActive = new JSONStorableBool("filterNameActive", false); RegisterBool(filterNameActive); var toggleFilter = CreateToggle(filterNameActive); toggleFilter.label = "Filter Atom Name"; JSONStorableAction ToggleNameFilterActive = new JSONStorableAction("Toggle Name Filter", () => { filterNameActive.val = !filterNameActive.val; }); RegisterAction(ToggleNameFilterActive); filterName = new JSONStorableString("filterString", "Person"); UIDynamic nameFilterText = CreateTextInput(filterName); nameFilterText.height = 10f; helpText_Offset = new JSONStorableString("helpText_Offset", "\nTarget Offset"); UIDynamic helpTextfield_Offset = CreateTextField(helpText_Offset, false); helpTextfield_Offset.height = 80f; uiTargetOffsetX = _createSlider("Target Offset X", 0f, -1.0f, 1.0f, false, true,false); uiTargetOffsetY = _createSlider("Target Offset Y", 0f, -1.0f, 1.0f, false, true, false); uiTargetOffsetZ = _createSlider("Target Offset Z", 0f, -1.0f, 1.0f, false, true, false); // Per morph multiplier reducers helpText2 = new JSONStorableString("Helptext2", "\nBulge Morph Adjustments\n\nUse these morphs to reduce Bulge Multiplier on individual morphs."); UIDynamic helpTextfield2 = CreateTextField(helpText2, true); helpTextfield2.height = 200f; uiBulge1Mult = _createSlider("Bulge 1 Amount Mult", 0.32f, 0.0f, 1.0f, rightSide:true); uiBulge2Mult = _createSlider("Bulge 2 Amount Mult", 0.67f, 0.0f, 1.0f, rightSide:true); uiBulge3Mult = _createSlider("Bulge 3 Amount Mult", 0.9f, 0.0f, 1.0f, rightSide:true); uiBulge4Mult = _createSlider("Bulge 4 Amount Mult", 1.0f, 0.0f, 1.0f, rightSide:true); uiBulge5Mult = _createSlider("Bulge 5 Amount Mult", 0.86f, 0.0f, 1.0f, rightSide:true); uiBulge6Mult = _createSlider("Bulge 6 Amount Mult", 0.7f, 0.0f, 1.0f, rightSide:true); uiBulge7Mult = _createSlider("Bulge 7 Amount Mult", 0.39f, 0.0f, 1.0f, rightSide:true); uiBulge8Mult = _createSlider("Bulge 8 Amount Mult", 0.18f, 0.0f, 1.0f, rightSide:true); // Collisions debug // helpText4 = new JSONStorableString("Helptext4", "\nBulge Morph Adjustments\n\nUse these morphs to reduce Bulge Multiplier on individual morphs."); // UIDynamic helpTextfield4 = CreateTextField(helpText4, false); // helpTextfield4.height = 200f; } catch (Exception e) { SuperController.LogError("Exception caught: " + e); } } private JSONStorableFloat _createSlider(string sliderName, float defaultValue, float minValue, float maxValue, bool constrain = true, bool interactable = true, bool rightSide=false) { JSONStorableFloat ret = new JSONStorableFloat(sliderName, defaultValue, minValue, maxValue, constrain, interactable); RegisterFloat(ret); CreateSlider(ret, rightSide); return ret; } void Start() { try { person = containingAtom; if (person != null) { triggerVag = (containingAtom.GetStorableByID("VaginaTrigger") as CollisionTrigger); triggerVagD = (containingAtom.GetStorableByID("DeepVaginaTrigger") as CollisionTrigger); triggerVagDD = (containingAtom.GetStorableByID("DeeperVaginaTrigger") as CollisionTrigger); triggerVag.triggerEnabledJSON.val = true; triggerVagD.triggerEnabledJSON.val = true; triggerVagDD.triggerEnabledJSON.val = true; triggerEvtHdl1 = triggerVag.GetComponent(typeof(CollisionTriggerEventHandler)) as CollisionTriggerEventHandler; triggerEvtHdl2 = triggerVagD.GetComponent(typeof(CollisionTriggerEventHandler)) as CollisionTriggerEventHandler; triggerEvtHdl3 = triggerVagDD.GetComponent(typeof(CollisionTriggerEventHandler)) as CollisionTriggerEventHandler; pelvisController = person.GetStorableByID("pelvisControl") as FreeControllerV3; ResetMorphs(); } } catch (Exception e) { SuperController.LogError("Exception caught: " + e); } } private void ResetMorphs() { if (notReset) { JSONStorable js = person.GetStorableByID("geometry"); DAZCharacterSelector dcs = js as DAZCharacterSelector; GenerateDAZMorphsControlUI morphUI = dcs.morphsControlUI; if (morphUI != null) { morphDeepBulgeBelly1 = morphUI.GetMorphByDisplayName("Belly Bulger 1"); morphDeepBulgeBelly1.Reset(); morphDeepBulgeBelly2 = morphUI.GetMorphByDisplayName("Belly Bulger 2"); morphDeepBulgeBelly2.Reset(); morphDeepBulgeBelly3 = morphUI.GetMorphByDisplayName("Belly Bulger 3"); morphDeepBulgeBelly3.Reset(); morphDeepBulgeBelly4 = morphUI.GetMorphByDisplayName("Belly Bulger 4"); morphDeepBulgeBelly4.Reset(); morphDeepBulgeBelly5 = morphUI.GetMorphByDisplayName("Belly Bulger 5"); morphDeepBulgeBelly5.Reset(); morphDeepBulgeBelly6 = morphUI.GetMorphByDisplayName("Belly Bulger 6"); morphDeepBulgeBelly6.Reset(); morphDeepBulgeBelly7 = morphUI.GetMorphByDisplayName("Belly Bulger 7"); morphDeepBulgeBelly7.Reset(); morphDeepBulgeBelly8 = morphUI.GetMorphByDisplayName("Belly Bulger 8"); morphDeepBulgeBelly8.Reset(); } notReset = false; } } void FixedUpdate() { if (isActive.val == true) { try { { // Find "target"atom and calculate distance targetAtomName = "None"; // Find target atom from "vag trigger event" atoms. var result = (triggerEvtHdl1.collidingWithDictionary) .Concat(triggerEvtHdl2.collidingWithDictionary) .Concat(triggerEvtHdl3.collidingWithDictionary) .GroupBy(d => d.Key).ToDictionary(d => d.Key, d => d.First().Value); foreach (KeyValuePair pair in result) { selectedTarget = (pair.Key.attachedRigidbody.GetComponentInParent(typeof(Atom)) as Atom).name; if (selectedTarget.Equals(containingAtom.name)) { selectedTarget = "None"; } else { if (filterNameActive.val && (selectedTarget.ToLower().Contains(filterName.val.ToLower())) || !filterNameActive.val) { distance = Vector3.Distance(pelvisController.followWhenOff.position, pair.Key.attachedRigidbody.position); if (distance < minDist) { temp = pair.Key; minDist = distance; targetAtomName = selectedTarget; } } } } colDistTarget = temp; if (targetAtomName == "None") { minDist = 10f; } else { var offset = new Vector3(uiTargetOffsetX.val, uiTargetOffsetY.val, uiTargetOffsetZ.val); // By calculating the rotation Quaternion, align the offset to the target's local coordinate system. oldDist = Vector3.Distance(pelvisController.followWhenOff.position, colDistTarget.attachedRigidbody.position + colDistTarget.attachedRigidbody.rotation * offset); tempDist = Vector3.Distance(pelvisController.followWhenOff.position, temp.attachedRigidbody.position + temp.attachedRigidbody.rotation * offset); minDist = Mathf.Min(tempDist, oldDist); } } // End Find Target uiFocusTarget.val = "\nTarget: " + targetAtomName;// + " dist: " + minDist; if (morphDeepBulgeBelly1 != null && morphDeepBulgeBelly2 != null && morphDeepBulgeBelly3 != null && morphDeepBulgeBelly4 != null && morphDeepBulgeBelly5 != null && morphDeepBulgeBelly6 != null && morphDeepBulgeBelly7 != null && morphDeepBulgeBelly8 != null) { notReset = true; // Calculate distance between vag and target. if (targetAtomName.Equals("None")) tempFloat = Mathf.SmoothDamp(tempFloat, 1f, ref yVelocity, smoothingFactor.val); else if (triggerVag.trigger.active || triggerVagD.trigger.active || triggerVagDD.trigger.active) { tempFloat = Mathf.SmoothDamp(tempFloat, remapp(minDist, 0.05f, 0.2f, 0f, 0.7f), ref yVelocity, smoothingFactor.val); } // Update Morphs calcUpdateMorph(morphDeepBulgeBelly1, tempFloat, 0.15f, uiBulge1Mult); calcUpdateMorph(morphDeepBulgeBelly2, tempFloat, 0.13f, uiBulge2Mult); calcUpdateMorph(morphDeepBulgeBelly3, tempFloat, 0.11f, uiBulge3Mult); calcUpdateMorph(morphDeepBulgeBelly4, tempFloat, 0.09f, uiBulge4Mult); calcUpdateMorph(morphDeepBulgeBelly5, tempFloat, 0.07f, uiBulge5Mult); calcUpdateMorph(morphDeepBulgeBelly6, tempFloat, 0.05f, uiBulge6Mult); calcUpdateMorph(morphDeepBulgeBelly7, tempFloat, 0.03f, uiBulge7Mult); calcUpdateMorph(morphDeepBulgeBelly8, tempFloat, 0.025f, uiBulge8Mult); } uiCurrentDepth.val = tempFloat; minDist = 10f; } catch (Exception e) { SuperController.LogError("Exception caught: " + e); } } else { ResetMorphs(); } } private void calcUpdateMorph(DAZMorph targetMorph, float distance, float eachDist0, JSONStorableFloat eachBulgeMult) { targetMorph.morphValue = Mathf.Max((1.0f - distance / (eachDist0 * uiMinBulgeDistMult.val)) * uiBulgeMult.val * eachBulgeMult.val, 0.0f); } private float remapp(float val, float from1, float to1, float from2, float to2) { return (val - from1) / (to1 - from1) * (to2 - from2) + from2; } void OnDestroy() { ResetMorphs(); } void OnDisable() { ResetMorphs(); } public UIDynamicTextField CreateTextInput(JSONStorableString jss, bool rightSide = false) { var textfield = CreateTextField(jss, rightSide); textfield.height = 20f; textfield.backgroundColor = Color.white; var input = textfield.gameObject.AddComponent(); var rect = input.GetComponent().sizeDelta = new Vector2(1f, 0.4f); input.textComponent = textfield.UItext; jss.inputField = input; return textfield; } } }