// --- START OF FILE OrificeDynamics.cs --- using System; using System.Collections; using System.Collections.Generic; using System.Linq; using UnityEngine; using SimpleJSON; using UnityEngine.UI; using Random = UnityEngine.Random; namespace Skynet { public class OrificeDynamics : MVRScript { // region --- Constants: AutoGensGape --- private const string BONE_A1_NAME = "_JointAl"; private const string BONE_A2_NAME = "_JointAr"; private const string BONE_V1_NAME = "_JointGl"; private const string BONE_V2_NAME = "_JointGr"; private const string MORPH_A_DISPLAY_NAME = "AutoAnalGape - Genital"; private const string MORPH_V_DISPLAY_NAME = "AutoVaginalGape - Genital"; private const string MORPH_M_DISPLAY_NAME = "Mouth Open Wide 2"; private const bool USE_DISPLAY_NAME_FOR_MORPHS = true; private const float SMOOTHING_MIN = 0.0f; private const float SMOOTHING_IN_MAX = 0.99f; private const float DECAY_TIME_MIN = 0.0f; private const float DECAY_TIME_MAX = 60.0f; private const float TWINK_INTERVAL_MEAN_MIN = 0.1f; private const float TWINK_INTERVAL_MEAN_MAX = 10.0f; private const float TWINK_INTERVAL_DELTA_MIN = 0.0f; private const float TWINK_INTERVAL_DELTA_MAX = 5.0f; private const float TWINK_DELAY_MIN = 0.0f; private const float DEFAULT_LIP_TRIGGER_RADIUS = 0.03f; private const float DEFAULT_GENITAL_JOINT_RADIUS = 0.02f; private const string LIP_TRIGGER_STORABLE_NAME = "LipTrigger"; private const string MOUTH_TRIGGER_STORABLE_NAME = "MouthTrigger"; private const string THROAT_TRIGGER_STORABLE_NAME = "ThroatTrigger"; private const string VAGINA_TRIGGER_STORABLE_NAME = "VaginaTrigger"; private const string TONGUE_CONTROL_STORABLE_NAME = "TongueControl"; private const string TONGUE_COLLISION_PARAM_NAME = "tongueCollision"; private const string FEMALE_ANATOMY_STORABLE_NAME = "FemaleAnatomy"; private const string FEMALE_ANATOMY_ENABLED_PARAM_NAME = "enabled"; private const string PHYSICS_MODEL_STORABLE_NAME = "PhysicsModel"; private const string DYNAMIC_FRICTION_PARAM_NAME = "dynamicFriction"; private const string STATIC_FRICTION_PARAM_NAME = "staticFriction"; private const string FRICTION_COMBINE_PARAM_NAME = "frictionCombine"; private const string LOWER_PHYSICS_MESH_STORABLE_NAME = "LowerPhysicsMesh"; private const string SOFT_VERTICES_COLLIDER_RADIUS_PARAM_NAME = "softVerticesColliderRadius"; private const string SOFT_VERTICES_DISTANCE_LIMIT_PARAM_NAME = "softVerticesDistanceLimit"; private const float TARGET_SOFT_VERTICES_COLLIDER_RADIUS = 0.015f; private const float TARGET_SOFT_VERTICES_DISTANCE_LIMIT = 0.060f; private const float SOFT_VERTICES_SMOOTHING_DEFAULT = 0.90f; private const string JAW_CONTROL_STORABLE_NAME = "JawControl"; private const string JAW_SPRING_PARAM_NAME = "spring"; private const float TARGET_JAW_SPRING_VALUE = 10f; private const string CUA_ATOM_CONTROL_ID = "AtomControl"; private const string CUA_COLLISION_ENABLED_PARAM = "collisionEnabled"; // endregion // region --- Constants: Bulge --- private const int BULGE_NUM_MOUTH_MORPHS = 3; private const int BULGE_NUM_BELLY_MORPHS = 5; private const bool BULGE_HARDCODED_CLAMP_MORPHS = false; private const float BULGE_DEFAULT_MORPH_APPLY_SMOOTHING = 0.80f; // Default for 1.0 - smoothingValue, so 0.1 effective lerp private const float BULGE_HARDCODED_DISTANCE_SMOOTHING = 0.05f; private const float BULGE_MOUTH_DIST_REMAP_MIN_DEFAULT = 0.02f; private const float BULGE_MOUTH_DIST_REMAP_MAX_DEFAULT = 0.15f; private const float BULGE_BELLY_DIST_REMAP_MIN_DEFAULT = 0.05f; private const float BULGE_BELLY_DIST_REMAP_MAX_DEFAULT = 0.20f; // endregion // region --- Constants: In/Out Motion --- private const float INOUT_DEFAULT_MORPH_STRENGTH_LIPS_IN = 0.5f; private const float INOUT_DEFAULT_MORPH_STRENGTH_LIPS_OUT = 1.4f; private const float INOUT_DEFAULT_MORPH_STRENGTH_VAGINA = 0.70f; private const float INOUT_DEFAULT_MORPH_STRENGTH_ANAL = 0.30f; private const float INOUT_DEFAULT_SMOOTHING = 0.95f; private const float INOUT_DEFAULT_DECAY_SMOOTHING = 0.95f; private const float INOUT_DEFAULT_MOTION_THRESHOLD = 0.0025f; private const string MORPH_ANAL_IN_NAME = "anus1 - Genital"; private const string MORPH_ANAL_OUT_NAME = "anus2 - Genital"; private const string MORPH_VAGINAL_IN_NAME = "vagina1 - Genital"; private const string MORPH_VAGINAL_OUT_NAME = "vagina2 - Genital"; private const string MORPH_LIPS_IN_NAME = "lips 1 v3"; private const string MORPH_LIPS_OUT_NAME = "lips 2 v3"; // endregion private const string LOG_PREFIX = "OrificeDynamics"; // --- Shared Atom Reference --- private Atom _personAtom; private DAZCharacterSelector _characterSelector; private bool _isPersonAtom = false; private bool _agg_main_init_done = false; private bool _fullInitializationRoutineComplete = false; // region --- AutoGensGape: References & State --- (Gape module) private Transform _boneA1Transform; private Transform _boneA2Transform; private bool _bonesAFound = false; private float _currentDistanceA = 0f; private Transform _boneV1Transform; private Transform _boneV2Transform; private bool _bonesVFound = false; private float _currentDistanceV = 0f; private Transform _lipTriggerTransform; private CollisionTrigger _mouthCollisionTriggerComponent; private CollisionTrigger _throatCollisionTriggerComponent; private CollisionTrigger _vaginaCollisionTriggerComponentAGG; private JSONStorableBool _tongueCollisionJSONStorable; private bool _lipTriggerTransformFound = false; private bool _mouthTriggerComponentFound = false; private bool _throatTriggerComponentFound = false; private bool _vaginaTriggerComponentAGGFound = false; private bool _tongueCollisionStorableFound = false; private float _smoothedMorphValueA = 0f; private DAZMorph _selectedMorphA; private float _smoothedMorphValueV = 0f; private DAZMorph _selectedMorphV; private float _smoothedMorphValueM = 0f; private DAZMorph _selectedMorphM_AGG; private float _currentSmoothedAdditiveAnalValue = 0f; private float _currentSmoothedAdditiveVaginalValue = 0f; private bool _isDecayingA = false; private float _decayStartTimeA = 0f; private float _decayStartValueA = 0f; private float _decayTargetValueA = 0f; private bool _isTwinkingA = false; private float _nextTwinkTimeA = 0f; private float _currentTwinkDurationA = 0f; private float _currentTwinkCloseTimeA = 0f; private float _twinkPhaseTimerA = 0f; private float _twinkFactorA1_A = 2f; private float _twinkFactorA2_A = 1000f; private bool _isDecayingV = false; private float _decayStartTimeV = 0f; private float _decayStartValueV = 0f; private float _decayTargetValueV = 0f; private bool _isCollidingA1 = false; private bool _isCollidingA2 = false; private bool _isCollidingV1 = false; private bool _isCollidingV2 = false; private bool _isCollidingLip = false; private bool _isCollidingMouthAGG = false; private bool _isCollidingThroatAGG = false; private bool _isCollidingVaginaTriggerAGG = false; private List _personColliders; private List _allowedSelfCollisionRigidbodyNames; private JSONStorableBool _femaleAnatomyEnabledJSON; private bool _originalFemaleAnatomyEnabledState = true; private bool _femaleAnatomyStorableFound = false; private bool _pluginEnabledFemaleAnatomyOverride = false; private JSONStorableBool _disableBaseAutoGensJSON; private JSONStorable _physicsModelStorable; private JSONStorableFloat _dynamicFrictionJSON; private JSONStorableFloat _staticFrictionJSON; private JSONStorableStringChooser _frictionCombineJSON; private float _originalDynamicFriction; private float _originalStaticFriction; private string _originalFrictionCombine; private bool _physicsFrictionStorablesFound = false; private bool _frictionOverriddenByLipTrigger = false; private JSONStorable _lowerPhysicsMeshStorable; private JSONStorableFloat _softVerticesColliderRadiusJSON; private JSONStorableFloat _softVerticesDistanceLimitJSON; private float _originalSoftVerticesColliderRadius; private float _originalSoftVerticesDistanceLimit; private float _currentSoftVerticesColliderRadius; private float _currentSoftVerticesDistanceLimit; private bool _softVerticesColliderRadiusFound = false; private bool _softVerticesDistanceLimitFound = false; private bool _softVerticesParamsOverridden = false; private JSONStorableFloat _softVerticesSmoothingJSON; private JSONStorable _jawControlStorable; private JSONStorableFloat _jawSpringJSON; private float _originalJawSpring; private bool _jawControlStorableFound = false; private bool _jawSpringOverridden = false; private JSONStorableBool _enableOptimizedPhysicsJSON; private bool _optimizedPhysicsStateRestored = true; private bool _originalTongueCollisionState = false; private bool _tongueCollisionOverridden = false; private Dictionary _cuaCollisionEnabledStorables = new Dictionary(); // endregion // region --- AutoGensGape: UI Elements --- (Gape module) private JSONStorableBool _enableAutoGensGapeModuleJSON; private JSONStorableFloat _overallStrengthJSON; private JSONStorableFloat _additiveAnalMorphValueJSON; private JSONStorableFloat _additiveVaginalMorphValueJSON; private JSONStorableFloat _additiveMouthMorphValueJSON; private JSONStorableFloat _lipTriggerRadiusJSON; private JSONStorableFloat _analBoneJointRadiusJSON; private JSONStorableFloat _vaginalBoneJointRadiusJSON; private JSONStorableBool _enableMorphDriveAJSON; private JSONStorableBool _enableMorphDriveVJSON; private JSONStorableBool _enableMorphDriveMJSON; private JSONStorableFloat _morphMultiplierAJSON; private JSONStorableFloat _morphOffsetAJSON; private JSONStorableFloat _morphDecayTimeAJSON; private JSONStorableBool _twinkEnabledJSON; private JSONStorableFloat _twinkIntervalMeanJSON; private JSONStorableFloat _twinkIntervalDeltaJSON; private JSONStorableFloat _twinkStartDelayJSON; private JSONStorableFloat _morphMultiplierVJSON; private JSONStorableFloat _morphOffsetVJSON; private JSONStorableFloat _morphDecayTimeVJSON; private JSONStorableFloat _morphSmoothInAJSON; private JSONStorableFloat _morphSmoothInVJSON; private JSONStorableFloat _morphSmoothInMJSON; private JSONStorableFloat _morphDecayTimeMJSON; // endregion // region --- Bulge: References & State --- protected FreeControllerV3 _bulge_mouthReferenceControl; protected FreeControllerV3 _bulge_bellyReferenceControl; private CollisionTrigger _bulge_mouthTrigger; private CollisionTriggerEventHandler _bulge_mouthTriggerEvtHandler; protected DAZMorph[] _bulge_mouthMorphs = new DAZMorph[BULGE_NUM_MOUTH_MORPHS]; protected float[] _bulge_currentMouthMorphValues = new float[BULGE_NUM_MOUTH_MORPHS]; private float _bulge_lastRawMouthDist = 0f; private CollisionTrigger _bulge_vaginaTrigger; private CollisionTriggerEventHandler _bulge_vaginaTriggerEvtHandler; protected DAZMorph[] _bulge_bellyMorphs = new DAZMorph[BULGE_NUM_BELLY_MORPHS]; protected float[] _bulge_currentBellyMorphValues = new float[BULGE_NUM_BELLY_MORPHS]; private float _bulge_lastRawBellyDist = 0f; private bool _bulge_mouthMorphsFullyLoaded = false; private bool _bulge_bellyMorphsFullyLoaded = false; private bool _bulge_needsReset = true; private float _bulge_yVelMouth = 0f, _bulge_yVelBelly = 0f; // endregion // region --- Bulge: UI Elements --- private JSONStorableBool _enableBulgeModuleJSON; private JSONStorableBool _enableThroatBulgeJSON; private JSONStorableBool _enableAbdomenBulgeJSON; protected JSONStorableFloat _bulge_uiThroatBulgeMult; protected JSONStorableFloat[] _bulge_uiMouthMorphMults = new JSONStorableFloat[BULGE_NUM_MOUTH_MORPHS]; protected JSONStorableFloat _bulge_uiAbdomenBulgeMult; protected JSONStorableFloat[] _bulge_uiBellyMorphMults = new JSONStorableFloat[BULGE_NUM_BELLY_MORPHS]; private JSONStorableFloat _mouthBulgeSmoothingJSON; private JSONStorableFloat _bellyBulgeSmoothingJSON; // endregion // region --- In/Out Motion: State & References --- private DAZMorph _selectedLipsMorphIn, _selectedLipsMorphOut; private DAZMorph _selectedVaginaMorphIn, _selectedVaginaMorphOut; private DAZMorph _selectedAnalMorphIn, _selectedAnalMorphOut; private float _currentLipsMorphValueIn, _currentLipsMorphValueOut; private float _currentVaginaMorphValueIn, _currentVaginaMorphValueOut; private float _currentAnalMorphValueIn, _currentAnalMorphValueOut; private float _previousLipsDepthInOut, _previousAbdomenDepthInOut; // endregion // region --- In/Out Motion: UI Elements --- private JSONStorableBool _enableLipsMotionJSON; private JSONStorableBool _enableVaginaMotionJSON; private JSONStorableBool _enableAnalMotionJSON; private JSONStorableFloat _motionMorphStrengthLipsInJSON; private JSONStorableFloat _motionMorphStrengthLipsOutJSON; private JSONStorableFloat _motionMorphStrengthVaginaJSON; private JSONStorableFloat _motionMorphStrengthAnalJSON; private JSONStorableFloat _motionMorphSmoothingJSON; private JSONStorableFloat _motionMorphDecaySmoothingJSON; private JSONStorableFloat _motionThresholdJSON; // endregion // --- Tab UI --- private UIDynamicButton _mainSettingsTabButton; private UIDynamicButton _advancedSettingsTabButton; private List _mainSettingsControls; private List _advancedSettingsControls; private string _activeUITab = "Main"; private Color _activeTabColorUI = new Color(0f, 0.75f, 0.75f, 1f); private Color _inactiveTabColorUI = Color.white; private Font _defaultFont; private UIDynamic CreateSpacerControl(bool rightSide, float? prefHeight = null) { UIDynamic item = base.CreateSpacer(rightSide); LayoutElement layoutElement = item.gameObject.GetComponent(); if (layoutElement == null) layoutElement = item.gameObject.AddComponent(); if (prefHeight.HasValue) { layoutElement.preferredHeight = prefHeight.Value; layoutElement.minHeight = prefHeight.Value; } else { layoutElement.preferredHeight = 0f; layoutElement.minHeight = 0f; } return item; } private UIDynamic CreateDisplayText(string textContent, bool forRightColumn, int fontSize = 28, float preferredHeight = 50f) { UIDynamic uiDynamicTextHolder = CreateSpacerControl(forRightColumn, preferredHeight); Text textComponent = uiDynamicTextHolder.gameObject.AddComponent(); textComponent.text = textContent; if (_defaultFont != null) { textComponent.font = _defaultFont; } else { textComponent.font = Resources.GetBuiltinResource("Arial.ttf"); } textComponent.fontSize = fontSize; textComponent.color = Color.white; textComponent.alignment = TextAnchor.MiddleLeft; textComponent.supportRichText = true; RectTransform rectTransform = textComponent.rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.pivot = new Vector2(0f, 0.5f); rectTransform.offsetMin = new Vector2(10f, 2f); rectTransform.offsetMax = new Vector2(-10f, -2f); return uiDynamicTextHolder; } public override void Init() { try { if (manager.configurableScrollablePopupPrefab != null) { Text sampleText = manager.configurableScrollablePopupPrefab.GetComponentInChildren(true); if (sampleText != null) _defaultFont = sampleText.font; } if (_defaultFont == null) _defaultFont = Resources.GetBuiltinResource("Arial.ttf"); pluginLabelJSON.val = "Orifice Dynamics"; _personAtom = containingAtom; if (_personAtom == null || _personAtom.type != "Person") { SuperController.LogError($"{LOG_PREFIX}: Requires Person Atom"); return; } _isPersonAtom = true; _characterSelector = _personAtom.GetStorableByID("character") as DAZCharacterSelector ?? _personAtom.GetStorableByID("geometry") as DAZCharacterSelector; if (_characterSelector == null) { SuperController.LogError($"{LOG_PREFIX}: DAZCharacterSelector not found"); return; } if (_characterSelector.morphsControlUI == null) { SuperController.LogError($"{LOG_PREFIX}: morphsControlUI not found"); return; } _allowedSelfCollisionRigidbodyNames = new List { "lHand", "rHand", "lHandControl", "rHandControl", "lFinger1_1", "lFinger1_2", "lFinger1_3", "lFinger2_1", "lFinger2_2", "lFinger2_3", "lFinger3_1", "lFinger3_2", "lFinger3_3", "lFinger4_1", "lFinger4_2", "lFinger4_3", "lThumb1", "lThumb2", "lThumb3", "rFinger1_1", "rFinger1_2", "rFinger1_3", "rFinger2_1", "rFinger2_2", "rFinger2_3", "rFinger3_1", "rFinger3_2", "rFinger3_3", "rFinger4_1", "rFinger4_2", "rFinger4_3", "rThumb1", "rThumb2", "rThumb3", "lFoot", "rFoot", "lFootControl", "rFootControl", "lToe", "rToe", "tongue0", "tongue01", "tongue1", "tongue02", "tongue2", "tongue03", "tongue3", "tongue04", "tongue4", "tongueBase", "tongueTip", "tongueControl", "tongueRootControl", "tongueMidControl", "tongueTipControl"}; _mainSettingsControls = new List(); _advancedSettingsControls = new List(); _enableAutoGensGapeModuleJSON = new JSONStorableBool("Enable Gape Module", true); RegisterBool(_enableAutoGensGapeModuleJSON); _enableBulgeModuleJSON = new JSONStorableBool("Enable Bulge Module", true); RegisterBool(_enableBulgeModuleJSON); _mainSettingsTabButton = CreateButton("Main Settings", false); _mainSettingsTabButton.button.onClick.AddListener(() => SetActiveUITab("Main")); _advancedSettingsTabButton = CreateButton("Advanced Settings", true); _advancedSettingsTabButton.button.onClick.AddListener(() => SetActiveUITab("Advanced")); float mainTitleHeight = 50f; float subTitleHeight = 40f; //float controlHeight = 60f; //float toggleHeight = 50f; // --- Main Settings UI Construction --- // Gape Dynamics Group (LEFT) _mainSettingsControls.Add(CreateDisplayText("--- Gape Dynamics ---", false, 30, mainTitleHeight)); UIDynamicToggle driveAToggle = CreateToggle(_enableMorphDriveAJSON = new JSONStorableBool("Enable Anal Gape", true), false); driveAToggle.label = "Anal Gape"; _enableMorphDriveAJSON.setCallbackFunction += EnableAnalCallback; _mainSettingsControls.Add(driveAToggle); RegisterBool(_enableMorphDriveAJSON); UIDynamicToggle driveVToggle = CreateToggle(_enableMorphDriveVJSON = new JSONStorableBool("Enable Vaginal Gape", true), false); driveVToggle.label = "Vaginal Gape"; _enableMorphDriveVJSON.setCallbackFunction += EnableVaginalCallback; _mainSettingsControls.Add(driveVToggle); RegisterBool(_enableMorphDriveVJSON); UIDynamicToggle driveMToggle = CreateToggle(_enableMorphDriveMJSON = new JSONStorableBool("Enable Mouth Gape", true), false); driveMToggle.label = "Mouth Gape"; _enableMorphDriveMJSON.setCallbackFunction += EnableMouthCallback; _mainSettingsControls.Add(driveMToggle); RegisterBool(_enableMorphDriveMJSON); _disableBaseAutoGensJSON = new JSONStorableBool("Disable Base Auto Genitals", true); RegisterBool(_disableBaseAutoGensJSON); UIDynamicToggle disableBaseGenToggle = CreateToggle(_disableBaseAutoGensJSON, false); disableBaseGenToggle.label = "Disable Base Auto Genitals"; _disableBaseAutoGensJSON.setCallbackFunction += HandleFemaleAnatomyToggle; _mainSettingsControls.Add(disableBaseGenToggle); RegisterBool(_disableBaseAutoGensJSON); _mainSettingsControls.Add(CreateDisplayText("Gape: Pre-Open, Strength, Proximity", false, 24, subTitleHeight)); UIDynamicSlider addAnalSlider = CreateSlider(_additiveAnalMorphValueJSON = new JSONStorableFloat("Pre-Open Anal", 0.2f, 0.0f, 1.0f), false); addAnalSlider.label = "Anal Pre-Open"; _mainSettingsControls.Add(addAnalSlider); RegisterFloat(_additiveAnalMorphValueJSON); UIDynamicSlider addVaginalSlider = CreateSlider(_additiveVaginalMorphValueJSON = new JSONStorableFloat("Pre-Open Vaginal", 0.2f, 0.0f, 1.0f), false); addVaginalSlider.label = "Vaginal Pre-Open"; _mainSettingsControls.Add(addVaginalSlider); RegisterFloat(_additiveVaginalMorphValueJSON); UIDynamicSlider lipRadiusSlider = CreateSlider(_lipTriggerRadiusJSON = new JSONStorableFloat("Mouth Proximity", DEFAULT_LIP_TRIGGER_RADIUS, 0.001f, 0.1f), false); lipRadiusSlider.label = "Mouth Proximity"; _lipTriggerRadiusJSON.storeType = JSONStorableParam.StoreType.Full; _mainSettingsControls.Add(lipRadiusSlider); RegisterFloat(_lipTriggerRadiusJSON); UIDynamicSlider analRadiusSlider = CreateSlider(_analBoneJointRadiusJSON = new JSONStorableFloat("Anal Proximity", DEFAULT_GENITAL_JOINT_RADIUS, 0.001f, 0.1f), false); analRadiusSlider.label = "Anal Proximity"; _analBoneJointRadiusJSON.storeType = JSONStorableParam.StoreType.Full; _mainSettingsControls.Add(analRadiusSlider); RegisterFloat(_analBoneJointRadiusJSON); UIDynamicSlider vagRadiusSlider = CreateSlider(_vaginalBoneJointRadiusJSON = new JSONStorableFloat("Vaginal Proximity", DEFAULT_GENITAL_JOINT_RADIUS, 0.001f, 0.1f), false); vagRadiusSlider.label = "Vaginal Proximity"; _vaginalBoneJointRadiusJSON.storeType = JSONStorableParam.StoreType.Full; _mainSettingsControls.Add(vagRadiusSlider); RegisterFloat(_vaginalBoneJointRadiusJSON); UIDynamicSlider analDecaySlider = CreateSlider(_morphDecayTimeAJSON = new JSONStorableFloat("Anal Gape Time", 10.00f, DECAY_TIME_MIN, DECAY_TIME_MAX), false); analDecaySlider.label = "Anal Gape Hold Time"; _morphDecayTimeAJSON.storeType = JSONStorableParam.StoreType.Full; _mainSettingsControls.Add(analDecaySlider); RegisterFloat(_morphDecayTimeAJSON); _mainSettingsControls.Add(CreateDisplayText("Anal Gape: Twink Effect Timing", false, 24, subTitleHeight)); UIDynamicToggle twinkToggle = CreateToggle(_twinkEnabledJSON = new JSONStorableBool("Enable Anal Twink Effect", true), false); twinkToggle.label = "Anal Twink Effect"; _mainSettingsControls.Add(twinkToggle); RegisterBool(_twinkEnabledJSON); UIDynamicSlider twinkMeanSlider = CreateSlider(_twinkIntervalMeanJSON = new JSONStorableFloat("Twink Time", 5.0f, TWINK_INTERVAL_MEAN_MIN, TWINK_INTERVAL_MEAN_MAX), false); twinkMeanSlider.label = "Twink Duration"; _twinkIntervalMeanJSON.storeType = JSONStorableParam.StoreType.Full; _mainSettingsControls.Add(twinkMeanSlider); RegisterFloat(_twinkIntervalMeanJSON); UIDynamicSlider twinkDeltaSlider = CreateSlider(_twinkIntervalDeltaJSON = new JSONStorableFloat("Twink Time Variation", 2.0f, TWINK_INTERVAL_DELTA_MIN, TWINK_INTERVAL_DELTA_MAX), false); twinkDeltaSlider.label = "Twink Variation"; _twinkIntervalDeltaJSON.storeType = JSONStorableParam.StoreType.Full; _mainSettingsControls.Add(twinkDeltaSlider); RegisterFloat(_twinkIntervalDeltaJSON); // Bulge Dynamics Group (RIGHT) _mainSettingsControls.Add(CreateSpacerControl(false, 0f)); _mainSettingsControls.Add(CreateDisplayText("--- Bulge Dynamics ---", true, 30, mainTitleHeight)); _mainSettingsControls.Add(CreateSpacerControl(false, 0f)); _mainSettingsControls.Add(CreateDisplayText("Bulge Masters & Strengths", true, 24, subTitleHeight)); _mainSettingsControls.Add(CreateSpacerControl(false, 0f)); UIDynamicToggle throatBulgeToggle = CreateToggle(_enableThroatBulgeJSON = new JSONStorableBool("Enable Throat Bulge", true), true); throatBulgeToggle.label = "Throat Bulge"; _mainSettingsControls.Add(throatBulgeToggle); RegisterBool(_enableThroatBulgeJSON); _mainSettingsControls.Add(CreateSpacerControl(false, 0f)); UIDynamicToggle abdomenBulgeToggle = CreateToggle(_enableAbdomenBulgeJSON = new JSONStorableBool("Enable Belly Bulge", true), true); abdomenBulgeToggle.label = "Belly Bulge"; _mainSettingsControls.Add(abdomenBulgeToggle); RegisterBool(_enableAbdomenBulgeJSON); _mainSettingsControls.Add(CreateSpacerControl(false, 0f)); UIDynamicSlider throatBulgeMultSlider = CreateSlider(_bulge_uiThroatBulgeMult = new JSONStorableFloat("Throat Bulge Multiplier", 1.30f, 0.0f, 3.0f), true); throatBulgeMultSlider.label = "Throat Bulge Strength"; _bulge_uiThroatBulgeMult.storeType = JSONStorableParam.StoreType.Full; _mainSettingsControls.Add(throatBulgeMultSlider); RegisterFloat(_bulge_uiThroatBulgeMult); _mainSettingsControls.Add(CreateSpacerControl(false, 0f)); UIDynamicSlider abdomenBulgeMultSlider = CreateSlider(_bulge_uiAbdomenBulgeMult = new JSONStorableFloat("Abdomen Bulge Multiplier", 1.00f, 0.0f, 3.0f), true); abdomenBulgeMultSlider.label = "Belly Bulge Strength"; _bulge_uiAbdomenBulgeMult.storeType = JSONStorableParam.StoreType.Full; _mainSettingsControls.Add(abdomenBulgeMultSlider); RegisterFloat(_bulge_uiAbdomenBulgeMult); // In/Out Motion Dynamics Group (RIGHT) _mainSettingsControls.Add(CreateSpacerControl(false, 0f)); _mainSettingsControls.Add(CreateDisplayText("--- In/Out Motion Dynamics ---", true, 30, mainTitleHeight)); _mainSettingsControls.Add(CreateSpacerControl(false, 0f)); _mainSettingsControls.Add(CreateDisplayText("In/Out Motion Master Toggles", true, 24, subTitleHeight)); _mainSettingsControls.Add(CreateSpacerControl(false, 0f)); UIDynamicToggle lipsMotionToggle = CreateToggle(_enableLipsMotionJSON = new JSONStorableBool("Enable Lips Motion", true), true); lipsMotionToggle.label = "Lips Motion"; _mainSettingsControls.Add(lipsMotionToggle); RegisterBool(_enableLipsMotionJSON); _mainSettingsControls.Add(CreateSpacerControl(false, 0f)); UIDynamicToggle vaginaMotionToggle = CreateToggle(_enableVaginaMotionJSON = new JSONStorableBool("Enable Vagina Motion", true), true); vaginaMotionToggle.label = "Vagina Motion"; _mainSettingsControls.Add(vaginaMotionToggle); RegisterBool(_enableVaginaMotionJSON); _mainSettingsControls.Add(CreateSpacerControl(false, 0f)); UIDynamicToggle analMotionToggle = CreateToggle(_enableAnalMotionJSON = new JSONStorableBool("Enable Anal Motion", true), true); analMotionToggle.label = "Anal Motion"; _mainSettingsControls.Add(analMotionToggle); RegisterBool(_enableAnalMotionJSON); // Physics Optimization Group (RIGHT) _mainSettingsControls.Add(CreateSpacerControl(false, 0f)); _mainSettingsControls.Add(CreateDisplayText("--- Physics Optimization ---", true, 30, mainTitleHeight)); _mainSettingsControls.Add(CreateSpacerControl(false, 0f)); _mainSettingsControls.Add(CreateDisplayText("Reduce collision jittering & Smooth penetration.", true, 24, subTitleHeight)); _mainSettingsControls.Add(CreateSpacerControl(false, 0f)); UIDynamicToggle optPhysicsToggle = CreateToggle(_enableOptimizedPhysicsJSON = new JSONStorableBool("Enable Optimized Physics", false), true); optPhysicsToggle.label = "Optimized Physics (Beta)"; _enableOptimizedPhysicsJSON.setCallbackFunction += EnableOptimizedPhysicsCallback; _mainSettingsControls.Add(optPhysicsToggle); RegisterBool(_enableOptimizedPhysicsJSON); // --- Advanced Settings UI Construction --- // Gape Dynamics (Advanced) (LEFT) _advancedSettingsControls.Add(CreateDisplayText("--- Gape Dynamics (Advanced) ---", false, 30, mainTitleHeight)); _advancedSettingsControls.Add(CreateDisplayText("Gape: Smoothing, Distance Modifiers, Hold Times", false, 24, subTitleHeight)); UIDynamicSlider svSmoothSlider = CreateSlider(_softVerticesSmoothingJSON = new JSONStorableFloat("Soft Vertices Smoothing", SOFT_VERTICES_SMOOTHING_DEFAULT, 0.01f, 0.99f), false); svSmoothSlider.label = "Physics Tune Smoothing"; _softVerticesSmoothingJSON.storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(svSmoothSlider); RegisterFloat(_softVerticesSmoothingJSON); UIDynamicSlider analSmoothSlider = CreateSlider(_morphSmoothInAJSON = new JSONStorableFloat("Anal Gape Smooth Factor", 0.95f, SMOOTHING_MIN, SMOOTHING_IN_MAX), false); analSmoothSlider.label = "Anal Gape Smooth"; _morphSmoothInAJSON.storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(analSmoothSlider); RegisterFloat(_morphSmoothInAJSON); UIDynamicSlider vagSmoothSlider = CreateSlider(_morphSmoothInVJSON = new JSONStorableFloat("Vaginal Gape Smooth Factor", 0.95f, SMOOTHING_MIN, SMOOTHING_IN_MAX), false); vagSmoothSlider.label = "Vaginal Gape Smooth"; _morphSmoothInVJSON.storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(vagSmoothSlider); RegisterFloat(_morphSmoothInVJSON); UIDynamicSlider mouthSmoothSlider = CreateSlider(_morphSmoothInMJSON = new JSONStorableFloat("Mouth Gape Smooth Factor", 0.95f, SMOOTHING_MIN, SMOOTHING_IN_MAX), false); mouthSmoothSlider.label = "Mouth Gape Smooth"; _morphSmoothInMJSON.storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(mouthSmoothSlider); RegisterFloat(_morphSmoothInMJSON); UIDynamicSlider mouthBulgeSmoothSlider = CreateSlider(_mouthBulgeSmoothingJSON = new JSONStorableFloat("Mouth Bulge Smoothing", BULGE_DEFAULT_MORPH_APPLY_SMOOTHING, 0.01f, 0.99f), false); mouthBulgeSmoothSlider.label = "Throat Bulge Smooth"; _mouthBulgeSmoothingJSON.storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(mouthBulgeSmoothSlider); RegisterFloat(_mouthBulgeSmoothingJSON); UIDynamicSlider bellyBulgeSmoothSlider = CreateSlider(_bellyBulgeSmoothingJSON = new JSONStorableFloat("Belly Bulge Smoothing", BULGE_DEFAULT_MORPH_APPLY_SMOOTHING, 0.01f, 0.99f), false); bellyBulgeSmoothSlider.label = "Belly Bulge Smooth"; _bellyBulgeSmoothingJSON.storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(bellyBulgeSmoothSlider); RegisterFloat(_bellyBulgeSmoothingJSON); _morphMultiplierAJSON = new JSONStorableFloat("Anal Morph Multiplier (Dist)", 10.0f, 0.0f, 20.0f); RegisterFloat(_morphMultiplierAJSON); _morphOffsetAJSON = new JSONStorableFloat("Anal Morph Offset (Dist)", -0.24f, -1.0f, 1.0f); RegisterFloat(_morphOffsetAJSON); _morphMultiplierVJSON = new JSONStorableFloat("Vaginal Morph Multiplier (Dist)", 10.0f, 0.0f, 20.0f); RegisterFloat(_morphMultiplierVJSON); _morphOffsetVJSON = new JSONStorableFloat("Vaginal Morph Offset (Dist)", -0.22f, -1.0f, 1.0f); RegisterFloat(_morphOffsetVJSON); UIDynamicSlider twinkDelaySlider = CreateSlider(_twinkStartDelayJSON = new JSONStorableFloat("Twink Start Delay", 1.00f, TWINK_DELAY_MIN, DECAY_TIME_MAX), false); twinkDelaySlider.label = "Twink Start Delay"; _twinkStartDelayJSON.storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(twinkDelaySlider); RegisterFloat(_twinkStartDelayJSON); UIDynamicSlider vagDecayTimeSlider = CreateSlider(_morphDecayTimeVJSON = new JSONStorableFloat("Vaginal Gape Time", 0.50f, DECAY_TIME_MIN, DECAY_TIME_MAX), false); vagDecayTimeSlider.label = "Vaginal Gape Hold"; _morphDecayTimeVJSON.storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(vagDecayTimeSlider); RegisterFloat(_morphDecayTimeVJSON); UIDynamicSlider mouthDecayTimeSlider = CreateSlider(_morphDecayTimeMJSON = new JSONStorableFloat("Mouth Gape Time", 0.60f, DECAY_TIME_MIN, DECAY_TIME_MAX), false); mouthDecayTimeSlider.label = "Mouth Gape Hold"; _morphDecayTimeMJSON.storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(mouthDecayTimeSlider); RegisterFloat(_morphDecayTimeMJSON); var btnBulgeReset = CreateButton("Reload Bulge Morphs", false); btnBulgeReset.button.onClick.AddListener(_bulge_FullReset); _advancedSettingsControls.Add(btnBulgeReset); // In/Out Motion (Advanced) (RIGHT) _advancedSettingsControls.Add(CreateSpacerControl(false,0f)); _advancedSettingsControls.Add(CreateDisplayText("--- In/Out Motion (Advanced) ---", true, 30, mainTitleHeight)); _advancedSettingsControls.Add(CreateSpacerControl(false,0f)); _advancedSettingsControls.Add(CreateDisplayText("Motion: Strength, Smoothing, Detection Threshold", true, 24, subTitleHeight)); _advancedSettingsControls.Add(CreateSpacerControl(false,0f)); UIDynamicSlider lipsInStrengthSlider = CreateSlider(_motionMorphStrengthLipsInJSON = new JSONStorableFloat("Lips In Strength", INOUT_DEFAULT_MORPH_STRENGTH_LIPS_IN, 0.0f, 2.0f), true); lipsInStrengthSlider.label = "Lips In Strength"; _motionMorphStrengthLipsInJSON.storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(lipsInStrengthSlider); RegisterFloat(_motionMorphStrengthLipsInJSON); _advancedSettingsControls.Add(CreateSpacerControl(false,0f)); UIDynamicSlider lipsOutStrengthSlider = CreateSlider(_motionMorphStrengthLipsOutJSON = new JSONStorableFloat("Lips Out Strength", INOUT_DEFAULT_MORPH_STRENGTH_LIPS_OUT, 0.0f, 2.0f), true); lipsOutStrengthSlider.label = "Lips Out Strength"; _motionMorphStrengthLipsOutJSON.storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(lipsOutStrengthSlider); RegisterFloat(_motionMorphStrengthLipsOutJSON); _advancedSettingsControls.Add(CreateSpacerControl(false,0f)); UIDynamicSlider vaginaMotionStrengthSlider = CreateSlider(_motionMorphStrengthVaginaJSON = new JSONStorableFloat("Vagina Motion Strength", INOUT_DEFAULT_MORPH_STRENGTH_VAGINA, 0.0f, 2.0f), true); vaginaMotionStrengthSlider.label = "Vagina Motion Strength"; _motionMorphStrengthVaginaJSON.storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(vaginaMotionStrengthSlider); RegisterFloat(_motionMorphStrengthVaginaJSON); _advancedSettingsControls.Add(CreateSpacerControl(false,0f)); UIDynamicSlider analMotionStrengthSlider = CreateSlider(_motionMorphStrengthAnalJSON = new JSONStorableFloat("Anal Motion Strength", INOUT_DEFAULT_MORPH_STRENGTH_ANAL, 0.0f, 2.0f), true); analMotionStrengthSlider.label = "Anal Motion Strength"; _motionMorphStrengthAnalJSON.storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(analMotionStrengthSlider); RegisterFloat(_motionMorphStrengthAnalJSON); _advancedSettingsControls.Add(CreateSpacerControl(false,0f)); UIDynamicSlider motionSmoothSlider = CreateSlider(_motionMorphSmoothingJSON = new JSONStorableFloat("Motion Morph Apply Smooth", INOUT_DEFAULT_SMOOTHING, 0.01f, 0.99f), true); motionSmoothSlider.label = "Motion Apply Smooth"; _motionMorphSmoothingJSON.storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(motionSmoothSlider); RegisterFloat(_motionMorphSmoothingJSON); _advancedSettingsControls.Add(CreateSpacerControl(false,0f)); UIDynamicSlider motionDecaySlider = CreateSlider(_motionMorphDecaySmoothingJSON = new JSONStorableFloat("Motion Morph Decay Smooth", INOUT_DEFAULT_DECAY_SMOOTHING, 0.01f, 0.99f), true); motionDecaySlider.label = "Motion Hold Smooth"; _motionMorphDecaySmoothingJSON.storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(motionDecaySlider); RegisterFloat(_motionMorphDecaySmoothingJSON); _advancedSettingsControls.Add(CreateSpacerControl(false,0f)); UIDynamicSlider motionThresholdSlider = CreateSlider(_motionThresholdJSON = new JSONStorableFloat("Motion Threshold", INOUT_DEFAULT_MOTION_THRESHOLD, 0.00001f, 0.01f), true); motionThresholdSlider.label = "Motion Threshold"; _motionThresholdJSON.storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(motionThresholdSlider); RegisterFloat(_motionThresholdJSON); // Bulge Morph Strengths (RIGHT) _advancedSettingsControls.Add(CreateSpacerControl(false,0f)); _advancedSettingsControls.Add(CreateDisplayText("--- Bulge Morph Strengths ---", true, 30, mainTitleHeight)); _advancedSettingsControls.Add(CreateSpacerControl(false,0f)); _advancedSettingsControls.Add(CreateDisplayText("Throat Bulge Morph Multipliers", true, 24, subTitleHeight)); float[] defaultMouthMorphMults = { 0.85f, 0.60f, 0.50f }; for (int i = 0; i < BULGE_NUM_MOUTH_MORPHS; i++) { _advancedSettingsControls.Add(CreateSpacerControl(false,0f)); UIDynamicSlider s = CreateSlider(_bulge_uiMouthMorphMults[i] = new JSONStorableFloat($"Mouth Bulge Morph {i + 1} Mult", defaultMouthMorphMults[i], 0.0f, 1.0f), true); s.label = $"Throat Bulge Morph {i + 1}"; _bulge_uiMouthMorphMults[i].storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(s); RegisterFloat(_bulge_uiMouthMorphMults[i]); } _advancedSettingsControls.Add(CreateSpacerControl(false,0f)); _advancedSettingsControls.Add(CreateDisplayText("Belly Bulge Morph Multipliers", true, 24, subTitleHeight)); float[] defaultBellyMorphMults = { 0.54f, 0.65f, 1.00f, 0.24f, 0.22f }; for (int i = 0; i < BULGE_NUM_BELLY_MORPHS; i++) { _advancedSettingsControls.Add(CreateSpacerControl(false,0f)); UIDynamicSlider s = CreateSlider(_bulge_uiBellyMorphMults[i] = new JSONStorableFloat($"Belly Bulge Morph {i + 1} Mult", defaultBellyMorphMults[i], 0.0f, 1.0f), true); s.label = $"Belly Bulge Morph {i + 1}"; _bulge_uiBellyMorphMults[i].storeType = JSONStorableParam.StoreType.Full; _advancedSettingsControls.Add(s); RegisterFloat(_bulge_uiBellyMorphMults[i]); } _agg_main_init_done = true; StartCoroutine(PostInitFindData()); StartCoroutine(DelayedInitialTabSet()); } catch (Exception e) { SuperController.LogError($"Exception caught in {LOG_PREFIX}.Init: {e}"); } } private IEnumerator DelayedInitialTabSet() { yield return new WaitForSeconds(0.2f); SetActiveUITab("Main"); } private void SetActiveUITab(string tabName) { _activeUITab = tabName; bool mainActive = (tabName == "Main"); bool advancedActive = (tabName == "Advanced"); if (_mainSettingsControls != null) { foreach (UIDynamic control in _mainSettingsControls) { if (control != null && control.gameObject != null) control.gameObject.SetActive(mainActive); } } if (_advancedSettingsControls != null) { foreach (UIDynamic control in _advancedSettingsControls) { if (control != null && control.gameObject != null) control.gameObject.SetActive(advancedActive); } } if (_mainSettingsTabButton != null && _mainSettingsTabButton.gameObject != null) { Image img = _mainSettingsTabButton.GetComponentInChildren(); if (img != null) img.color = mainActive ? _activeTabColorUI : _inactiveTabColorUI; } if (_advancedSettingsTabButton != null && _advancedSettingsTabButton.gameObject != null) { Image img = _advancedSettingsTabButton.GetComponentInChildren(); if (img != null) img.color = advancedActive ? _activeTabColorUI : _inactiveTabColorUI; } } private void HandleFemaleAnatomyToggle(bool disableBase) { if (!_femaleAnatomyStorableFound || _femaleAnatomyEnabledJSON == null) return; if (disableBase) { if (_femaleAnatomyEnabledJSON.val) { _femaleAnatomyEnabledJSON.val = false; _pluginEnabledFemaleAnatomyOverride = true; } } else { if (_pluginEnabledFemaleAnatomyOverride) { if (_originalFemaleAnatomyEnabledState && !_femaleAnatomyEnabledJSON.val) { _femaleAnatomyEnabledJSON.val = true; } _pluginEnabledFemaleAnatomyOverride = false; } } } private IEnumerator PostInitFindData() { yield return new WaitForEndOfFrame(); if (!_isPersonAtom) { _fullInitializationRoutineComplete = true; yield break; } FindAtomElementsAGG(); if (_femaleAnatomyStorableFound && _femaleAnatomyEnabledJSON != null) { _originalFemaleAnatomyEnabledState = _femaleAnatomyEnabledJSON.val; if(_disableBaseAutoGensJSON != null) _disableBaseAutoGensJSON.setCallbackFunction = HandleFemaleAnatomyToggle; } if ((_enableAutoGensGapeModuleJSON?.val ?? false) || (_enableOptimizedPhysicsJSON?.val ?? false) || (_softVerticesColliderRadiusFound || _softVerticesDistanceLimitFound) ) { _personColliders = _personAtom.gameObject.GetComponentsInChildren(true).ToList(); if (_personColliders == null) _personColliders = new List(); FindHardcodedMorphsAGG(); if (_physicsFrictionStorablesFound) { _originalDynamicFriction = _dynamicFrictionJSON.val; _originalStaticFriction = _staticFrictionJSON.val; _originalFrictionCombine = _frictionCombineJSON.val; } if (_tongueCollisionStorableFound && _tongueCollisionJSONStorable != null) { _originalTongueCollisionState = _tongueCollisionJSONStorable.val; } if (_jawControlStorableFound && _jawSpringJSON != null) _originalJawSpring = _jawSpringJSON.val; if (_softVerticesColliderRadiusFound) { _originalSoftVerticesColliderRadius = _softVerticesColliderRadiusJSON.val; _currentSoftVerticesColliderRadius = _originalSoftVerticesColliderRadius; } if (_softVerticesDistanceLimitFound) { _originalSoftVerticesDistanceLimit = _softVerticesDistanceLimitJSON.val; _currentSoftVerticesDistanceLimit = _originalSoftVerticesDistanceLimit; } _tongueCollisionOverridden = false; _jawSpringOverridden = false; _softVerticesParamsOverridden = false; _frictionOverriddenByLipTrigger = false; _smoothedMorphValueA = _selectedMorphA?.morphValue ?? 0f; _currentSmoothedAdditiveAnalValue = 0f; _smoothedMorphValueV = _selectedMorphV?.morphValue ?? 0f; _currentSmoothedAdditiveVaginalValue = 0f; _smoothedMorphValueM = _selectedMorphM_AGG?.morphValue ?? 0f; _isDecayingA = false; _isTwinkingA = false; _nextTwinkTimeA = 0f; _isDecayingV = false; } if (_enableBulgeModuleJSON?.val ?? false) { _bulge_Initialize(); } CacheInOutMorphs(); _previousLipsDepthInOut = 0f; _previousAbdomenDepthInOut = 0f; if(_agg_main_init_done) { EnableAnalCallback(_enableMorphDriveAJSON.val); EnableVaginalCallback(_enableMorphDriveVJSON.val); EnableMouthCallback(_enableMorphDriveMJSON.val); } HandleFemaleAnatomyOnLoadOrEnable(); _fullInitializationRoutineComplete = true; } private void HandleFemaleAnatomyOnLoadOrEnable() { if (_disableBaseAutoGensJSON != null) { HandleFemaleAnatomyToggle(_disableBaseAutoGensJSON.val); } } private void CacheInOutMorphs() { if (_characterSelector == null || _characterSelector.morphsControlUI == null) return; GenerateDAZMorphsControlUI morphUI = _characterSelector.morphsControlUI; _selectedLipsMorphIn = morphUI.GetMorphByDisplayName(MORPH_LIPS_IN_NAME); _selectedLipsMorphOut = morphUI.GetMorphByDisplayName(MORPH_LIPS_OUT_NAME); _selectedVaginaMorphIn = morphUI.GetMorphByDisplayName(MORPH_VAGINAL_IN_NAME); _selectedVaginaMorphOut = morphUI.GetMorphByDisplayName(MORPH_VAGINAL_OUT_NAME); _selectedAnalMorphIn = morphUI.GetMorphByDisplayName(MORPH_ANAL_IN_NAME); _selectedAnalMorphOut = morphUI.GetMorphByDisplayName(MORPH_ANAL_OUT_NAME); } private void FindAtomElementsAGG() { if (!_isPersonAtom || _personAtom == null) return; _bonesAFound = false; _bonesVFound = false; Transform physicsRoot = null; JSONStorable atomPhysicsStorable = _personAtom.GetStorableByID("PhysicsModel"); if (atomPhysicsStorable != null) { physicsRoot = atomPhysicsStorable.transform; } else { Transform rescaleNode = _personAtom.transform.Find("rescaleNode"); if (rescaleNode != null) { physicsRoot = rescaleNode; } else { SuperController.LogError($"{LOG_PREFIX}: Could not find PhysicsModel or rescaleNode transform for AGG."); return; } } _boneA1Transform = FindTransformByNameInChildren(physicsRoot, BONE_A1_NAME); _boneA2Transform = FindTransformByNameInChildren(physicsRoot, BONE_A2_NAME); _bonesAFound = (_boneA1Transform != null && _boneA2Transform != null); _boneV1Transform = FindTransformByNameInChildren(physicsRoot, BONE_V1_NAME); _boneV2Transform = FindTransformByNameInChildren(physicsRoot, BONE_V2_NAME); _bonesVFound = (_boneV1Transform != null && _boneV2Transform != null); _lipTriggerTransformFound = false; JSONStorable lipTriggerStorable = _personAtom.GetStorableByID(LIP_TRIGGER_STORABLE_NAME); if (lipTriggerStorable != null) { _lipTriggerTransform = lipTriggerStorable.transform; _lipTriggerTransformFound = (_lipTriggerTransform != null); } _mouthTriggerComponentFound = false; _mouthCollisionTriggerComponent = _personAtom.GetStorableByID(MOUTH_TRIGGER_STORABLE_NAME) as CollisionTrigger; _mouthTriggerComponentFound = (_mouthCollisionTriggerComponent != null); _throatTriggerComponentFound = false; _throatCollisionTriggerComponent = _personAtom.GetStorableByID(THROAT_TRIGGER_STORABLE_NAME) as CollisionTrigger; _throatTriggerComponentFound = (_throatCollisionTriggerComponent != null); _vaginaTriggerComponentAGGFound = false; _vaginaCollisionTriggerComponentAGG = _personAtom.GetStorableByID(VAGINA_TRIGGER_STORABLE_NAME) as CollisionTrigger; _vaginaTriggerComponentAGGFound = (_vaginaCollisionTriggerComponentAGG != null); _tongueCollisionStorableFound = false; _tongueCollisionJSONStorable = null; JSONStorable tongueControlStorable = _personAtom.GetStorableByID(TONGUE_CONTROL_STORABLE_NAME); if (tongueControlStorable != null) { _tongueCollisionJSONStorable = tongueControlStorable.GetBoolJSONParam(TONGUE_COLLISION_PARAM_NAME); if (_tongueCollisionJSONStorable != null) { _tongueCollisionStorableFound = true; } } _femaleAnatomyStorableFound = false; _femaleAnatomyEnabledJSON = null; JSONStorable femaleAnatomyStorable = _personAtom.GetStorableByID(FEMALE_ANATOMY_STORABLE_NAME); if (femaleAnatomyStorable != null) { _femaleAnatomyEnabledJSON = femaleAnatomyStorable.GetBoolJSONParam(FEMALE_ANATOMY_ENABLED_PARAM_NAME); if (_femaleAnatomyEnabledJSON != null) { _femaleAnatomyStorableFound = true; } } _physicsFrictionStorablesFound = false; _physicsModelStorable = _personAtom.GetStorableByID(PHYSICS_MODEL_STORABLE_NAME); if (_physicsModelStorable != null) { _dynamicFrictionJSON = _physicsModelStorable.GetFloatJSONParam(DYNAMIC_FRICTION_PARAM_NAME); _staticFrictionJSON = _physicsModelStorable.GetFloatJSONParam(STATIC_FRICTION_PARAM_NAME); _frictionCombineJSON = _physicsModelStorable.GetStringChooserJSONParam(FRICTION_COMBINE_PARAM_NAME); if (_dynamicFrictionJSON != null && _staticFrictionJSON != null && _frictionCombineJSON != null) { _physicsFrictionStorablesFound = true; } } _softVerticesColliderRadiusFound = false; _softVerticesDistanceLimitFound = false; _lowerPhysicsMeshStorable = _personAtom.GetStorableByID(LOWER_PHYSICS_MESH_STORABLE_NAME); if (_lowerPhysicsMeshStorable != null) { _softVerticesColliderRadiusJSON = _lowerPhysicsMeshStorable.GetFloatJSONParam(SOFT_VERTICES_COLLIDER_RADIUS_PARAM_NAME); if (_softVerticesColliderRadiusJSON != null) { _softVerticesColliderRadiusFound = true; } _softVerticesDistanceLimitJSON = _lowerPhysicsMeshStorable.GetFloatJSONParam(SOFT_VERTICES_DISTANCE_LIMIT_PARAM_NAME); if (_softVerticesDistanceLimitJSON != null) { _softVerticesDistanceLimitFound = true; } } _jawControlStorableFound = false; _jawControlStorable = _personAtom.GetStorableByID(JAW_CONTROL_STORABLE_NAME); if (_jawControlStorable != null) { _jawSpringJSON = _jawControlStorable.GetFloatJSONParam(JAW_SPRING_PARAM_NAME); if (_jawSpringJSON != null) { _jawControlStorableFound = true; } } } private Transform FindTransformByNameInChildren(Transform parent, string name) { if (parent == null || string.IsNullOrEmpty(name)) return null; if (parent.name == name) return parent; foreach (Transform child in parent) { Transform result = FindTransformByNameInChildren(child, name); if (result != null) return result; } return null; } private void FindHardcodedMorphsAGG() { if (!_isPersonAtom || _characterSelector == null || _characterSelector.morphsControlUI == null) { return; } GenerateDAZMorphsControlUI morphUI = _characterSelector.morphsControlUI; try { _selectedMorphA = USE_DISPLAY_NAME_FOR_MORPHS ? morphUI.GetMorphByDisplayName(MORPH_A_DISPLAY_NAME) : morphUI.GetMorphByUid(MORPH_A_DISPLAY_NAME); } catch { SuperController.LogMessage($"{LOG_PREFIX}: Could not find morph: {MORPH_A_DISPLAY_NAME}"); _selectedMorphA = null;} try { _selectedMorphV = USE_DISPLAY_NAME_FOR_MORPHS ? morphUI.GetMorphByDisplayName(MORPH_V_DISPLAY_NAME) : morphUI.GetMorphByUid(MORPH_V_DISPLAY_NAME); } catch { SuperController.LogMessage($"{LOG_PREFIX}: Could not find morph: {MORPH_V_DISPLAY_NAME}"); _selectedMorphV = null; } try { _selectedMorphM_AGG = USE_DISPLAY_NAME_FOR_MORPHS ? morphUI.GetMorphByDisplayName(MORPH_M_DISPLAY_NAME) : morphUI.GetMorphByUid(MORPH_M_DISPLAY_NAME); } catch { SuperController.LogMessage($"{LOG_PREFIX}: Could not find morph: {MORPH_M_DISPLAY_NAME}"); _selectedMorphM_AGG = null; } } private void EnableAnalCallback(bool enabled) { if (!_agg_main_init_done && !_fullInitializationRoutineComplete) return; if (!enabled) { if (_selectedMorphA != null) { try { _selectedMorphA.morphValue = 0f; } catch { } } _smoothedMorphValueA = 0f; _currentSmoothedAdditiveAnalValue = 0f; _isDecayingA = false; _isTwinkingA = false; if (_enableAnalMotionJSON != null) _enableAnalMotionJSON.val = false; _DecayInOutMorph(ref _currentAnalMorphValueIn, _selectedAnalMorphIn); _DecayInOutMorph(ref _currentAnalMorphValueOut, _selectedAnalMorphOut); } else { _smoothedMorphValueA = _selectedMorphA?.morphValue ?? 0f; _currentSmoothedAdditiveAnalValue = 0f; } } private void EnableVaginalCallback(bool enabled) { if (!_agg_main_init_done && !_fullInitializationRoutineComplete) return; if (!enabled) { if (_selectedMorphV != null) { try { _selectedMorphV.morphValue = 0f; } catch { } } _smoothedMorphValueV = 0f; _currentSmoothedAdditiveVaginalValue = 0f; _isDecayingV = false; if (_enableVaginaMotionJSON != null) _enableVaginaMotionJSON.val = false; _DecayInOutMorph(ref _currentVaginaMorphValueIn, _selectedVaginaMorphIn); _DecayInOutMorph(ref _currentVaginaMorphValueOut, _selectedVaginaMorphOut); } else { _smoothedMorphValueV = _selectedMorphV?.morphValue ?? 0f; _currentSmoothedAdditiveVaginalValue = 0f; } } private void EnableMouthCallback(bool enabled) { if (!enabled) { if (_selectedMorphM_AGG != null) { try { _selectedMorphM_AGG.morphValue = 0f; } catch { } } _smoothedMorphValueM = 0f; if (_frictionOverriddenByLipTrigger) { RestoreNativeFrictionSettings(); _frictionOverriddenByLipTrigger = false; } } else { _smoothedMorphValueM = _selectedMorphM_AGG?.morphValue ?? 0f; } } private void EnableOptimizedPhysicsCallback(bool enabled) { if (!_fullInitializationRoutineComplete) return; if (!enabled) { RestoreOtherOptimizedPhysicsSettings(); RestoreSoftVerticesParameters(); // Explicitly restore soft vertices when toggle is OFF _optimizedPhysicsStateRestored = true; } else { _optimizedPhysicsStateRestored = false; // Ensure current soft-vertices are the original ones to allow lerping to targets from a clean state if(_softVerticesColliderRadiusFound) _currentSoftVerticesColliderRadius = _originalSoftVerticesColliderRadius; if(_softVerticesDistanceLimitFound) _currentSoftVerticesDistanceLimit = _originalSoftVerticesDistanceLimit; } } private bool _isCuaCollisionPermitted(Atom potentialCuaAtom) { if (potentialCuaAtom == null || potentialCuaAtom.type != "CustomUnityAsset") return true; JSONStorableBool collisionEnabledStorable; if (_cuaCollisionEnabledStorables.TryGetValue(potentialCuaAtom, out collisionEnabledStorable)) return collisionEnabledStorable == null || collisionEnabledStorable.val; JSONStorable atomControl = potentialCuaAtom.GetStorableByID(CUA_ATOM_CONTROL_ID); if (atomControl != null) { collisionEnabledStorable = atomControl.GetBoolJSONParam(CUA_COLLISION_ENABLED_PARAM); _cuaCollisionEnabledStorables[potentialCuaAtom] = collisionEnabledStorable; return collisionEnabledStorable == null || collisionEnabledStorable.val; } _cuaCollisionEnabledStorables[potentialCuaAtom] = null; return true; } private bool IsColliderAllowedForBoneJointAGG(Collider col) { if (col == null) return false; Atom colliderAtom = col.GetComponentInParent(); if (colliderAtom == _personAtom) { return col.attachedRigidbody != null && _allowedSelfCollisionRigidbodyNames.Contains(col.attachedRigidbody.name); } else if (colliderAtom != null) { return _isCuaCollisionPermitted(colliderAtom); } return true; } private void CheckCollisionsAGG() { if (!_isPersonAtom || _personAtom == null ) return; if (_personColliders == null) _personColliders = new List(); _isCollidingA1 = false; _isCollidingA2 = false; _isCollidingV1 = false; _isCollidingV2 = false; _isCollidingLip = false; float currentAnalRadius = _analBoneJointRadiusJSON?.val ?? DEFAULT_GENITAL_JOINT_RADIUS; float currentVaginalRadius = _vaginalBoneJointRadiusJSON?.val ?? DEFAULT_GENITAL_JOINT_RADIUS; if (_bonesAFound) { if (_boneA1Transform != null) { Collider[] cs = Physics.OverlapSphere(_boneA1Transform.position, currentAnalRadius); foreach (Collider c in cs) if (IsColliderAllowedForBoneJointAGG(c)) { _isCollidingA1 = true; break; } } if (_boneA2Transform != null) { Collider[] cs = Physics.OverlapSphere(_boneA2Transform.position, currentAnalRadius); foreach (Collider c in cs) if (IsColliderAllowedForBoneJointAGG(c)) { _isCollidingA2 = true; break; } } } if (_bonesVFound) { if (_boneV1Transform != null) { Collider[] cs = Physics.OverlapSphere(_boneV1Transform.position, currentVaginalRadius); foreach (Collider c in cs) if (IsColliderAllowedForBoneJointAGG(c)) { _isCollidingV1 = true; break; } } if (_boneV2Transform != null) { Collider[] cs = Physics.OverlapSphere(_boneV2Transform.position, currentVaginalRadius); foreach (Collider c in cs) if (IsColliderAllowedForBoneJointAGG(c)) { _isCollidingV2 = true; break; } } } if (_lipTriggerTransformFound && _lipTriggerTransform != null && _lipTriggerRadiusJSON != null) { Collider[] collidersLip = Physics.OverlapSphere(_lipTriggerTransform.position, _lipTriggerRadiusJSON.val); foreach (Collider colIn in collidersLip) { if (colIn != null && !_personColliders.Contains(colIn)) { Atom colliderAtom = colIn.GetComponentInParent(); if (colliderAtom != _personAtom) { if (!_isCuaCollisionPermitted(colliderAtom)) continue; _isCollidingLip = true; break; } } } } _isCollidingMouthAGG = _mouthTriggerComponentFound && _mouthCollisionTriggerComponent != null && _mouthCollisionTriggerComponent.trigger.active; _isCollidingThroatAGG = _throatTriggerComponentFound && _throatCollisionTriggerComponent != null && _throatCollisionTriggerComponent.trigger.active; _isCollidingVaginaTriggerAGG = _vaginaTriggerComponentAGGFound && _vaginaCollisionTriggerComponentAGG != null && _vaginaCollisionTriggerComponentAGG.trigger.active; } private void CalculateDistanceA_AGG() { _currentDistanceA = (_bonesAFound && _boneA1Transform != null && _boneA2Transform != null) ? Vector3.Distance(_boneA1Transform.position, _boneA2Transform.position) : 0f; } private void CalculateDistanceV_AGG() { _currentDistanceV = (_bonesVFound && _boneV1Transform != null && _boneV2Transform != null) ? Vector3.Distance(_boneV1Transform.position, _boneV2Transform.position) : 0f; } private void ManageOtherOptimizedPhysics() { UpdateTongueCollisionAGG(); UpdateJawSpringAGG(); UpdateFrictionSettingsAGG(); } private void RestoreOtherOptimizedPhysicsSettings() { RestoreTongueCollisionAGG(true); RestoreJawSpringAGG(true); if(_frictionOverriddenByLipTrigger) RestoreNativeFrictionSettings(); } private void UpdateFrictionSettingsAGG() { if (!_physicsFrictionStorablesFound) return; if (_isCollidingLip) { if (!_frictionOverriddenByLipTrigger) { _dynamicFrictionJSON.val = 0f; _staticFrictionJSON.val = 0f; _frictionCombineJSON.val = "Minimum"; _frictionOverriddenByLipTrigger = true; } } else { if (_frictionOverriddenByLipTrigger) { RestoreNativeFrictionSettings(); _frictionOverriddenByLipTrigger = false; } } } private void RestoreNativeFrictionSettings() { if (_physicsFrictionStorablesFound) { if (_dynamicFrictionJSON.val != _originalDynamicFriction) _dynamicFrictionJSON.val = _originalDynamicFriction; if (_staticFrictionJSON.val != _originalStaticFriction) _staticFrictionJSON.val = _originalStaticFriction; if (_frictionCombineJSON.val != _originalFrictionCombine) _frictionCombineJSON.val = _originalFrictionCombine; } _frictionOverriddenByLipTrigger = false; } private void UpdateTongueCollisionAGG() { if (!_tongueCollisionStorableFound || _tongueCollisionJSONStorable == null) return; if (_isCollidingLip) { if (_tongueCollisionJSONStorable.val) { _tongueCollisionJSONStorable.val = false; _tongueCollisionOverridden = true; } } else { if (_tongueCollisionOverridden) { _tongueCollisionJSONStorable.val = _originalTongueCollisionState; _tongueCollisionOverridden = false; } } } private void RestoreTongueCollisionAGG(bool force = false) { if (_tongueCollisionStorableFound && _tongueCollisionJSONStorable != null) { if (force || _tongueCollisionOverridden) { if (_tongueCollisionJSONStorable.val != _originalTongueCollisionState) { _tongueCollisionJSONStorable.val = _originalTongueCollisionState; } } if (force) _tongueCollisionOverridden = false; } } private void UpdateJawSpringAGG() { if (!_jawControlStorableFound || _jawSpringJSON == null) return; if (_isCollidingLip) { if (_jawSpringJSON.val != TARGET_JAW_SPRING_VALUE) { _jawSpringJSON.val = TARGET_JAW_SPRING_VALUE; _jawSpringOverridden = true; } } else { if (_jawSpringOverridden) { _jawSpringJSON.val = _originalJawSpring; _jawSpringOverridden = false; } } } private void RestoreJawSpringAGG(bool force = false) { if (_jawControlStorableFound && _jawSpringJSON != null) { if (force || _jawSpringOverridden) { if (_jawSpringJSON.val != _originalJawSpring) { _jawSpringJSON.val = _originalJawSpring; } } if (force) _jawSpringOverridden = false; } } private void RestoreFemaleAnatomyState() { if (_femaleAnatomyStorableFound && _femaleAnatomyEnabledJSON != null) { if (_pluginEnabledFemaleAnatomyOverride && _originalFemaleAnatomyEnabledState) { if (!_femaleAnatomyEnabledJSON.val) { _femaleAnatomyEnabledJSON.val = true; } } } _pluginEnabledFemaleAnatomyOverride = false; } private void UpdateSoftVerticesParametersAGG() { if (!_softVerticesColliderRadiusFound && !_softVerticesDistanceLimitFound) return; float targetRadius, targetLimit; float smoothingAlpha = 1.0f - (_softVerticesSmoothingJSON?.val ?? SOFT_VERTICES_SMOOTHING_DEFAULT); // Determine if conditions are met to apply target "optimized" values bool applyOptimizedTargets = (_isCollidingA1 || _isCollidingA2 || _isCollidingV1 || _isCollidingV2) && !_isCollidingVaginaTriggerAGG; if ((_enableOptimizedPhysicsJSON?.val ?? false) && applyOptimizedTargets) { targetRadius = TARGET_SOFT_VERTICES_COLLIDER_RADIUS; targetLimit = TARGET_SOFT_VERTICES_DISTANCE_LIMIT; if (!_softVerticesParamsOverridden) _softVerticesParamsOverridden = true; } else { targetRadius = _originalSoftVerticesColliderRadius; targetLimit = _originalSoftVerticesDistanceLimit; // If Optimized Physics is off, or conditions no longer met, ensure we are not marked as overridden if (_softVerticesParamsOverridden && !(_enableOptimizedPhysicsJSON?.val ?? false) ) { _softVerticesParamsOverridden = false; } else if (_softVerticesParamsOverridden && (_enableOptimizedPhysicsJSON?.val ?? false) && !applyOptimizedTargets) { // Still in optimized mode, but conditions for target are not met, so lerp back to original but remain "overridden" in principle // This case might need refinement based on desired behavior when Optimized is ON but no penetration. // For now, it will lerp back to original. } } if (_softVerticesColliderRadiusFound && _softVerticesColliderRadiusJSON != null) { _currentSoftVerticesColliderRadius = Mathf.Lerp(_currentSoftVerticesColliderRadius, targetRadius, smoothingAlpha); _softVerticesColliderRadiusJSON.val = _currentSoftVerticesColliderRadius; } if (_softVerticesDistanceLimitFound && _softVerticesDistanceLimitJSON != null) { _currentSoftVerticesDistanceLimit = Mathf.Lerp(_currentSoftVerticesDistanceLimit, targetLimit, smoothingAlpha); _softVerticesDistanceLimitJSON.val = _currentSoftVerticesDistanceLimit; } } private void RestoreSoftVerticesParameters() { if (_softVerticesColliderRadiusFound && _softVerticesColliderRadiusJSON != null) { _softVerticesColliderRadiusJSON.val = _originalSoftVerticesColliderRadius; _currentSoftVerticesColliderRadius = _originalSoftVerticesColliderRadius; } if (_softVerticesDistanceLimitFound && _softVerticesDistanceLimitJSON != null) { _softVerticesDistanceLimitJSON.val = _originalSoftVerticesDistanceLimit; _currentSoftVerticesDistanceLimit = _originalSoftVerticesDistanceLimit; } _softVerticesParamsOverridden = false; } private void DriveMorphA_AGG() { if (_selectedMorphA == null || _morphMultiplierAJSON == null || _morphOffsetAJSON == null || _morphSmoothInAJSON == null || _additiveAnalMorphValueJSON == null || _overallStrengthJSON == null || _twinkStartDelayJSON == null || _morphDecayTimeAJSON == null || _twinkEnabledJSON == null || _twinkIntervalMeanJSON == null || _twinkIntervalDeltaJSON == null ) return; float targetMorphValueDist = _currentDistanceA * _morphMultiplierAJSON.val + _morphOffsetAJSON.val; float clampedTargetValueDist = Mathf.Clamp01(targetMorphValueDist); if (targetMorphValueDist >= _smoothedMorphValueA) { _isDecayingA = false; _isTwinkingA = false; float smoothingFactor = 1.0f - _morphSmoothInAJSON.val; _smoothedMorphValueA = Mathf.Lerp(_smoothedMorphValueA, clampedTargetValueDist, smoothingFactor); } else { if (!_isDecayingA) { _isDecayingA = true; _decayStartTimeA = Time.time; _decayStartValueA = _smoothedMorphValueA; _nextTwinkTimeA = _twinkStartDelayJSON.val; _isTwinkingA = false; } float decayDuration = _morphDecayTimeAJSON.val; if (decayDuration <= 0f) { _smoothedMorphValueA = 0f; _isDecayingA = false; _isTwinkingA = false; } else { float elapsedTime = Time.time - _decayStartTimeA; float decayT = Mathf.Clamp01(elapsedTime / decayDuration); _decayTargetValueA = Mathf.Lerp(_decayStartValueA, 0f, decayT); _smoothedMorphValueA = _decayTargetValueA; if (_twinkEnabledJSON.val) { if (elapsedTime >= _twinkStartDelayJSON.val) { _nextTwinkTimeA -= Time.deltaTime; } if (!_isTwinkingA && _nextTwinkTimeA <= 0f && elapsedTime >= _twinkStartDelayJSON.val && decayT < 1.0f) { _isTwinkingA = true; float mean = _twinkIntervalMeanJSON.val; float delta = _twinkIntervalDeltaJSON.val; delta = Mathf.Min(delta, mean * 0.95f); float interval = Random.Range(mean - delta, mean + delta); _nextTwinkTimeA = interval; _currentTwinkDurationA = interval * Random.Range(0.4f, 0.6f); _currentTwinkCloseTimeA = _currentTwinkDurationA * Random.Range(0.1f, 0.2f); _twinkPhaseTimerA = 0f; _twinkFactorA1_A = (_currentTwinkCloseTimeA > 0.001f) ? Mathf.Pow(2f, 1f / _currentTwinkCloseTimeA) : 2f; float openTime = _currentTwinkDurationA - _currentTwinkCloseTimeA; _twinkFactorA2_A = (openTime > 0.001f) ? Mathf.Pow(.001f, -1f / openTime) : 1000f; } if (_isTwinkingA) { _twinkPhaseTimerA += Time.deltaTime; float t = _twinkPhaseTimerA; float closeTime = _currentTwinkCloseTimeA; float twinkMultiplier = 1f; if (t < closeTime) { twinkMultiplier = (2f - Mathf.Pow(_twinkFactorA1_A, t)); } else { twinkMultiplier = (1f - Mathf.Pow(_twinkFactorA2_A, closeTime - t)); } _smoothedMorphValueA = _decayTargetValueA * Mathf.Clamp01(twinkMultiplier); if (_twinkPhaseTimerA >= _currentTwinkDurationA) { _isTwinkingA = false; _smoothedMorphValueA = _decayTargetValueA; } } } else { _isTwinkingA = false; } if (decayT >= 1.0f) { _isDecayingA = false; _isTwinkingA = false; _smoothedMorphValueA = 0f; } } } float targetAdditiveAnalValue = (_vaginaTriggerComponentAGGFound && _isCollidingVaginaTriggerAGG) ? 0f : ((_bonesAFound && (_isCollidingA1 || _isCollidingA2)) ? (_additiveAnalMorphValueJSON.val) : 0f); float additiveSmoothingLerpAlpha = 1.0f - _morphSmoothInAJSON.val; _currentSmoothedAdditiveAnalValue = Mathf.Lerp(_currentSmoothedAdditiveAnalValue, targetAdditiveAnalValue, additiveSmoothingLerpAlpha); float combinedValue = _smoothedMorphValueA + _currentSmoothedAdditiveAnalValue; float finalValueToApplyA = Mathf.Clamp01(combinedValue * _overallStrengthJSON.val); try { _selectedMorphA.morphValue = finalValueToApplyA; } catch { _selectedMorphA = null; EnableAnalCallback(false); } } private void DriveMorphV_AGG() { if (_selectedMorphV == null || _morphMultiplierVJSON == null || _morphOffsetVJSON == null || _morphSmoothInVJSON == null || _additiveVaginalMorphValueJSON == null || _overallStrengthJSON == null || _morphDecayTimeVJSON == null) return; float targetMorphValueDist = _currentDistanceV * _morphMultiplierVJSON.val + _morphOffsetVJSON.val; float clampedTargetValueDist = Mathf.Clamp01(targetMorphValueDist); if (targetMorphValueDist >= _smoothedMorphValueV) { _isDecayingV = false; float smoothingFactor = 1.0f - _morphSmoothInVJSON.val; _smoothedMorphValueV = Mathf.Lerp(_smoothedMorphValueV, clampedTargetValueDist, smoothingFactor); } else { if (!_isDecayingV) { _isDecayingV = true; _decayStartTimeV = Time.time; _decayStartValueV = _smoothedMorphValueV; } float decayDuration = _morphDecayTimeVJSON.val; if (decayDuration <= 0f) { _smoothedMorphValueV = 0f; _isDecayingV = false; } else { float elapsedTime = Time.time - _decayStartTimeV; float decayT = Mathf.Clamp01(elapsedTime / decayDuration); _decayTargetValueV = Mathf.Lerp(_decayStartValueV, 0f, decayT); _smoothedMorphValueV = _decayTargetValueV; if (decayT >= 1.0f) { _isDecayingV = false; _smoothedMorphValueV = 0f; } } } float targetAdditiveVaginalValue = (_vaginaTriggerComponentAGGFound && _isCollidingVaginaTriggerAGG) ? 0f : ((_bonesVFound && (_isCollidingV1 || _isCollidingV2)) ? (_additiveVaginalMorphValueJSON.val) : 0f); float additiveSmoothingLerpAlpha = 1.0f - _morphSmoothInVJSON.val; _currentSmoothedAdditiveVaginalValue = Mathf.Lerp(_currentSmoothedAdditiveVaginalValue, targetAdditiveVaginalValue, additiveSmoothingLerpAlpha); float combinedValue = _smoothedMorphValueV + _currentSmoothedAdditiveVaginalValue; float finalValueToApplyV = Mathf.Clamp01(combinedValue * _overallStrengthJSON.val); try { _selectedMorphV.morphValue = finalValueToApplyV; } catch { _selectedMorphV = null; EnableVaginalCallback(false); } } private void DriveMorphM_AGG() { if (_selectedMorphM_AGG == null || _additiveMouthMorphValueJSON == null || _overallStrengthJSON == null || _morphSmoothInMJSON == null ) return; float targetMorphValueM = (_mouthTriggerComponentFound && _isCollidingMouthAGG) ? 0f : ((_lipTriggerTransformFound && _isCollidingLip) ? (_additiveMouthMorphValueJSON.val) : 0f); float smoothingLerpAlpha = 1.0f - _morphSmoothInMJSON.val; _smoothedMorphValueM = Mathf.Lerp(_smoothedMorphValueM, targetMorphValueM, smoothingLerpAlpha); float finalValueToApplyM = Mathf.Clamp01(_smoothedMorphValueM * _overallStrengthJSON.val); finalValueToApplyM = Mathf.Min(finalValueToApplyM, 0.5f); try { _selectedMorphM_AGG.morphValue = finalValueToApplyM; } catch { _selectedMorphM_AGG = null; EnableMouthCallback(false); } } private void _bulge_Initialize() { if (_personAtom == null) return; _bulge_mouthReferenceControl = _personAtom.GetStorableByID("headControl") as FreeControllerV3 ?? _personAtom.GetStorableByID("neckControl") as FreeControllerV3; _bulge_bellyReferenceControl = _personAtom.GetStorableByID("pelvisControl") as FreeControllerV3 ?? _personAtom.GetStorableByID("hipControl") as FreeControllerV3; _bulge_mouthTrigger = _personAtom.GetStorableByID("MouthTrigger") as CollisionTrigger; _bulge_vaginaTrigger = _personAtom.GetStorableByID("VaginaTrigger") as CollisionTrigger; if (_bulge_mouthTrigger != null) { _bulge_mouthTrigger.triggerEnabledJSON.val = true; _bulge_mouthTriggerEvtHandler = _bulge_mouthTrigger.GetComponent(); } if (_bulge_vaginaTrigger != null) { _bulge_vaginaTrigger.triggerEnabledJSON.val = true; _bulge_vaginaTriggerEvtHandler = _bulge_vaginaTrigger.GetComponent(); } _bulge_FullReset(); } private void _bulge_FullReset() { _bulge_ResetAllMorphValues(); _bulge_LoadAllMorphs(); _bulge_needsReset = false; _bulge_lastRawMouthDist = BULGE_MOUTH_DIST_REMAP_MAX_DEFAULT + 0.1f; _bulge_lastRawBellyDist = BULGE_BELLY_DIST_REMAP_MAX_DEFAULT + 0.1f; if (_selectedLipsMorphIn != null) _selectedLipsMorphIn.morphValue = 0f; if (_selectedLipsMorphOut != null) _selectedLipsMorphOut.morphValue = 0f; if (_selectedVaginaMorphIn != null) _selectedVaginaMorphIn.morphValue = 0f; if (_selectedVaginaMorphOut != null) _selectedVaginaMorphOut.morphValue = 0f; if (_selectedAnalMorphIn != null) _selectedAnalMorphIn.morphValue = 0f; if (_selectedAnalMorphOut != null) _selectedAnalMorphOut.morphValue = 0f; _currentLipsMorphValueIn = 0f; _currentLipsMorphValueOut = 0f; _currentVaginaMorphValueIn = 0f; _currentVaginaMorphValueOut = 0f; _currentAnalMorphValueIn = 0f; _currentAnalMorphValueOut = 0f; } private void _bulge_LoadAllMorphs() { _bulge_mouthMorphsFullyLoaded = false; _bulge_bellyMorphsFullyLoaded = false; if (_personAtom == null || _characterSelector == null || _characterSelector.morphsControlUI == null) return; GenerateDAZMorphsControlUI morphUI = _characterSelector.morphsControlUI; _bulge_mouthMorphsFullyLoaded = true; for (int i = 0; i < BULGE_NUM_MOUTH_MORPHS; i++) { _bulge_mouthMorphs[i] = morphUI.GetMorphByDisplayName($"mouth{i + 1}"); if (_bulge_mouthMorphs[i] == null) { _bulge_mouthMorphsFullyLoaded = false; SuperController.LogMessage($"{LOG_PREFIX}: Mouth Bulge Morph 'mouth{i + 1}' not found."); } else _bulge_mouthMorphs[i].morphValue = 0f; _bulge_currentMouthMorphValues[i] = 0f; } _bulge_bellyMorphsFullyLoaded = true; for (int i = 0; i < BULGE_NUM_BELLY_MORPHS; i++) { _bulge_bellyMorphs[i] = morphUI.GetMorphByDisplayName($"belly{i + 1}"); if (_bulge_bellyMorphs[i] == null) { _bulge_bellyMorphsFullyLoaded = false; SuperController.LogMessage($"{LOG_PREFIX}: Belly Bulge Morph 'belly{i + 1}' not found."); } else _bulge_bellyMorphs[i].morphValue = 0f; _bulge_currentBellyMorphValues[i] = 0f; } } private void _bulge_ResetAllMorphValues() { for (int i = 0; i < BULGE_NUM_MOUTH_MORPHS; i++) { if (_bulge_mouthMorphs[i] != null) _bulge_mouthMorphs[i].morphValue = 0f; _bulge_currentMouthMorphValues[i] = 0f; } for (int i = 0; i < BULGE_NUM_BELLY_MORPHS; i++) { if (_bulge_bellyMorphs[i] != null) _bulge_bellyMorphs[i].morphValue = 0f; _bulge_currentBellyMorphValues[i] = 0f; } _bulge_lastRawMouthDist = BULGE_MOUTH_DIST_REMAP_MAX_DEFAULT + 0.1f; _bulge_lastRawBellyDist = BULGE_BELLY_DIST_REMAP_MAX_DEFAULT + 0.1f; } private void _bulge_UpdateLogic() { bool throatBulgeActive = _enableThroatBulgeJSON?.val ?? false; bool abdomenBulgeActive = _enableAbdomenBulgeJSON?.val ?? false; bool masterAnalGapeActive = _enableMorphDriveAJSON?.val ?? false; bool masterVaginalGapeActive = _enableMorphDriveVJSON?.val ?? false; bool lipsMotionActive = _enableLipsMotionJSON?.val ?? false; bool vaginaInOutActive = (_enableVaginaMotionJSON?.val ?? false) && masterVaginalGapeActive; bool analInOutActive = (_enableAnalMotionJSON?.val ?? false) && masterAnalGapeActive; if (!(_enableBulgeModuleJSON?.val ?? false) && !lipsMotionActive && !vaginaInOutActive && !analInOutActive ) { if (!_bulge_needsReset) { _bulge_FullReset(); _bulge_needsReset = true; } return; } if (_bulge_needsReset && (_enableBulgeModuleJSON?.val ?? false)) _bulge_FullReset(); float overallThroatDepth = 0f; float overallAbdomenDepth = 0f; try { if ((_enableBulgeModuleJSON?.val ?? false) && (throatBulgeActive || lipsMotionActive)) { if (_bulge_mouthTrigger?.trigger != null && _bulge_mouthTriggerEvtHandler != null && _bulge_mouthReferenceControl?.followWhenOff != null) { if (_bulge_mouthTrigger.trigger.active) { string targetNameIgnored; float rawDist = _bulge_GetClosestColliderDistance(_bulge_mouthTriggerEvtHandler, _bulge_mouthReferenceControl.followWhenOff.position, out targetNameIgnored); _bulge_lastRawMouthDist = Mathf.SmoothDamp(_bulge_lastRawMouthDist, rawDist, ref _bulge_yVelMouth, BULGE_HARDCODED_DISTANCE_SMOOTHING); overallThroatDepth = _bulge_RemapDistanceToDepth(_bulge_lastRawMouthDist, BULGE_MOUTH_DIST_REMAP_MIN_DEFAULT, BULGE_MOUTH_DIST_REMAP_MAX_DEFAULT); } else { _bulge_lastRawMouthDist = Mathf.SmoothDamp(_bulge_lastRawMouthDist, BULGE_MOUTH_DIST_REMAP_MAX_DEFAULT + 0.1f, ref _bulge_yVelMouth, 0.1f); overallThroatDepth = _bulge_RemapDistanceToDepth(_bulge_lastRawMouthDist, BULGE_MOUTH_DIST_REMAP_MIN_DEFAULT, BULGE_MOUTH_DIST_REMAP_MAX_DEFAULT); } } else { _bulge_lastRawMouthDist = Mathf.SmoothDamp(_bulge_lastRawMouthDist, BULGE_MOUTH_DIST_REMAP_MAX_DEFAULT + 0.1f, ref _bulge_yVelMouth, 0.1f); overallThroatDepth = _bulge_RemapDistanceToDepth(_bulge_lastRawMouthDist, BULGE_MOUTH_DIST_REMAP_MIN_DEFAULT, BULGE_MOUTH_DIST_REMAP_MAX_DEFAULT); } if (throatBulgeActive) { float mouthSmoothVal = _mouthBulgeSmoothingJSON != null ? _mouthBulgeSmoothingJSON.val : (1.0f - BULGE_DEFAULT_MORPH_APPLY_SMOOTHING); if (_bulge_mouthMorphsFullyLoaded) _bulge_ApplyMorphsSequentially(_bulge_mouthMorphs, _bulge_currentMouthMorphValues, overallThroatDepth, _bulge_uiMouthMorphMults, _bulge_uiThroatBulgeMult.val, BULGE_NUM_MOUTH_MORPHS, mouthSmoothVal); else _bulge_ResetSpecificMorphSet(_bulge_mouthMorphs, _bulge_currentMouthMorphValues, BULGE_NUM_MOUTH_MORPHS); } else { _bulge_ResetSpecificMorphSet(_bulge_mouthMorphs, _bulge_currentMouthMorphValues, BULGE_NUM_MOUTH_MORPHS); } if (lipsMotionActive) { _UpdateInOutMorphs(overallThroatDepth, ref _previousLipsDepthInOut, _selectedLipsMorphIn, _selectedLipsMorphOut, ref _currentLipsMorphValueIn, ref _currentLipsMorphValueOut, _motionMorphStrengthLipsInJSON.val, _motionMorphStrengthLipsOutJSON.val); } else { _DecayInOutMorph(ref _currentLipsMorphValueIn, _selectedLipsMorphIn); _DecayInOutMorph(ref _currentLipsMorphValueOut, _selectedLipsMorphOut); } } else { _bulge_ResetSpecificMorphSet(_bulge_mouthMorphs, _bulge_currentMouthMorphValues, BULGE_NUM_MOUTH_MORPHS); _DecayInOutMorph(ref _currentLipsMorphValueIn, _selectedLipsMorphIn); _DecayInOutMorph(ref _currentLipsMorphValueOut, _selectedLipsMorphOut); _bulge_lastRawMouthDist = BULGE_MOUTH_DIST_REMAP_MAX_DEFAULT + 0.1f; _previousLipsDepthInOut = 0f;} bool anyAbdomenEffectActive = ((_enableBulgeModuleJSON?.val ?? false) && abdomenBulgeActive) || vaginaInOutActive || analInOutActive; if (anyAbdomenEffectActive) { if (_bulge_vaginaTrigger?.trigger != null && _bulge_vaginaTriggerEvtHandler != null && _bulge_bellyReferenceControl?.followWhenOff != null) { if (_bulge_vaginaTrigger.trigger.active) { string targetNameIgnored; float rawDist = _bulge_GetClosestColliderDistance(_bulge_vaginaTriggerEvtHandler, _bulge_bellyReferenceControl.followWhenOff.position, out targetNameIgnored); _bulge_lastRawBellyDist = Mathf.SmoothDamp(_bulge_lastRawBellyDist, rawDist, ref _bulge_yVelBelly, BULGE_HARDCODED_DISTANCE_SMOOTHING); overallAbdomenDepth = _bulge_RemapDistanceToDepth(_bulge_lastRawBellyDist, BULGE_BELLY_DIST_REMAP_MIN_DEFAULT, BULGE_BELLY_DIST_REMAP_MAX_DEFAULT); } else { _bulge_lastRawBellyDist = Mathf.SmoothDamp(_bulge_lastRawBellyDist, BULGE_BELLY_DIST_REMAP_MAX_DEFAULT + 0.1f, ref _bulge_yVelBelly, 0.1f); overallAbdomenDepth = _bulge_RemapDistanceToDepth(_bulge_lastRawBellyDist, BULGE_BELLY_DIST_REMAP_MIN_DEFAULT, BULGE_BELLY_DIST_REMAP_MAX_DEFAULT); } } else { _bulge_lastRawBellyDist = Mathf.SmoothDamp(_bulge_lastRawBellyDist, BULGE_BELLY_DIST_REMAP_MAX_DEFAULT + 0.1f, ref _bulge_yVelBelly, 0.1f); overallAbdomenDepth = _bulge_RemapDistanceToDepth(_bulge_lastRawBellyDist, BULGE_BELLY_DIST_REMAP_MIN_DEFAULT, BULGE_BELLY_DIST_REMAP_MAX_DEFAULT); } } else { _bulge_lastRawBellyDist = Mathf.SmoothDamp(_bulge_lastRawBellyDist, BULGE_BELLY_DIST_REMAP_MAX_DEFAULT + 0.1f, ref _bulge_yVelBelly, 0.1f); overallAbdomenDepth = 0f; _previousAbdomenDepthInOut = 0f; } if ((_enableBulgeModuleJSON?.val ?? false) && abdomenBulgeActive) { float bellySmoothVal = _bellyBulgeSmoothingJSON != null ? _bellyBulgeSmoothingJSON.val : (1.0f - BULGE_DEFAULT_MORPH_APPLY_SMOOTHING); if (_bulge_bellyMorphsFullyLoaded) _bulge_ApplyMorphsSequentially(_bulge_bellyMorphs, _bulge_currentBellyMorphValues, overallAbdomenDepth, _bulge_uiBellyMorphMults, _bulge_uiAbdomenBulgeMult.val, BULGE_NUM_BELLY_MORPHS, bellySmoothVal); else _bulge_ResetSpecificMorphSet(_bulge_bellyMorphs, _bulge_currentBellyMorphValues, BULGE_NUM_BELLY_MORPHS); } else { _bulge_ResetSpecificMorphSet(_bulge_bellyMorphs, _bulge_currentBellyMorphValues, BULGE_NUM_BELLY_MORPHS); } bool isVaginaActiveForInOut = vaginaInOutActive && (_isCollidingV1 || _isCollidingV2 || (_bulge_vaginaTrigger != null && _bulge_vaginaTrigger.trigger.active)); if (isVaginaActiveForInOut) { _UpdateInOutMorphs(overallAbdomenDepth, ref _previousAbdomenDepthInOut, _selectedVaginaMorphIn, _selectedVaginaMorphOut, ref _currentVaginaMorphValueIn, ref _currentVaginaMorphValueOut, _motionMorphStrengthVaginaJSON.val); } else { _DecayInOutMorph(ref _currentVaginaMorphValueIn, _selectedVaginaMorphIn); _DecayInOutMorph(ref _currentVaginaMorphValueOut, _selectedVaginaMorphOut); } bool isAnalActiveForInOut = analInOutActive && (_isCollidingA1 || _isCollidingA2); if (isAnalActiveForInOut) { _UpdateInOutMorphs(overallAbdomenDepth, ref _previousAbdomenDepthInOut, _selectedAnalMorphIn, _selectedAnalMorphOut, ref _currentAnalMorphValueIn, ref _currentAnalMorphValueOut, _motionMorphStrengthAnalJSON.val); } else { _DecayInOutMorph(ref _currentAnalMorphValueIn, _selectedAnalMorphIn); _DecayInOutMorph(ref _currentAnalMorphValueOut, _selectedAnalMorphOut); } if (!vaginaInOutActive && !analInOutActive && (_enableBulgeModuleJSON?.val ?? false) && abdomenBulgeActive) { _previousAbdomenDepthInOut = overallAbdomenDepth;} } catch (Exception e) { SuperController.LogError($"{LOG_PREFIX} (Bulge/InOut).FixedUpdate: Exception: {e}"); } } private float _bulge_GetClosestColliderDistance(CollisionTriggerEventHandler eventHandler, Vector3 referencePoint, out string targetAtomName) { targetAtomName = "None"; if (eventHandler.collidingWithDictionary == null || eventHandler.collidingWithDictionary.Count == 0) return float.MaxValue; float closestDist = float.MaxValue; Collider closestCol = null; foreach (KeyValuePair pair in eventHandler.collidingWithDictionary) { if (pair.Key?.attachedRigidbody == null) continue; Atom currentAtom = pair.Key.attachedRigidbody.GetComponentInParent(); if (currentAtom == null || currentAtom == _personAtom) continue; if (!_isCuaCollisionPermitted(currentAtom)) continue; float dist = Vector3.Distance(referencePoint, pair.Key.attachedRigidbody.position); if (dist < closestDist) { closestDist = dist; closestCol = pair.Key; } } if (closestCol != null) { Atom foundAtom = closestCol.attachedRigidbody.GetComponentInParent(); if (foundAtom != null) targetAtomName = foundAtom.name; return closestDist; } return float.MaxValue; } private float _bulge_RemapDistanceToDepth(float currentDistance, float minDistanceForMaxDepth, float maxDistanceForMinDepth) { if (maxDistanceForMinDepth <= minDistanceForMaxDepth) return 0f; float val = (currentDistance - maxDistanceForMinDepth) / (minDistanceForMaxDepth - maxDistanceForMinDepth); return Mathf.Clamp01(val); } private void _bulge_ResetSpecificMorphSet(DAZMorph[] morphs, float[] currentValues, int numMorphs) { for (int i = 0; i < numMorphs; ++i) { currentValues[i] = 0f; if (morphs[i] != null && morphs[i].morphValue > 0.001f) { morphs[i].morphValue = 0f; } } } private void _bulge_ApplyMorphsSequentially(DAZMorph[] morphs, float[] currentMorphValues, float overallDepth, JSONStorableFloat[] individualMultipliers, float globalMultiplier, int numMorphs, float smoothingValue) { if (numMorphs == 0) return; float segment = 1.0f / numMorphs; float lerpFactor = 1.0f - smoothingValue; for (int i = 0; i < numMorphs; i++) { if (morphs[i] == null) continue; float depthRemainingForThisMorph = overallDepth - (i * segment); float targetMorphVal = Mathf.Clamp01(depthRemainingForThisMorph / segment); targetMorphVal *= individualMultipliers[i].val * globalMultiplier; if (BULGE_HARDCODED_CLAMP_MORPHS) targetMorphVal = Mathf.Clamp01(targetMorphVal); else targetMorphVal = Mathf.Max(0f, targetMorphVal); currentMorphValues[i] = Mathf.Lerp(currentMorphValues[i], targetMorphVal, lerpFactor); morphs[i].morphValue = currentMorphValues[i]; } } private void _UpdateInOutMorphs(float currentDepth, ref float previousDepth, DAZMorph morphIn, DAZMorph morphOut, ref float currentMorphValIn, ref float currentMorphValOut, float specificStrengthIn, float specificStrengthOut = -1f) { if(specificStrengthOut < 0) specificStrengthOut = specificStrengthIn; float velocity = currentDepth - previousDepth; float threshold = _motionThresholdJSON.val; float applySmoothingAlpha = 1.0f - _motionMorphSmoothingJSON.val; float decaySmoothingAlpha = 1.0f - _motionMorphDecaySmoothingJSON.val; if (velocity > threshold) { if (morphIn != null) { currentMorphValIn = Mathf.Lerp(currentMorphValIn, specificStrengthIn, applySmoothingAlpha); try{morphIn.morphValue = Mathf.Clamp01(currentMorphValIn);}catch{} } if (morphOut != null) { currentMorphValOut = Mathf.Lerp(currentMorphValOut, 0f, decaySmoothingAlpha); try{morphOut.morphValue = Mathf.Clamp01(currentMorphValOut);}catch{} } } else if (velocity < -threshold) { if (morphOut != null) { currentMorphValOut = Mathf.Lerp(currentMorphValOut, specificStrengthOut, applySmoothingAlpha); try{morphOut.morphValue = Mathf.Clamp01(currentMorphValOut);}catch{} } if (morphIn != null) { currentMorphValIn = Mathf.Lerp(currentMorphValIn, 0f, decaySmoothingAlpha); try{morphIn.morphValue = Mathf.Clamp01(currentMorphValIn);}catch{} } } else { if (morphIn != null) { currentMorphValIn = Mathf.Lerp(currentMorphValIn, 0f, decaySmoothingAlpha); try{morphIn.morphValue = Mathf.Clamp01(currentMorphValIn);}catch{} } if (morphOut != null) { currentMorphValOut = Mathf.Lerp(currentMorphValOut, 0f, decaySmoothingAlpha); try{morphOut.morphValue = Mathf.Clamp01(currentMorphValOut);}catch{} } } previousDepth = currentDepth; } private void _DecayInOutMorph(ref float currentMorphVal, DAZMorph morph) { if (morph == null) return; float decaySmoothingAlpha = 1.0f - _motionMorphDecaySmoothingJSON.val; currentMorphVal = Mathf.Lerp(currentMorphVal, 0f, decaySmoothingAlpha); if (currentMorphVal < 0.001f) currentMorphVal = 0f; try { morph.morphValue = Mathf.Clamp01(currentMorphVal); } catch {} } protected void OnEnable() { if (!_isPersonAtom) return; if (_fullInitializationRoutineComplete) { HandleFemaleAnatomyToggle(_disableBaseAutoGensJSON.val); if (_enableOptimizedPhysicsJSON != null) { _optimizedPhysicsStateRestored = !_enableOptimizedPhysicsJSON.val; } else { _optimizedPhysicsStateRestored = true; } if (!(_enableOptimizedPhysicsJSON?.val ?? false)) { RestoreSoftVerticesParameters(); } } else { FindAtomElementsAGG(); if (_femaleAnatomyStorableFound && _femaleAnatomyEnabledJSON != null) { _originalFemaleAnatomyEnabledState = _femaleAnatomyEnabledJSON.val; } _optimizedPhysicsStateRestored = true; RestoreSoftVerticesParameters(); } if (_physicsFrictionStorablesFound && !_frictionOverriddenByLipTrigger) { _originalDynamicFriction = _dynamicFrictionJSON.val; _originalStaticFriction = _staticFrictionJSON.val; _originalFrictionCombine = _frictionCombineJSON.val; } if (_tongueCollisionStorableFound && _tongueCollisionJSONStorable != null && !_tongueCollisionOverridden) _originalTongueCollisionState = _tongueCollisionJSONStorable.val; if (_jawControlStorableFound && _jawSpringJSON != null && !_jawSpringOverridden) _originalJawSpring = _jawSpringJSON.val; if (_softVerticesColliderRadiusFound && _softVerticesColliderRadiusJSON != null && !_softVerticesParamsOverridden) _originalSoftVerticesColliderRadius = _softVerticesColliderRadiusJSON.val; if (_softVerticesDistanceLimitFound && _softVerticesDistanceLimitJSON != null && !_softVerticesParamsOverridden) _originalSoftVerticesDistanceLimit = _softVerticesDistanceLimitJSON.val; } void FixedUpdate() { if (!_isPersonAtom || !_fullInitializationRoutineComplete) return; _bulge_UpdateLogic(); } void Update() { if (!_isPersonAtom || !_fullInitializationRoutineComplete) return; if (_disableBaseAutoGensJSON != null && _femaleAnatomyStorableFound && _femaleAnatomyEnabledJSON != null) { if (_disableBaseAutoGensJSON.val) { if (_femaleAnatomyEnabledJSON.val) { _femaleAnatomyEnabledJSON.val = false; if(!_pluginEnabledFemaleAnatomyOverride) _pluginEnabledFemaleAnatomyOverride = true; } } } } void LateUpdate() { if (!_isPersonAtom || !_fullInitializationRoutineComplete) return; bool optPhysEnabled = _enableOptimizedPhysicsJSON?.val ?? false; bool aggGapeEnabled = _enableAutoGensGapeModuleJSON?.val ?? true; bool anyGapeOrOptPhysics = optPhysEnabled || aggGapeEnabled; if (anyGapeOrOptPhysics) { CheckCollisionsAGG(); } if (optPhysEnabled) { _optimizedPhysicsStateRestored = false; ManageOtherOptimizedPhysics(); if (_softVerticesColliderRadiusFound || _softVerticesDistanceLimitFound) { UpdateSoftVerticesParametersAGG(); } } else { if (!_optimizedPhysicsStateRestored) { RestoreOtherOptimizedPhysicsSettings(); RestoreSoftVerticesParameters(); _optimizedPhysicsStateRestored = true; } } if (aggGapeEnabled) { if (_bonesAFound) CalculateDistanceA_AGG(); if (_bonesVFound) CalculateDistanceV_AGG(); if (_enableMorphDriveAJSON.val) DriveMorphA_AGG(); if (_enableMorphDriveVJSON.val) DriveMorphV_AGG(); if (_enableMorphDriveMJSON.val) DriveMorphM_AGG(); } } private void RestoreOptimizedPhysicsSettings() { RestoreOtherOptimizedPhysicsSettings(); RestoreSoftVerticesParameters(); } public override JSONClass GetJSON(bool includePhysical = true, bool includeAppearance = true, bool forceStore = false) { JSONClass json = base.GetJSON(includePhysical, includeAppearance, forceStore); needsStore = true; if (_enableAutoGensGapeModuleJSON != null) json[_enableAutoGensGapeModuleJSON.name] = _enableAutoGensGapeModuleJSON.val.ToString(); if (_overallStrengthJSON != null) json[_overallStrengthJSON.name] = _overallStrengthJSON.val.ToString(); if (_additiveAnalMorphValueJSON != null) json[_additiveAnalMorphValueJSON.name] = _additiveAnalMorphValueJSON.val.ToString(); if (_additiveVaginalMorphValueJSON != null) json[_additiveVaginalMorphValueJSON.name] = _additiveVaginalMorphValueJSON.val.ToString(); if (_additiveMouthMorphValueJSON != null) json[_additiveMouthMorphValueJSON.name] = _additiveMouthMorphValueJSON.val.ToString(); if (_lipTriggerRadiusJSON != null) json[_lipTriggerRadiusJSON.name] = _lipTriggerRadiusJSON.val.ToString(); if (_analBoneJointRadiusJSON != null) json[_analBoneJointRadiusJSON.name] = _analBoneJointRadiusJSON.val.ToString(); if (_vaginalBoneJointRadiusJSON != null) json[_vaginalBoneJointRadiusJSON.name] = _vaginalBoneJointRadiusJSON.val.ToString(); if (_enableMorphDriveAJSON != null) json[_enableMorphDriveAJSON.name] = _enableMorphDriveAJSON.val.ToString(); if (_enableMorphDriveVJSON != null) json[_enableMorphDriveVJSON.name] = _enableMorphDriveVJSON.val.ToString(); if (_enableMorphDriveMJSON != null) json[_enableMorphDriveMJSON.name] = _enableMorphDriveMJSON.val.ToString(); if (_enableOptimizedPhysicsJSON != null) json[_enableOptimizedPhysicsJSON.name] = _enableOptimizedPhysicsJSON.val.ToString(); if(_disableBaseAutoGensJSON != null) json[_disableBaseAutoGensJSON.name] = _disableBaseAutoGensJSON.val.ToString(); if (_morphSmoothInAJSON != null) json[_morphSmoothInAJSON.name] = _morphSmoothInAJSON.val.ToString(); if (_morphSmoothInVJSON != null) json[_morphSmoothInVJSON.name] = _morphSmoothInVJSON.val.ToString(); if (_morphSmoothInMJSON != null) json[_morphSmoothInMJSON.name] = _morphSmoothInMJSON.val.ToString(); if (_mouthBulgeSmoothingJSON != null) json[_mouthBulgeSmoothingJSON.name] = _mouthBulgeSmoothingJSON.val.ToString(); if (_bellyBulgeSmoothingJSON != null) json[_bellyBulgeSmoothingJSON.name] = _bellyBulgeSmoothingJSON.val.ToString(); if (_morphMultiplierAJSON != null) json[_morphMultiplierAJSON.name] = _morphMultiplierAJSON.val.ToString(); if (_morphOffsetAJSON != null) json[_morphOffsetAJSON.name] = _morphOffsetAJSON.val.ToString(); if (_morphDecayTimeAJSON != null) json[_morphDecayTimeAJSON.name] = _morphDecayTimeAJSON.val.ToString(); if (_twinkEnabledJSON != null) json[_twinkEnabledJSON.name] = _twinkEnabledJSON.val.ToString(); if (_twinkIntervalMeanJSON != null) json[_twinkIntervalMeanJSON.name] = _twinkIntervalMeanJSON.val.ToString(); if (_twinkIntervalDeltaJSON != null) json[_twinkIntervalDeltaJSON.name] = _twinkIntervalDeltaJSON.val.ToString(); if (_twinkStartDelayJSON != null) json[_twinkStartDelayJSON.name] = _twinkStartDelayJSON.val.ToString(); if (_morphMultiplierVJSON != null) json[_morphMultiplierVJSON.name] = _morphMultiplierVJSON.val.ToString(); if (_morphOffsetVJSON != null) json[_morphOffsetVJSON.name] = _morphOffsetVJSON.val.ToString(); if (_morphDecayTimeVJSON != null) json[_morphDecayTimeVJSON.name] = _morphDecayTimeVJSON.val.ToString(); if (_morphDecayTimeMJSON != null) json[_morphDecayTimeMJSON.name] = _morphDecayTimeMJSON.val.ToString(); if (_softVerticesSmoothingJSON != null) json[_softVerticesSmoothingJSON.name] = _softVerticesSmoothingJSON.val.ToString(); if (_enableBulgeModuleJSON != null) json[_enableBulgeModuleJSON.name] = _enableBulgeModuleJSON.val.ToString(); if (_enableThroatBulgeJSON != null) json[_enableThroatBulgeJSON.name] = _enableThroatBulgeJSON.val.ToString(); if (_enableAbdomenBulgeJSON != null) json[_enableAbdomenBulgeJSON.name] = _enableAbdomenBulgeJSON.val.ToString(); if (_bulge_uiThroatBulgeMult != null) json[_bulge_uiThroatBulgeMult.name] = _bulge_uiThroatBulgeMult.val.ToString(); if (_bulge_uiAbdomenBulgeMult != null) json[_bulge_uiAbdomenBulgeMult.name] = _bulge_uiAbdomenBulgeMult.val.ToString(); for (int i = 0; i < BULGE_NUM_MOUTH_MORPHS; i++) { if (_bulge_uiMouthMorphMults[i] != null) json[_bulge_uiMouthMorphMults[i].name] = _bulge_uiMouthMorphMults[i].val.ToString(); } for (int i = 0; i < BULGE_NUM_BELLY_MORPHS; i++) { if (_bulge_uiBellyMorphMults[i] != null) json[_bulge_uiBellyMorphMults[i].name] = _bulge_uiBellyMorphMults[i].val.ToString(); } if (_enableLipsMotionJSON != null) json[_enableLipsMotionJSON.name] = _enableLipsMotionJSON.val.ToString(); if (_enableVaginaMotionJSON != null) json[_enableVaginaMotionJSON.name] = _enableVaginaMotionJSON.val.ToString(); if (_enableAnalMotionJSON != null) json[_enableAnalMotionJSON.name] = _enableAnalMotionJSON.val.ToString(); if (_motionMorphStrengthLipsInJSON != null) json[_motionMorphStrengthLipsInJSON.name] = _motionMorphStrengthLipsInJSON.val.ToString(); if (_motionMorphStrengthLipsOutJSON != null) json[_motionMorphStrengthLipsOutJSON.name] = _motionMorphStrengthLipsOutJSON.val.ToString(); if (_motionMorphStrengthVaginaJSON != null) json[_motionMorphStrengthVaginaJSON.name] = _motionMorphStrengthVaginaJSON.val.ToString(); if (_motionMorphStrengthAnalJSON != null) json[_motionMorphStrengthAnalJSON.name] = _motionMorphStrengthAnalJSON.val.ToString(); if (_motionMorphSmoothingJSON != null) json[_motionMorphSmoothingJSON.name] = _motionMorphSmoothingJSON.val.ToString(); if (_motionMorphDecaySmoothingJSON != null) json[_motionMorphDecaySmoothingJSON.name] = _motionMorphDecaySmoothingJSON.val.ToString(); if (_motionThresholdJSON != null) json[_motionThresholdJSON.name] = _motionThresholdJSON.val.ToString(); return json; } public override void RestoreFromJSON(JSONClass json, bool restorePhysical = true, bool restoreAppearance = true, JSONArray presetAtoms = null, bool setMissingToDefault = true) { _fullInitializationRoutineComplete = false; _cuaCollisionEnabledStorables.Clear(); base.RestoreFromJSON(json, restorePhysical, restoreAppearance, presetAtoms, setMissingToDefault); string oldAdditiveAnalName = "Target Additive Anal Gape", oldAdditiveVaginalName = "Target Additive Vaginal Gape", oldAdditiveMouthName = "Target Mouth Gape"; string oldAnalDecayName = "Anal Decay Time (Dist)", oldTwinkIntervalMeanName = "Twink Interval Mean (Dist)", oldTwinkIntervalDeltaName = "Twink Interval Delta (Dist)", oldEnableTwinkName = "Enable Anal Twinking (Dist)"; string oldEnableMouthBulgeName = "Enable Mouth Bulge"; string oldMouthBulgeMultName = "Mouth Global Multiplier"; string oldInOutMorphStrengthName = "In/Out Morph Max Strength"; string oldEnableThroatInOutFXName = "Enable Throat In/Out FX"; if (_enableAutoGensGapeModuleJSON != null) { if (json.HasKey(_enableAutoGensGapeModuleJSON.name)) _enableAutoGensGapeModuleJSON.val = json[_enableAutoGensGapeModuleJSON.name].AsBool; else if(setMissingToDefault) _enableAutoGensGapeModuleJSON.val = true; } if (_overallStrengthJSON != null) { if (json.HasKey(_overallStrengthJSON.name)) _overallStrengthJSON.val = json[_overallStrengthJSON.name].AsFloat; else if (json.HasKey("Global Morphs Multiplier")) _overallStrengthJSON.val = json["Global Morphs Multiplier"].AsFloat; else if(setMissingToDefault) _overallStrengthJSON.val = 1.0f; } if (_additiveAnalMorphValueJSON != null) { if (json.HasKey(_additiveAnalMorphValueJSON.name)) _additiveAnalMorphValueJSON.val = json[_additiveAnalMorphValueJSON.name].AsFloat; else if (json.HasKey(oldAdditiveAnalName)) _additiveAnalMorphValueJSON.val = json[oldAdditiveAnalName].AsFloat; else if(setMissingToDefault) _additiveAnalMorphValueJSON.val = 0.2f; } if (_additiveVaginalMorphValueJSON != null) { if (json.HasKey(_additiveVaginalMorphValueJSON.name)) _additiveVaginalMorphValueJSON.val = json[_additiveVaginalMorphValueJSON.name].AsFloat; else if (json.HasKey(oldAdditiveVaginalName)) _additiveVaginalMorphValueJSON.val = json[oldAdditiveVaginalName].AsFloat; else if(setMissingToDefault) _additiveVaginalMorphValueJSON.val = 0.2f;} if (_additiveMouthMorphValueJSON != null) { if (json.HasKey(_additiveMouthMorphValueJSON.name)) _additiveMouthMorphValueJSON.val = json[_additiveMouthMorphValueJSON.name].AsFloat; else if (json.HasKey(oldAdditiveMouthName)) _additiveMouthMorphValueJSON.val = json[oldAdditiveMouthName].AsFloat; else if(setMissingToDefault) _additiveMouthMorphValueJSON.val = 0.6f;} if (_lipTriggerRadiusJSON != null) { if (json.HasKey(_lipTriggerRadiusJSON.name)) _lipTriggerRadiusJSON.val = json[_lipTriggerRadiusJSON.name].AsFloat; else if(setMissingToDefault) _lipTriggerRadiusJSON.val = DEFAULT_LIP_TRIGGER_RADIUS;} if (_analBoneJointRadiusJSON != null) { if (json.HasKey(_analBoneJointRadiusJSON.name)) _analBoneJointRadiusJSON.val = json[_analBoneJointRadiusJSON.name].AsFloat; else if(setMissingToDefault) _analBoneJointRadiusJSON.val = DEFAULT_GENITAL_JOINT_RADIUS;} if (_vaginalBoneJointRadiusJSON != null) { if (json.HasKey(_vaginalBoneJointRadiusJSON.name)) _vaginalBoneJointRadiusJSON.val = json[_vaginalBoneJointRadiusJSON.name].AsFloat; else if(setMissingToDefault) _vaginalBoneJointRadiusJSON.val = DEFAULT_GENITAL_JOINT_RADIUS;} if (_enableMorphDriveAJSON != null) { if (json.HasKey(_enableMorphDriveAJSON.name)) _enableMorphDriveAJSON.val = json[_enableMorphDriveAJSON.name].AsBool; else if(setMissingToDefault) _enableMorphDriveAJSON.val = true;} if (_enableMorphDriveVJSON != null) { if (json.HasKey(_enableMorphDriveVJSON.name)) _enableMorphDriveVJSON.val = json[_enableMorphDriveVJSON.name].AsBool; else if(setMissingToDefault) _enableMorphDriveVJSON.val = true;} if (_enableMorphDriveMJSON != null) { if (json.HasKey(_enableMorphDriveMJSON.name)) _enableMorphDriveMJSON.val = json[_enableMorphDriveMJSON.name].AsBool; else if(setMissingToDefault) _enableMorphDriveMJSON.val = true;} if (_enableOptimizedPhysicsJSON != null) { if (json.HasKey(_enableOptimizedPhysicsJSON.name)) _enableOptimizedPhysicsJSON.val = json[_enableOptimizedPhysicsJSON.name].AsBool; else if(setMissingToDefault) _enableOptimizedPhysicsJSON.val = false; _optimizedPhysicsStateRestored = !_enableOptimizedPhysicsJSON.val; } else { _optimizedPhysicsStateRestored = true; } if (_disableBaseAutoGensJSON != null) { if (json.HasKey(_disableBaseAutoGensJSON.name)) _disableBaseAutoGensJSON.val = json[_disableBaseAutoGensJSON.name].AsBool; else if(setMissingToDefault) _disableBaseAutoGensJSON.val = true; } if (_morphSmoothInAJSON != null) { if (json.HasKey(_morphSmoothInAJSON.name)) _morphSmoothInAJSON.val = json[_morphSmoothInAJSON.name].AsFloat; else if(setMissingToDefault) _morphSmoothInAJSON.val = 0.95f; } if (_morphSmoothInVJSON != null) { if (json.HasKey(_morphSmoothInVJSON.name)) _morphSmoothInVJSON.val = json[_morphSmoothInVJSON.name].AsFloat; else if(setMissingToDefault) _morphSmoothInVJSON.val = 0.95f; } if (_morphSmoothInMJSON != null) { if (json.HasKey(_morphSmoothInMJSON.name)) _morphSmoothInMJSON.val = json[_morphSmoothInMJSON.name].AsFloat; else if(setMissingToDefault) _morphSmoothInMJSON.val = 0.95f; } if (_mouthBulgeSmoothingJSON != null) { if (json.HasKey(_mouthBulgeSmoothingJSON.name)) _mouthBulgeSmoothingJSON.val = json[_mouthBulgeSmoothingJSON.name].AsFloat; else if(setMissingToDefault) _mouthBulgeSmoothingJSON.val = (1.0f - BULGE_DEFAULT_MORPH_APPLY_SMOOTHING); } if (_bellyBulgeSmoothingJSON != null) { if (json.HasKey(_bellyBulgeSmoothingJSON.name)) _bellyBulgeSmoothingJSON.val = json[_bellyBulgeSmoothingJSON.name].AsFloat; else if(setMissingToDefault) _bellyBulgeSmoothingJSON.val = (1.0f- BULGE_DEFAULT_MORPH_APPLY_SMOOTHING); } if (_morphMultiplierAJSON != null) { if (json.HasKey(_morphMultiplierAJSON.name)) _morphMultiplierAJSON.val = json[_morphMultiplierAJSON.name].AsFloat; else if(setMissingToDefault) _morphMultiplierAJSON.val = 10.0f;} if (_morphOffsetAJSON != null) { if (json.HasKey(_morphOffsetAJSON.name)) _morphOffsetAJSON.val = json[_morphOffsetAJSON.name].AsFloat; else if(setMissingToDefault) _morphOffsetAJSON.val = -0.24f;} if (_morphDecayTimeAJSON != null) { if (json.HasKey(_morphDecayTimeAJSON.name)) _morphDecayTimeAJSON.val = json[_morphDecayTimeAJSON.name].AsFloat; else if (json.HasKey(oldAnalDecayName)) _morphDecayTimeAJSON.val = json[oldAnalDecayName].AsFloat; else if(setMissingToDefault) _morphDecayTimeAJSON.val = 10.0f;} if (_twinkEnabledJSON != null) { if (json.HasKey(_twinkEnabledJSON.name)) _twinkEnabledJSON.val = json[_twinkEnabledJSON.name].AsBool; else if (json.HasKey(oldEnableTwinkName)) _twinkEnabledJSON.val = json[oldEnableTwinkName].AsBool; else if(setMissingToDefault) _twinkEnabledJSON.val = true;} if (_twinkIntervalMeanJSON != null) { if (json.HasKey(_twinkIntervalMeanJSON.name)) _twinkIntervalMeanJSON.val = json[_twinkIntervalMeanJSON.name].AsFloat; else if (json.HasKey(oldTwinkIntervalMeanName)) _twinkIntervalMeanJSON.val = json[oldTwinkIntervalMeanName].AsFloat; else if(setMissingToDefault) _twinkIntervalMeanJSON.val = 5.0f;} if (_twinkIntervalDeltaJSON != null) { if (json.HasKey(_twinkIntervalDeltaJSON.name)) _twinkIntervalDeltaJSON.val = json[_twinkIntervalDeltaJSON.name].AsFloat; else if (json.HasKey(oldTwinkIntervalDeltaName)) _twinkIntervalDeltaJSON.val = json[oldTwinkIntervalDeltaName].AsFloat; else if(setMissingToDefault) _twinkIntervalDeltaJSON.val = 2.0f;} if (_twinkStartDelayJSON != null) { if (json.HasKey(_twinkStartDelayJSON.name)) _twinkStartDelayJSON.val = json[_twinkStartDelayJSON.name].AsFloat; else if(setMissingToDefault) _twinkStartDelayJSON.val = 1.0f;} if (_morphMultiplierVJSON != null) { if (json.HasKey(_morphMultiplierVJSON.name)) _morphMultiplierVJSON.val = json[_morphMultiplierVJSON.name].AsFloat; else if(setMissingToDefault) _morphMultiplierVJSON.val = 10.0f;} if (_morphOffsetVJSON != null) { if (json.HasKey(_morphOffsetVJSON.name)) _morphOffsetVJSON.val = json[_morphOffsetVJSON.name].AsFloat; else if(setMissingToDefault) _morphOffsetVJSON.val = -0.22f;} if (_morphDecayTimeVJSON != null) { if (json.HasKey(_morphDecayTimeVJSON.name)) _morphDecayTimeVJSON.val = json[_morphDecayTimeVJSON.name].AsFloat; else if(setMissingToDefault) _morphDecayTimeVJSON.val = 0.5f;} if (_morphDecayTimeMJSON != null) { if (json.HasKey(_morphDecayTimeMJSON.name)) _morphDecayTimeMJSON.val = json[_morphDecayTimeMJSON.name].AsFloat; else if(setMissingToDefault) _morphDecayTimeMJSON.val = 0.6f;} if (_softVerticesSmoothingJSON != null) { if (json.HasKey(_softVerticesSmoothingJSON.name)) _softVerticesSmoothingJSON.val = json[_softVerticesSmoothingJSON.name].AsFloat; else if(setMissingToDefault) _softVerticesSmoothingJSON.val = SOFT_VERTICES_SMOOTHING_DEFAULT;} if (_enableBulgeModuleJSON != null) { if (json.HasKey(_enableBulgeModuleJSON.name)) _enableBulgeModuleJSON.val = json[_enableBulgeModuleJSON.name].AsBool; else if(setMissingToDefault) _enableBulgeModuleJSON.val = true;} if (_enableThroatBulgeJSON != null) { if (json.HasKey(_enableThroatBulgeJSON.name)) _enableThroatBulgeJSON.val = json[_enableThroatBulgeJSON.name].AsBool; else if (json.HasKey(oldEnableMouthBulgeName)) _enableThroatBulgeJSON.val = json[oldEnableMouthBulgeName].AsBool; else if(setMissingToDefault) _enableThroatBulgeJSON.val = true; } if (_enableAbdomenBulgeJSON != null) { if (json.HasKey(_enableAbdomenBulgeJSON.name)) _enableAbdomenBulgeJSON.val = json[_enableAbdomenBulgeJSON.name].AsBool; else if(setMissingToDefault) _enableAbdomenBulgeJSON.val = true; } if (_bulge_uiThroatBulgeMult != null) { if (json.HasKey(_bulge_uiThroatBulgeMult.name)) _bulge_uiThroatBulgeMult.val = json[_bulge_uiThroatBulgeMult.name].AsFloat; else if (json.HasKey(oldMouthBulgeMultName)) _bulge_uiThroatBulgeMult.val = json[oldMouthBulgeMultName].AsFloat; else if(setMissingToDefault) _bulge_uiThroatBulgeMult.val = 1.3f; } if (_bulge_uiAbdomenBulgeMult != null) { if (json.HasKey(_bulge_uiAbdomenBulgeMult.name)) _bulge_uiAbdomenBulgeMult.val = json[_bulge_uiAbdomenBulgeMult.name].AsFloat; else if(setMissingToDefault) _bulge_uiAbdomenBulgeMult.val = 1.0f; } float[] defaultMouthMorphMults = { 1.00f, 0.67f, 1.00f }; for (int i = 0; i < BULGE_NUM_MOUTH_MORPHS; i++) { if (_bulge_uiMouthMorphMults[i] != null) { if (json.HasKey(_bulge_uiMouthMorphMults[i].name)) _bulge_uiMouthMorphMults[i].val = json[_bulge_uiMouthMorphMults[i].name].AsFloat; else if(setMissingToDefault && i < defaultMouthMorphMults.Length) _bulge_uiMouthMorphMults[i].val = defaultMouthMorphMults[i];}} float[] defaultBellyMorphMults = { 0.54f, 0.65f, 1.00f, 0.24f, 0.22f }; for (int i = 0; i < BULGE_NUM_BELLY_MORPHS; i++) { if (_bulge_uiBellyMorphMults[i] != null) { if (json.HasKey(_bulge_uiBellyMorphMults[i].name)) _bulge_uiBellyMorphMults[i].val = json[_bulge_uiBellyMorphMults[i].name].AsFloat; else if(setMissingToDefault && i < defaultBellyMorphMults.Length) _bulge_uiBellyMorphMults[i].val = defaultBellyMorphMults[i];}} if (_enableLipsMotionJSON != null) { if (json.HasKey(_enableLipsMotionJSON.name)) _enableLipsMotionJSON.val = json[_enableLipsMotionJSON.name].AsBool; else if (json.HasKey(oldEnableThroatInOutFXName)) _enableLipsMotionJSON.val = json[oldEnableThroatInOutFXName].AsBool; else if(setMissingToDefault) _enableLipsMotionJSON.val = true; } if (_enableVaginaMotionJSON != null) { if (json.HasKey(_enableVaginaMotionJSON.name)) _enableVaginaMotionJSON.val = json[_enableVaginaMotionJSON.name].AsBool; else if(setMissingToDefault) _enableVaginaMotionJSON.val = true; } if (_enableAnalMotionJSON != null) { if (json.HasKey(_enableAnalMotionJSON.name)) _enableAnalMotionJSON.val = json[_enableAnalMotionJSON.name].AsBool; else if(setMissingToDefault) _enableAnalMotionJSON.val = true; } if (json.HasKey("Enable Abdomen In/Out FX") && setMissingToDefault) { bool oldAbdomenInOutVal = json["Enable Abdomen In/Out FX"].AsBool; if (_enableVaginaMotionJSON != null && !json.HasKey(_enableVaginaMotionJSON.name)) _enableVaginaMotionJSON.val = oldAbdomenInOutVal; if (_enableAnalMotionJSON != null && !json.HasKey(_enableAnalMotionJSON.name)) _enableAnalMotionJSON.val = oldAbdomenInOutVal; } float oldStrengthValForBackwardCompat = INOUT_DEFAULT_MORPH_STRENGTH_LIPS_IN; if (json.HasKey(oldInOutMorphStrengthName)) oldStrengthValForBackwardCompat = json[oldInOutMorphStrengthName].AsFloat; if (_motionMorphStrengthLipsInJSON != null) { if (json.HasKey(_motionMorphStrengthLipsInJSON.name)) _motionMorphStrengthLipsInJSON.val = json[_motionMorphStrengthLipsInJSON.name].AsFloat; else if (json.HasKey("Throat In/Out Strength")) _motionMorphStrengthLipsInJSON.val = json["Throat In/Out Strength"].AsFloat; else if(setMissingToDefault) _motionMorphStrengthLipsInJSON.val = json.HasKey(oldInOutMorphStrengthName) ? oldStrengthValForBackwardCompat : INOUT_DEFAULT_MORPH_STRENGTH_LIPS_IN; } if (_motionMorphStrengthLipsOutJSON != null) { if (json.HasKey(_motionMorphStrengthLipsOutJSON.name)) _motionMorphStrengthLipsOutJSON.val = json[_motionMorphStrengthLipsOutJSON.name].AsFloat; else if (json.HasKey("Throat In/Out Strength")) _motionMorphStrengthLipsOutJSON.val = json["Throat In/Out Strength"].AsFloat; else if(setMissingToDefault) _motionMorphStrengthLipsOutJSON.val = json.HasKey(oldInOutMorphStrengthName) ? oldStrengthValForBackwardCompat : INOUT_DEFAULT_MORPH_STRENGTH_LIPS_OUT; } if (_motionMorphStrengthVaginaJSON != null) { if (json.HasKey(_motionMorphStrengthVaginaJSON.name)) _motionMorphStrengthVaginaJSON.val = json[_motionMorphStrengthVaginaJSON.name].AsFloat; else if(setMissingToDefault) _motionMorphStrengthVaginaJSON.val = json.HasKey(oldInOutMorphStrengthName) ? oldStrengthValForBackwardCompat : INOUT_DEFAULT_MORPH_STRENGTH_VAGINA; } if (_motionMorphStrengthAnalJSON != null) { if (json.HasKey(_motionMorphStrengthAnalJSON.name)) _motionMorphStrengthAnalJSON.val = json[_motionMorphStrengthAnalJSON.name].AsFloat; else if(setMissingToDefault) _motionMorphStrengthAnalJSON.val = json.HasKey(oldInOutMorphStrengthName) ? oldStrengthValForBackwardCompat : INOUT_DEFAULT_MORPH_STRENGTH_ANAL; } if (_motionMorphSmoothingJSON != null) { if (json.HasKey(_motionMorphSmoothingJSON.name)) _motionMorphSmoothingJSON.val = json[_motionMorphSmoothingJSON.name].AsFloat; else if(setMissingToDefault) _motionMorphSmoothingJSON.val = INOUT_DEFAULT_SMOOTHING; } if (_motionMorphDecaySmoothingJSON != null) { if (json.HasKey(_motionMorphDecaySmoothingJSON.name)) _motionMorphDecaySmoothingJSON.val = json[_motionMorphDecaySmoothingJSON.name].AsFloat; else if(setMissingToDefault) _motionMorphDecaySmoothingJSON.val = INOUT_DEFAULT_DECAY_SMOOTHING; } if (_motionThresholdJSON != null) { if (json.HasKey(_motionThresholdJSON.name)) _motionThresholdJSON.val = json[_motionThresholdJSON.name].AsFloat; else if(setMissingToDefault) _motionThresholdJSON.val = INOUT_DEFAULT_MOTION_THRESHOLD; } if (_agg_main_init_done && _isPersonAtom) { StartCoroutine(PostInitFindData()); } else { _fullInitializationRoutineComplete = true; } } protected void OnDisable() { if (!_isPersonAtom || !_fullInitializationRoutineComplete) return; RestoreFemaleAnatomyState(); RestoreOptimizedPhysicsSettings(); _optimizedPhysicsStateRestored = true; if (_selectedMorphA != null) { try { _selectedMorphA.morphValue = 0f; } catch { } } if (_selectedMorphV != null) { try { _selectedMorphV.morphValue = 0f; } catch { } } if (_selectedMorphM_AGG != null) { try { _selectedMorphM_AGG.morphValue = 0f; } catch { } } _smoothedMorphValueA = 0f; _currentSmoothedAdditiveAnalValue = 0f; _smoothedMorphValueV = 0f; _currentSmoothedAdditiveVaginalValue = 0f; _smoothedMorphValueM = 0f; _isDecayingA = false; _isTwinkingA = false; _isDecayingV = false; _frictionOverriddenByLipTrigger = false; _bulge_FullReset(); _bulge_needsReset = true; _cuaCollisionEnabledStorables.Clear(); } protected void OnDestroy() { if (_mainSettingsTabButton != null && _mainSettingsTabButton.button != null) _mainSettingsTabButton.button.onClick.RemoveAllListeners(); if (_advancedSettingsTabButton != null && _advancedSettingsTabButton.button != null) _advancedSettingsTabButton.button.onClick.RemoveAllListeners(); OnDisable(); } // endregion } } // --- END OF FILE OrificeDynamics.cs ---