├── Better Penetration Studio Instructions.pdf ├── Better Penetration Frequently Asked Questions.pdf ├── Core_BetterPenetration ├── Constants.cs ├── ControllerOptions.cs ├── ItemColliderInfo.cs ├── CollisionPoint.cs ├── Core_BetterPenetration.shproj ├── CollisionPoints.cs ├── DanPoint.cs ├── Core_BetterPenetration.projitems ├── DanOptions.cs ├── MathHelpers.cs ├── DanPoints.cs ├── CollisionOptions.cs ├── Tools.cs ├── TwistedPlane.cs ├── BoneNames.cs ├── Studio_BetterPenetration.cs ├── CoreGame.cs └── CollisionAgent.cs ├── AI_BetterPenetration └── AI_BetterPenetration.csproj ├── HS2_BetterPenetration └── HS2_BetterPenetration.csproj ├── KKS_BetterPenetration ├── KKS_BetterPenetration.csproj └── KKS_BetterPenetration.cs ├── KK_BetterPenetration ├── KK_BetterPenetration.csproj └── KK_BetterPenetration.cs ├── AI_Studio_BetterPenetration └── AI_Studio_BetterPenetration.csproj ├── HS2_Studio_BetterPenetration └── HS2_Studio_BetterPenetration.csproj ├── KKS_Studio_BetterPenetration └── KKS_Studio_BetterPenetration.csproj ├── KK_Studio_BetterPenetration └── KK_Studio_BetterPenetration.csproj ├── nuget.config ├── README.md ├── Directory.Build.props ├── .gitignore └── BetterPenetration.sln /Better Penetration Studio Instructions.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IllusionMods/BetterPenetration/HEAD/Better Penetration Studio Instructions.pdf -------------------------------------------------------------------------------- /Better Penetration Frequently Asked Questions.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IllusionMods/BetterPenetration/HEAD/Better Penetration Frequently Asked Questions.pdf -------------------------------------------------------------------------------- /Core_BetterPenetration/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Core_BetterPenetration 2 | { 3 | internal class Constants 4 | { 5 | #if KK || KKS || EC 6 | public const string PluginVersion = "4.5.5.4"; 7 | #else 8 | public const string PluginVersion = "5.0.1.4"; 9 | #endif 10 | } 11 | } -------------------------------------------------------------------------------- /AI_BetterPenetration/AI_BetterPenetration.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net46 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /HS2_BetterPenetration/HS2_BetterPenetration.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net46 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /KKS_BetterPenetration/KKS_BetterPenetration.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net46 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /KK_BetterPenetration/KK_BetterPenetration.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net35 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /AI_Studio_BetterPenetration/AI_Studio_BetterPenetration.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net46 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /HS2_Studio_BetterPenetration/HS2_Studio_BetterPenetration.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net46 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /KKS_Studio_BetterPenetration/KKS_Studio_BetterPenetration.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net46 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /KK_Studio_BetterPenetration/KK_Studio_BetterPenetration.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net35 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Core_BetterPenetration/ControllerOptions.cs: -------------------------------------------------------------------------------- 1 | #if Studio 2 | namespace Core_BetterPenetration 3 | { 4 | class ControllerOptions 5 | { 6 | internal enum AutoTarget 7 | { 8 | Off, 9 | Vaginal, 10 | Anal, 11 | Oral 12 | } 13 | 14 | internal AutoTarget danAutoTarget; 15 | 16 | public ControllerOptions(AutoTarget danAutoTarget) 17 | { 18 | this.danAutoTarget = danAutoTarget; 19 | } 20 | } 21 | } 22 | #endif 23 | -------------------------------------------------------------------------------- /Core_BetterPenetration/ItemColliderInfo.cs: -------------------------------------------------------------------------------- 1 | #if AI || HS2 2 | using System.Collections.Generic; 3 | 4 | namespace Core_BetterPenetration 5 | { 6 | class ItemColliderInfo 7 | { 8 | internal List animationNames; 9 | internal List itemBones; 10 | internal DynamicBoneColliderBase.Direction direction; 11 | internal float colliderRadius; 12 | internal float colliderHeight; 13 | 14 | public ItemColliderInfo(List animationNames, List itemBones, DynamicBoneColliderBase.Direction direction, float colliderRadius, float colliderHeight) 15 | { 16 | this.animationNames = animationNames; 17 | this.itemBones = itemBones; 18 | this.direction = direction; 19 | this.colliderRadius = colliderRadius; 20 | this.colliderHeight = colliderHeight; 21 | } 22 | } 23 | } 24 | #endif -------------------------------------------------------------------------------- /Core_BetterPenetration/CollisionPoint.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace Core_BetterPenetration 4 | { 5 | class CollisionPoint 6 | { 7 | internal Transform transform; 8 | internal CollisionPointInfo info; 9 | 10 | public CollisionPoint(Transform point, CollisionPointInfo collisionInfo) 11 | { 12 | transform = point; 13 | info = collisionInfo; 14 | } 15 | 16 | public void UpdateCollisionInfo(CollisionPointInfo collisionInfo) 17 | { 18 | info = collisionInfo; 19 | } 20 | 21 | } 22 | 23 | class CollisionPointInfo 24 | { 25 | internal string name; 26 | internal float offset; 27 | internal bool inward; 28 | 29 | public CollisionPointInfo(string transformName, float offsetValue, bool inwardDirection) 30 | { 31 | name = transformName; 32 | offset = offsetValue; 33 | inward = inwardDirection; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Core_BetterPenetration/Core_BetterPenetration.shproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ccd25b62-1040-4727-a0b5-78335fcf9f01 5 | 14.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Core_BetterPenetration/CollisionPoints.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace Core_BetterPenetration 5 | { 6 | class CollisionPoints 7 | { 8 | internal List frontCollisionPoints; 9 | internal List backCollisionPoints; 10 | 11 | public CollisionPoints() 12 | { 13 | frontCollisionPoints = new List(); 14 | backCollisionPoints = new List(); 15 | } 16 | 17 | public CollisionPoints(List frontPoints, List backPoints) 18 | { 19 | frontCollisionPoints = frontPoints; 20 | backCollisionPoints = backPoints; 21 | } 22 | 23 | public void UpdateCollisionOptions(CollisionOptions options) 24 | { 25 | for (var point = 0; point < frontCollisionPoints.Count && point < options.frontCollisionInfo.Count; point++) 26 | frontCollisionPoints[point].UpdateCollisionInfo(options.frontCollisionInfo[point]); 27 | 28 | for (var point = 0; point < frontCollisionPoints.Count && point < options.frontCollisionInfo.Count; point++) 29 | backCollisionPoints[point].UpdateCollisionInfo(options.backCollisonInfo[point]); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Core_BetterPenetration/DanPoint.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | 3 | namespace Core_BetterPenetration 4 | { 5 | class DanPoint 6 | { 7 | internal Transform transform; 8 | internal Vector3 defaultLocalPosition; 9 | internal Vector3 defaultLocalScale; 10 | internal Vector3 defaultLossyScale; 11 | 12 | public DanPoint(Transform point) 13 | { 14 | this.transform = point; 15 | this.defaultLocalPosition = point.localPosition; 16 | this.defaultLocalScale = point.localScale; 17 | this.defaultLossyScale = point.lossyScale; 18 | } 19 | 20 | internal void ResetDanPoint() 21 | { 22 | if (transform == null) 23 | return; 24 | 25 | transform.localPosition = defaultLocalPosition; 26 | transform.localScale = defaultLocalScale; 27 | } 28 | 29 | internal void SetDanPointRotation(Quaternion rotation) 30 | { 31 | if (transform == null) 32 | return; 33 | 34 | transform.rotation = rotation; 35 | } 36 | 37 | internal Quaternion GetDanPointRotation() 38 | { 39 | if (transform == null) 40 | return Quaternion.identity; 41 | 42 | return transform.rotation; 43 | } 44 | 45 | internal void ScaleDanGirth(float girthScale) 46 | { 47 | if (transform == null) 48 | return; 49 | 50 | transform.localScale = new Vector3(defaultLocalScale.x * girthScale, defaultLocalScale.y * girthScale, transform.localScale.z); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Core_BetterPenetration/Core_BetterPenetration.projitems: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | ccd25b62-1040-4727-a0b5-78335fcf9f01 7 | 8 | 9 | Core_BetterPenetration 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BetterPenetration 2 | This plugin seeks to improve HScenes in AI Shoujo, Honey Select 2, Koikatsu, and Koikatsu Sunshine by making penetration look a bit more realistic. It also adds some additional features to overall improve the HScene experience. 3 | 4 | # Features 5 | Replaces the default "telescoping" behavior by allowing the head to move past the point of penetration.
6 | After the penis has passed the point of penetration, it will bend to avoid clipping.
7 | You can allow the penis to begin to squish after it has penetrated by a specified percentage
8 | Works for vaginal, anal, and oral penetraion.
9 | Maintain proper rotation of the penis, no more spinning shafts during certain positions.
10 | Offset options to further tweak things when using characters with abnormal body shapes.
11 | Supports multiple male and multiple female positions.
12 | Includes several male and female uncensors designed specifically for BetterPenetration, the plugin works best with those uncensors.
13 | Female uncensors will spread open when penetrated. They also include individual toe bones that can be repositioned in studio.
14 | Penis uncensors have additional bones which allows them to bend, so they can penetrate at the correct point and still not clip.
15 | Ball uncensors have additional dynamic bones which are affected by gravity and will collide with the guy and the girl, drastically improving the way they look.
16 | Adds a push/pull effect to the mouth (all games) and vagina (HS2/AIS). This effect pushes the mouth/vagina inward during insertion and pulls it outward during extraction.
17 | (HS2/AIS) Adds colliders to all characters fingers and other game objects so the vagina spreads correctly when using those objects.
18 | 19 | # Installation 20 | Requires BepInEx
21 | Copy the dll to your install directory /BepInEx/plugins 22 | 23 | # For early releases and to offer support visit: 24 | https://www.patreon.com/Animal42069 25 | -------------------------------------------------------------------------------- /Core_BetterPenetration/DanOptions.cs: -------------------------------------------------------------------------------- 1 | namespace Core_BetterPenetration 2 | { 3 | class DanOptions 4 | { 5 | public float danLengthSquish; 6 | public float danGirthSquish; 7 | public float squishThreshold; 8 | public float danRadiusScale; 9 | public float danLengthScale; 10 | 11 | public bool simplifyVaginal; 12 | public bool simplifyOral; 13 | public bool simplifyAnal; 14 | public bool squishOralGirth; 15 | public bool rotateTamaWithShaft; 16 | 17 | public bool limitCorrection; 18 | public float maxCorrection; 19 | 20 | public DanOptions(float danRadiusScale, float danLengthScale, 21 | float danLengthSquish, float danGirthSquish, float squishThreshold, bool squishOralGirth, 22 | bool simplifyVaginal, bool simplifyOral, bool simplifyAnal, bool rotateTamaWithShaft, 23 | bool limitCorrection, float maxCorrection) 24 | { 25 | this.danLengthSquish = danLengthSquish; 26 | this.danGirthSquish = danGirthSquish; 27 | this.squishThreshold = squishThreshold; 28 | this.simplifyVaginal = simplifyVaginal; 29 | this.simplifyOral = simplifyOral; 30 | this.simplifyAnal = simplifyAnal; 31 | this.squishOralGirth = squishOralGirth; 32 | this.rotateTamaWithShaft = rotateTamaWithShaft; 33 | this.danRadiusScale = danRadiusScale; 34 | this.danLengthScale = danLengthScale; 35 | this.limitCorrection = limitCorrection; 36 | this.maxCorrection = maxCorrection; 37 | } 38 | 39 | public DanOptions(float danRadiusScale, float danLengthScale, 40 | float danLengthSquish, float danGirthSquish, float squishThreshold, 41 | bool limitCorrection, float maxCorrection) 42 | { 43 | this.danLengthSquish = danLengthSquish; 44 | this.danGirthSquish = danGirthSquish; 45 | this.squishThreshold = squishThreshold; 46 | this.danRadiusScale = danRadiusScale; 47 | this.danLengthScale = danLengthScale; 48 | simplifyVaginal = true; 49 | simplifyOral = true; 50 | simplifyAnal = true; 51 | squishOralGirth = true; 52 | rotateTamaWithShaft = true; 53 | this.limitCorrection = limitCorrection; 54 | this.maxCorrection = maxCorrection; 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Core_BetterPenetration/MathHelpers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace Core_BetterPenetration 5 | { 6 | internal static class MathHelpers 7 | { 8 | public enum Axis 9 | { 10 | X = 0, 11 | Y = 1, 12 | Z = 2 13 | } 14 | 15 | // Casts a point onto an infinite line defined by two points on the line 16 | internal static Vector3 CastToSegment(Vector3 position, Vector3 lineStart, Vector3 lineVector) 17 | { 18 | Vector3 lineEnd = lineStart + lineVector; 19 | float normDistAlongSegment = Vector3.Dot(position - lineStart, lineVector) / Vector3.Magnitude(lineVector); 20 | return Vector3.LerpUnclamped(lineStart, lineEnd, normDistAlongSegment); 21 | } 22 | 23 | // Finds the point on segment C to D that is closest to segment A to B 24 | // SEGMENT C D must be normalized 25 | internal static Vector3 CastSegmentToSegment(Vector3 projFromStart, Vector3 projFromVector, Vector3 projToStart, Vector3 projToVector) 26 | { 27 | Vector3 projFromEnd = projFromStart + projFromVector; 28 | Vector3 inPlaneStart = projFromStart - Vector3.Dot(projFromStart - projToStart, projToVector) * projToVector; 29 | Vector3 inPlaneEnd = projFromEnd - Vector3.Dot(projFromEnd - projToStart, projToVector) * projToVector; 30 | Vector3 inPlaneVector = inPlaneEnd - inPlaneStart; 31 | Vector3 inToPlaneStart = projToStart - inPlaneStart; 32 | 33 | float dotinToPlaneStartinPlaneVector = Vector3.Dot(inToPlaneStart, inPlaneVector); 34 | float inPlaneVectorMag = Vector3.Magnitude(inPlaneVector); 35 | float normDistAlongLine = dotinToPlaneStartinPlaneVector / inPlaneVectorMag; 36 | 37 | return CastToSegment(projFromStart + projFromVector * normDistAlongLine, projToStart, projToVector); 38 | } 39 | 40 | internal static double DegToRad(double degrees) 41 | { 42 | return (degrees * Math.PI / 180); 43 | } 44 | 45 | internal static double RadToDeg(double radians) 46 | { 47 | return (radians * 180 / Math.PI); 48 | } 49 | 50 | // returns the two solutions of a quadratic equation 51 | internal static bool SolveQuadratic(double quadA, double quadB, double quadC, out double solution1, out double solution2) 52 | { 53 | solution1 = solution2 = 0; 54 | if (ApproximatelyZero(quadA)) 55 | { 56 | if (ApproximatelyZero(quadB)) 57 | return false; 58 | 59 | solution1 = solution2 = -quadC / quadB; 60 | return true; 61 | } 62 | 63 | double quadD = (quadB * quadB) - (4 * quadA * quadC); 64 | if (quadD >= 0) 65 | { 66 | solution1 = (-quadB + Math.Sqrt(quadD)) / (2 * quadA); 67 | solution2 = (-quadB - Math.Sqrt(quadD)) / (2 * quadA); 68 | return true; 69 | } 70 | 71 | return false; 72 | } 73 | 74 | internal static bool ApproximatelyZero(double value) 75 | { 76 | return (value > -0.0001 && value < 0.0001); 77 | } 78 | 79 | internal static void SolveSSATriangle(double sideA, double sideB, double angleA, out double sideC, out double angleB, out double angleC) 80 | { 81 | sideC = 0; 82 | angleB = 0; 83 | angleC = 0; 84 | 85 | if (ApproximatelyZero(sideA) || ApproximatelyZero(sideB) || ApproximatelyZero(angleA)) 86 | return; 87 | 88 | angleB = Math.Asin(Math.Sin(angleA) * sideB / sideA); 89 | angleC = Math.PI - angleA - angleB; 90 | sideC = sideA * Math.Sin(angleC) / Math.Sin(angleA); 91 | } 92 | 93 | // Return True if Vectors are close enough to each other 94 | internal static bool VectorsEqual(Vector3 firstVector, Vector3 secondVector, float threshold = 0.01f) 95 | { 96 | if (Math.Abs(firstVector.x - secondVector.x) > threshold) 97 | return false; 98 | 99 | if (Math.Abs(firstVector.y - secondVector.y) > threshold) 100 | return false; 101 | 102 | if (Math.Abs(firstVector.z - secondVector.z) > threshold) 103 | return false; 104 | 105 | return true; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5.0.1.5 5 | 6 | 7 | https://gitgoon.dev/IllusionMods/BetterPenetration 8 | 9 | Copyright GPL-3.0 © 2020 10 | 11 | BetterPenetration 12 | 13 | Make penetration look a bit more realistic and overall improve the HScene experience 14 | 15 | false 16 | 17 | Library 18 | true 19 | 20 | true 21 | true 22 | embedded 23 | 512 24 | 25 | 26 | 27 | true 28 | false 29 | 30 | 31 | 32 | 33 | 34 | PreserveNewest 35 | 36 | 37 | PreserveNewest 38 | 39 | 40 | 41 | 42 | 43 | $(MSBuildProjectName.Replace('-',';').Replace('_',';')) 44 | 45 | ..\bin\$(MSBuildProjectName.Substring(0, $(MSBuildProjectName.IndexOf('_'))))\ 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | $(OutputPath)\..\TEMP_COPY_$(AssemblyName) 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 89 | 90 | $(IntermediateOutputPath)GeneratedConstantsFile.cs 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /Core_BetterPenetration/DanPoints.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace Core_BetterPenetration 5 | { 6 | class DanPoints 7 | { 8 | internal Transform danTop; 9 | internal List danPoints; 10 | internal Transform danEnd; 11 | internal List virtualDanPoints; 12 | 13 | public DanPoints(List danTransforms, Transform top, Transform end = null, List virtualDanTransforms = null) 14 | { 15 | danTop = top; 16 | danEnd = end; 17 | virtualDanPoints = virtualDanTransforms; 18 | danPoints = new List(); 19 | 20 | foreach (var transform in danTransforms) 21 | danPoints.Add(new DanPoint(transform)); 22 | } 23 | 24 | internal void AimDanPoints(List newDanPositions, bool aimTop, List virtualDanPositions) 25 | { 26 | if (newDanPositions == null || danPoints == null || newDanPositions.Count == 0 || danPoints.Count == 0 || newDanPositions.Count != danPoints.Count) 27 | return; 28 | 29 | Quaternion danQuaternion = Quaternion.identity; 30 | for (int point = 0; point < danPoints.Count - 1; point++) 31 | { 32 | Vector3 forwardVector = Vector3.Normalize(newDanPositions[point + 1] - newDanPositions[point]); 33 | danQuaternion = Quaternion.LookRotation(forwardVector, Vector3.Cross(forwardVector, danTop.right)); 34 | danPoints[point].transform.SetPositionAndRotation(newDanPositions[point], danQuaternion); 35 | } 36 | 37 | danPoints[danPoints.Count - 1].transform.SetPositionAndRotation(newDanPositions[danPoints.Count - 1], danQuaternion); 38 | 39 | if (danEnd != null) 40 | danEnd.transform.SetPositionAndRotation(newDanPositions[danPoints.Count - 1], danQuaternion); 41 | 42 | if (aimTop) 43 | AimDanTop(); 44 | 45 | if (virtualDanPositions == null || virtualDanPoints == null || virtualDanPositions.Count == 0 || virtualDanPoints.Count == 0 || virtualDanPositions.Count != virtualDanPoints.Count) 46 | return; 47 | 48 | for (int point = 0; point < virtualDanPoints.Count - 1; point++) 49 | { 50 | Vector3 forwardVector = Vector3.Normalize(virtualDanPositions[point + 1] - virtualDanPositions[point]); 51 | danQuaternion = Quaternion.LookRotation(forwardVector, Vector3.Cross(forwardVector, danTop.right)); 52 | virtualDanPoints[point].SetPositionAndRotation(virtualDanPositions[point], danQuaternion); 53 | } 54 | 55 | virtualDanPoints[virtualDanPoints.Count - 1].SetPositionAndRotation(virtualDanPositions[virtualDanPoints.Count - 1], danQuaternion); 56 | } 57 | 58 | internal void AimDanTop() 59 | { 60 | danTop.transform.rotation = danPoints[0].transform.rotation; 61 | } 62 | 63 | #if !Studio 64 | internal void SquishDanGirth(float girthScaleFactor) 65 | { 66 | float points = danPoints.Count - 1; 67 | if (points <= 0) 68 | return; 69 | 70 | float inverseScaleFactor = (girthScaleFactor - (girthScaleFactor - 1) / points) / girthScaleFactor; 71 | 72 | for (int point = 0; point < danPoints.Count; point++) 73 | { 74 | if (point == 0) 75 | danPoints[point].ScaleDanGirth(girthScaleFactor); 76 | else 77 | danPoints[point].ScaleDanGirth(inverseScaleFactor); 78 | } 79 | } 80 | #else 81 | internal void SquishDanGirth(float girthScaleFactor) 82 | { 83 | float points = danPoints.Count - 2; 84 | if (points <= 0) 85 | return; 86 | 87 | float inverseScaleFactor = (girthScaleFactor - (girthScaleFactor - 1) / points) / girthScaleFactor; 88 | 89 | for (int point = 1; point < danPoints.Count; point++) 90 | { 91 | if (point == 1) 92 | danPoints[point].ScaleDanGirth(girthScaleFactor); 93 | else 94 | danPoints[point].ScaleDanGirth(inverseScaleFactor); 95 | } 96 | } 97 | #endif 98 | 99 | internal void ResetDanPoints() 100 | { 101 | if (danPoints == null || danPoints.Count <= 0) 102 | return; 103 | 104 | foreach (var danPoint in danPoints) 105 | danPoint.ResetDanPoint(); 106 | 107 | Quaternion danRotation = danPoints[0].GetDanPointRotation(); 108 | for (int point = 1; point < danPoints.Count - 1; point++) 109 | danPoints[point].SetDanPointRotation(danRotation); 110 | } 111 | 112 | internal Vector3 GetDanStartPosition() 113 | { 114 | if (danPoints == null) 115 | return Vector3.zero; 116 | 117 | return danPoints[0].transform.position; 118 | } 119 | 120 | internal Vector3 GetDanEndPosition() 121 | { 122 | if (danPoints == null) 123 | return Vector3.zero; 124 | 125 | return danPoints[danPoints.Count - 1].transform.position; 126 | } 127 | 128 | internal float GetDanLossyScale() 129 | { 130 | if (danPoints == null || danPoints?[0].transform == null) 131 | return 1; 132 | 133 | return danPoints[0].transform.lossyScale.z; 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | PostBuild.ps1 2 | .vscode/ 3 | 4 | ## Ignore Visual Studio temporary files, build results, and 5 | ## files generated by popular Visual Studio add-ons. 6 | 7 | # User-specific files 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | bld/ 24 | [Bb]in/ 25 | [Oo]bj/ 26 | [Ll]og/ 27 | 28 | # Visual Studio 2015 cache/options directory 29 | .vs/ 30 | # Uncomment if you have tasks that create the project's static files in wwwroot 31 | #wwwroot/ 32 | 33 | # MSTest test Results 34 | [Tt]est[Rr]esult*/ 35 | [Bb]uild[Ll]og.* 36 | 37 | # NUNIT 38 | *.VisualState.xml 39 | TestResult.xml 40 | 41 | # Build Results of an ATL Project 42 | [Dd]ebugPS/ 43 | [Rr]eleasePS/ 44 | dlldata.c 45 | 46 | # DNX 47 | project.lock.json 48 | project.fragment.lock.json 49 | artifacts/ 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # NCrunch 117 | _NCrunch_* 118 | .*crunch*.local.xml 119 | nCrunchTemp_* 120 | 121 | # MightyMoose 122 | *.mm.* 123 | AutoTest.Net/ 124 | 125 | # Web workbench (sass) 126 | .sass-cache/ 127 | 128 | # Installshield output folder 129 | [Ee]xpress/ 130 | 131 | # DocProject is a documentation generator add-in 132 | DocProject/buildhelp/ 133 | DocProject/Help/*.HxT 134 | DocProject/Help/*.HxC 135 | DocProject/Help/*.hhc 136 | DocProject/Help/*.hhk 137 | DocProject/Help/*.hhp 138 | DocProject/Help/Html2 139 | DocProject/Help/html 140 | 141 | # Click-Once directory 142 | publish/ 143 | 144 | # Publish Web Output 145 | *.[Pp]ublish.xml 146 | *.azurePubxml 147 | # TODO: Comment the next line if you want to checkin your web deploy settings 148 | # but database connection strings (with potential passwords) will be unencrypted 149 | #*.pubxml 150 | *.publishproj 151 | 152 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 153 | # checkin your Azure Web App publish settings, but sensitive information contained 154 | # in these scripts will be unencrypted 155 | PublishScripts/ 156 | 157 | # NuGet Packages 158 | *.nupkg 159 | # The packages folder can be ignored because of Package Restore 160 | **/packages/* 161 | # except build/, which is used as an MSBuild target. 162 | !**/packages/build/ 163 | # Uncomment if necessary however generally it will be regenerated when needed 164 | #!**/packages/repositories.config 165 | # NuGet v3's project.json files produces more ignoreable files 166 | *.nuget.props 167 | *.nuget.targets 168 | 169 | # Microsoft Azure Build Output 170 | csx/ 171 | *.build.csdef 172 | 173 | # Microsoft Azure Emulator 174 | ecf/ 175 | rcf/ 176 | 177 | # Windows Store app package directories and files 178 | AppPackages/ 179 | BundleArtifacts/ 180 | Package.StoreAssociation.xml 181 | _pkginfo.txt 182 | 183 | # Visual Studio cache files 184 | # files ending in .cache can be ignored 185 | *.[Cc]ache 186 | # but keep track of directories ending in .cache 187 | !*.[Cc]ache/ 188 | 189 | # Others 190 | ClientBin/ 191 | ~$* 192 | *~ 193 | *.dbmdl 194 | *.dbproj.schemaview 195 | *.jfm 196 | *.pfx 197 | *.publishsettings 198 | node_modules/ 199 | orleans.codegen.cs 200 | 201 | # Since there are multiple workflows, uncomment next line to ignore bower_components 202 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 203 | #bower_components/ 204 | 205 | # RIA/Silverlight projects 206 | Generated_Code/ 207 | 208 | # Backup & report files from converting an old project file 209 | # to a newer Visual Studio version. Backup files are not needed, 210 | # because we have git ;-) 211 | _UpgradeReport_Files/ 212 | Backup*/ 213 | UpgradeLog*.XML 214 | UpgradeLog*.htm 215 | 216 | # SQL Server files 217 | *.mdf 218 | *.ldf 219 | 220 | # Business Intelligence projects 221 | *.rdl.data 222 | *.bim.layout 223 | *.bim_*.settings 224 | 225 | # Microsoft Fakes 226 | FakesAssemblies/ 227 | 228 | # GhostDoc plugin setting file 229 | *.GhostDoc.xml 230 | 231 | # Node.js Tools for Visual Studio 232 | .ntvs_analysis.dat 233 | 234 | # Visual Studio 6 build log 235 | *.plg 236 | 237 | # Visual Studio 6 workspace options file 238 | *.opt 239 | 240 | # Visual Studio LightSwitch build output 241 | **/*.HTMLClient/GeneratedArtifacts 242 | **/*.DesktopClient/GeneratedArtifacts 243 | **/*.DesktopClient/ModelManifest.xml 244 | **/*.Server/GeneratedArtifacts 245 | **/*.Server/ModelManifest.xml 246 | _Pvt_Extensions 247 | 248 | # Paket dependency manager 249 | #.paket/paket.exe 250 | .paket/Paket.Restore.targets 251 | paket-files/ 252 | 253 | # FAKE - F# Make 254 | .fake/ 255 | 256 | # JetBrains Rider 257 | .idea/ 258 | *.sln.iml 259 | 260 | # CodeRush 261 | .cr/ 262 | 263 | # Python Tools for Visual Studio (PTVS) 264 | __pycache__/ 265 | *.pyc 266 | -------------------------------------------------------------------------------- /Core_BetterPenetration/CollisionOptions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using UnityEngine; 3 | 4 | namespace Core_BetterPenetration 5 | { 6 | class CollisionOptions 7 | { 8 | internal float kokanOffset = 0.0f; 9 | internal float innerKokanOffset = 0.0f; 10 | internal float mouthOffset = 0.0f; 11 | internal float innerMouthOffset = 0.0f; 12 | 13 | internal bool kokan_adjust = false; 14 | internal float kokan_adjust_position_z = 0; 15 | internal float kokan_adjust_position_y = 0; 16 | internal float kokan_adjust_rotation_x = 0; 17 | internal float clippingDepth = 0; 18 | internal bool ana_adjust = false; 19 | internal Vector3 ana_adjust_position = Vector3.zero; 20 | internal Vector3 ana_adjust_rotation = Vector3.zero; 21 | #if HS2 || AI 22 | internal bool enableKokanPush = true; 23 | #else 24 | internal bool enableKokanPush = false; 25 | #endif 26 | internal float maxKokanPush = 0.08f; 27 | internal float maxKokanPull = 0.04f; 28 | internal float kokanPullRate = 18.0f; 29 | internal float kokanReturnRate = 0.3f; 30 | internal bool enableOralPush = true; 31 | internal float maxOralPush = 0.02f; 32 | internal float maxOralPull = 0.10f; 33 | internal float oralPullRate = 18.0f; 34 | internal float oralReturnRate = 0.3f; 35 | #if HS2 || AI 36 | internal bool enableAnaPush = true; 37 | #else 38 | internal bool enableAnaPush = false; 39 | #endif 40 | internal float maxAnaPush = 0.08f; 41 | internal float maxAnaPull = 0.04f; 42 | internal float anaPullRate = 18.0f; 43 | internal float anaReturnRate = 0.3f; 44 | 45 | internal TargetType outer = TargetType.Average; 46 | internal TargetType inner = TargetType.Average; 47 | 48 | internal List frontCollisionInfo; 49 | internal List backCollisonInfo; 50 | 51 | internal bool enableBellyBulge = true; 52 | internal float bellyBulgeScale = 1.0f; 53 | 54 | public enum TargetType 55 | { 56 | Outer = 0, 57 | Average = 1, 58 | Inside = 2 59 | } 60 | 61 | public CollisionOptions(float kokanOffset, float innerKokanOffset, float mouthOffset, float innerMouthOffset, 62 | bool kokan_adjust, float kokan_adjust_position_z, float kokan_adjust_position_y, float kokan_adjust_rotation_x, 63 | bool ana_adjust, Vector3 ana_adjust_position, Vector3 ana_adjust_rotation, 64 | float clippingDepth, List frontInfo, List backInfo, 65 | bool enableKokanPush, float maxKokanPush, float maxKokanPull, float kokanPullRate, float kokanReturnRate, 66 | bool enableOralPush, float maxOralPush, float maxOralPull, float oralPullRate, float oralReturnRate, 67 | bool enableAnaPush, float maxAnaPush, float maxAnaPull, float anaPullRate, float anaReturnRate, TargetType outer = TargetType.Average, TargetType inner = TargetType.Inside, 68 | bool enableBellyBulge = true, float bellyBulgeScale = 1.0f) 69 | { 70 | this.kokanOffset = kokanOffset; 71 | this.innerKokanOffset = innerKokanOffset; 72 | this.mouthOffset = mouthOffset; 73 | this.innerMouthOffset = innerMouthOffset; 74 | 75 | this.kokan_adjust = kokan_adjust; 76 | this.kokan_adjust_position_z = kokan_adjust_position_z; 77 | this.kokan_adjust_position_y = kokan_adjust_position_y; 78 | this.kokan_adjust_rotation_x = kokan_adjust_rotation_x; 79 | this.clippingDepth = clippingDepth; 80 | 81 | this.ana_adjust = ana_adjust; 82 | this.ana_adjust_position = ana_adjust_position; 83 | this.ana_adjust_rotation = ana_adjust_rotation; 84 | 85 | #if HS2 || AI 86 | this.enableKokanPush = enableKokanPush; 87 | #else 88 | this.enableKokanPush = false; 89 | #endif 90 | this.maxKokanPush = maxKokanPush; 91 | this.maxKokanPull = maxKokanPull; 92 | this.kokanPullRate = kokanPullRate; 93 | this.kokanReturnRate = kokanReturnRate; 94 | 95 | this.enableOralPush = enableOralPush; 96 | this.maxOralPush = maxOralPush; 97 | this.maxOralPull = maxOralPull; 98 | this.oralPullRate = oralPullRate; 99 | this.oralReturnRate = oralReturnRate; 100 | 101 | this.enableAnaPush = enableAnaPush; 102 | this.maxAnaPush = maxAnaPush; 103 | this.maxAnaPull = maxAnaPull; 104 | this.anaPullRate = anaPullRate; 105 | this.anaReturnRate = anaReturnRate; 106 | 107 | frontCollisionInfo = frontInfo; 108 | backCollisonInfo = backInfo; 109 | 110 | this.outer = outer; 111 | this.inner = inner; 112 | 113 | this.enableBellyBulge = enableBellyBulge; 114 | this.bellyBulgeScale = bellyBulgeScale; 115 | } 116 | 117 | #if Studio 118 | public CollisionOptions(float maxPush, float maxPull, float pullRate, float returnRate, bool enableBellyBulge = true, float bellyBulgeScale = 1.0f) 119 | { 120 | kokanOffset = 0.0f; 121 | innerKokanOffset = 0.0f; 122 | mouthOffset = 0.0f; 123 | innerMouthOffset = 0.0f; 124 | 125 | kokan_adjust = false; 126 | kokan_adjust_position_z = 0; 127 | kokan_adjust_position_y = 0; 128 | kokan_adjust_rotation_x = 0; 129 | clippingDepth = 0; 130 | ana_adjust = false; 131 | ana_adjust_position = Vector3.zero; 132 | ana_adjust_rotation = Vector3.zero; 133 | 134 | #if HS2 || AI 135 | enableKokanPush = true; 136 | enableOralPush = true; 137 | enableAnaPush = true; 138 | #else 139 | enableKokanPush = false; 140 | enableOralPush = true; 141 | enableAnaPush = false; 142 | #endif 143 | maxKokanPush = maxPush; 144 | maxKokanPull = maxPull; 145 | kokanPullRate = pullRate; 146 | kokanReturnRate = returnRate; 147 | 148 | maxOralPush = maxPush; 149 | maxOralPull = maxPull; 150 | oralPullRate = pullRate; 151 | oralReturnRate = returnRate; 152 | 153 | maxAnaPush = maxPush; 154 | maxAnaPull = maxPull; 155 | anaPullRate = pullRate; 156 | anaReturnRate = returnRate; 157 | 158 | frontCollisionInfo = null; 159 | backCollisonInfo = null; 160 | 161 | this.enableBellyBulge = enableBellyBulge; 162 | this.bellyBulgeScale = bellyBulgeScale; 163 | } 164 | #endif 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /BetterPenetration.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32328.378 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HS2_BetterPenetration", "HS2_BetterPenetration\HS2_BetterPenetration.csproj", "{F5E096BB-2509-4431-972E-148500210D06}" 7 | ProjectSection(ProjectDependencies) = postProject 8 | {06A2BE07-7AB5-46CD-8A77-FDE1BFDE687C} = {06A2BE07-7AB5-46CD-8A77-FDE1BFDE687C} 9 | EndProjectSection 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AI_BetterPenetration", "AI_BetterPenetration\AI_BetterPenetration.csproj", "{052B343B-8793-481B-B053-0A99B09A0B52}" 12 | ProjectSection(ProjectDependencies) = postProject 13 | {7008D2E3-1C29-4786-A4F6-7CFD6D6D8AD9} = {7008D2E3-1C29-4786-A4F6-7CFD6D6D8AD9} 14 | EndProjectSection 15 | EndProject 16 | Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Core_BetterPenetration", "Core_BetterPenetration\Core_BetterPenetration.shproj", "{CCD25B62-1040-4727-A0B5-78335FCF9F01}" 17 | EndProject 18 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KK_BetterPenetration", "KK_BetterPenetration\KK_BetterPenetration.csproj", "{13F643E0-45CA-483E-ADFA-32634A22787E}" 19 | ProjectSection(ProjectDependencies) = postProject 20 | {A68BEAE5-21B9-4C45-9624-A377BB953238} = {A68BEAE5-21B9-4C45-9624-A377BB953238} 21 | EndProjectSection 22 | EndProject 23 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HS2_Studio_BetterPenetration", "HS2_Studio_BetterPenetration\HS2_Studio_BetterPenetration.csproj", "{06A2BE07-7AB5-46CD-8A77-FDE1BFDE687C}" 24 | EndProject 25 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AI_Studio_BetterPenetration", "AI_Studio_BetterPenetration\AI_Studio_BetterPenetration.csproj", "{7008D2E3-1C29-4786-A4F6-7CFD6D6D8AD9}" 26 | EndProject 27 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KK_Studio_BetterPenetration", "KK_Studio_BetterPenetration\KK_Studio_BetterPenetration.csproj", "{A68BEAE5-21B9-4C45-9624-A377BB953238}" 28 | EndProject 29 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KKS_BetterPenetration", "KKS_BetterPenetration\KKS_BetterPenetration.csproj", "{0FF42A1B-86EB-4CEE-BD04-DA3BB249773F}" 30 | ProjectSection(ProjectDependencies) = postProject 31 | {B2F3F19C-14D5-477B-ADF7-EFBE76E85AD7} = {B2F3F19C-14D5-477B-ADF7-EFBE76E85AD7} 32 | EndProjectSection 33 | EndProject 34 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KKS_Studio_BetterPenetration", "KKS_Studio_BetterPenetration\KKS_Studio_BetterPenetration.csproj", "{B2F3F19C-14D5-477B-ADF7-EFBE76E85AD7}" 35 | EndProject 36 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Solution", "_Solution", "{58356582-991A-40E5-8DEF-704666191C72}" 37 | ProjectSection(SolutionItems) = preProject 38 | Directory.Build.props = Directory.Build.props 39 | README.md = README.md 40 | EndProjectSection 41 | EndProject 42 | Global 43 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 44 | Debug|Any CPU = Debug|Any CPU 45 | Release|Any CPU = Release|Any CPU 46 | EndGlobalSection 47 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 48 | {F5E096BB-2509-4431-972E-148500210D06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {F5E096BB-2509-4431-972E-148500210D06}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {F5E096BB-2509-4431-972E-148500210D06}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {F5E096BB-2509-4431-972E-148500210D06}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {052B343B-8793-481B-B053-0A99B09A0B52}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 | {052B343B-8793-481B-B053-0A99B09A0B52}.Debug|Any CPU.Build.0 = Debug|Any CPU 54 | {052B343B-8793-481B-B053-0A99B09A0B52}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {052B343B-8793-481B-B053-0A99B09A0B52}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {13F643E0-45CA-483E-ADFA-32634A22787E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {13F643E0-45CA-483E-ADFA-32634A22787E}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {13F643E0-45CA-483E-ADFA-32634A22787E}.Release|Any CPU.ActiveCfg = Release|Any CPU 59 | {13F643E0-45CA-483E-ADFA-32634A22787E}.Release|Any CPU.Build.0 = Release|Any CPU 60 | {06A2BE07-7AB5-46CD-8A77-FDE1BFDE687C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 61 | {06A2BE07-7AB5-46CD-8A77-FDE1BFDE687C}.Debug|Any CPU.Build.0 = Debug|Any CPU 62 | {06A2BE07-7AB5-46CD-8A77-FDE1BFDE687C}.Release|Any CPU.ActiveCfg = Release|Any CPU 63 | {06A2BE07-7AB5-46CD-8A77-FDE1BFDE687C}.Release|Any CPU.Build.0 = Release|Any CPU 64 | {7008D2E3-1C29-4786-A4F6-7CFD6D6D8AD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 65 | {7008D2E3-1C29-4786-A4F6-7CFD6D6D8AD9}.Debug|Any CPU.Build.0 = Debug|Any CPU 66 | {7008D2E3-1C29-4786-A4F6-7CFD6D6D8AD9}.Release|Any CPU.ActiveCfg = Release|Any CPU 67 | {7008D2E3-1C29-4786-A4F6-7CFD6D6D8AD9}.Release|Any CPU.Build.0 = Release|Any CPU 68 | {A68BEAE5-21B9-4C45-9624-A377BB953238}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 69 | {A68BEAE5-21B9-4C45-9624-A377BB953238}.Debug|Any CPU.Build.0 = Debug|Any CPU 70 | {A68BEAE5-21B9-4C45-9624-A377BB953238}.Release|Any CPU.ActiveCfg = Release|Any CPU 71 | {A68BEAE5-21B9-4C45-9624-A377BB953238}.Release|Any CPU.Build.0 = Release|Any CPU 72 | {0FF42A1B-86EB-4CEE-BD04-DA3BB249773F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 73 | {0FF42A1B-86EB-4CEE-BD04-DA3BB249773F}.Debug|Any CPU.Build.0 = Debug|Any CPU 74 | {0FF42A1B-86EB-4CEE-BD04-DA3BB249773F}.Release|Any CPU.ActiveCfg = Release|Any CPU 75 | {0FF42A1B-86EB-4CEE-BD04-DA3BB249773F}.Release|Any CPU.Build.0 = Release|Any CPU 76 | {B2F3F19C-14D5-477B-ADF7-EFBE76E85AD7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 77 | {B2F3F19C-14D5-477B-ADF7-EFBE76E85AD7}.Debug|Any CPU.Build.0 = Debug|Any CPU 78 | {B2F3F19C-14D5-477B-ADF7-EFBE76E85AD7}.Release|Any CPU.ActiveCfg = Release|Any CPU 79 | {B2F3F19C-14D5-477B-ADF7-EFBE76E85AD7}.Release|Any CPU.Build.0 = Release|Any CPU 80 | EndGlobalSection 81 | GlobalSection(SolutionProperties) = preSolution 82 | HideSolutionNode = FALSE 83 | EndGlobalSection 84 | GlobalSection(ExtensibilityGlobals) = postSolution 85 | SolutionGuid = {CCFA8C78-8530-4958-8678-C27D5C24C5F8} 86 | EndGlobalSection 87 | GlobalSection(SharedMSBuildProjectFiles) = preSolution 88 | Core_BetterPenetration\Core_BetterPenetration.projitems*{052b343b-8793-481b-b053-0a99b09a0b52}*SharedItemsImports = 5 89 | Core_BetterPenetration\Core_BetterPenetration.projitems*{06a2be07-7ab5-46cd-8a77-fde1bfde687c}*SharedItemsImports = 5 90 | Core_BetterPenetration\Core_BetterPenetration.projitems*{0ff42a1b-86eb-4cee-bd04-da3bb249773f}*SharedItemsImports = 5 91 | Core_BetterPenetration\Core_BetterPenetration.projitems*{13f643e0-45ca-483e-adfa-32634a22787e}*SharedItemsImports = 5 92 | Core_BetterPenetration\Core_BetterPenetration.projitems*{7008d2e3-1c29-4786-a4f6-7cfd6d6d8ad9}*SharedItemsImports = 5 93 | Core_BetterPenetration\Core_BetterPenetration.projitems*{a68beae5-21b9-4c45-9624-a377bb953238}*SharedItemsImports = 5 94 | Core_BetterPenetration\Core_BetterPenetration.projitems*{b2f3f19c-14d5-477b-adf7-efbe76e85ad7}*SharedItemsImports = 5 95 | Core_BetterPenetration\Core_BetterPenetration.projitems*{ccd25b62-1040-4727-a0b5-78335fcf9f01}*SharedItemsImports = 13 96 | Core_BetterPenetration\Core_BetterPenetration.projitems*{f5e096bb-2509-4431-972e-148500210d06}*SharedItemsImports = 5 97 | EndGlobalSection 98 | EndGlobal 99 | -------------------------------------------------------------------------------- /Core_BetterPenetration/Tools.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using UnityEngine; 5 | using System.Linq; 6 | #if AI || HS2 7 | using AIChara; 8 | #endif 9 | 10 | namespace Core_BetterPenetration 11 | { 12 | static class Tools 13 | { 14 | internal struct MemberKey 15 | { 16 | public readonly Type type; 17 | public readonly string name; 18 | internal readonly int _hashCode; 19 | 20 | public MemberKey(Type inType, string inName) 21 | { 22 | this.type = inType; 23 | this.name = inName; 24 | this._hashCode = this.type.GetHashCode() ^ this.name.GetHashCode(); 25 | } 26 | 27 | public override int GetHashCode() 28 | { 29 | return this._hashCode; 30 | } 31 | } 32 | 33 | internal static readonly Dictionary _propertyCache = new Dictionary(); 34 | 35 | internal static object GetPrivateProperty(this object self, string name) 36 | { 37 | MemberKey key = new MemberKey(self.GetType(), name); 38 | if (_propertyCache.TryGetValue(key, out PropertyInfo info) == false) 39 | { 40 | info = key.type.GetProperty(key.name, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy); 41 | _propertyCache.Add(key, info); 42 | } 43 | return info.GetValue(self, null); 44 | } 45 | 46 | public static Transform GetTransformOfChaControl(ChaControl chaControl, string transformName) 47 | { 48 | Transform transform = null; 49 | if (chaControl == null) 50 | return transform; 51 | 52 | var transforms = chaControl.GetComponentsInChildren().Where(x => x.name != null && x.name.Equals(transformName)); 53 | if (transforms.Count() == 0) 54 | return transform; 55 | 56 | for (int transformIndex = transforms.Count() - 1; transformIndex >= 0; transformIndex--) 57 | { 58 | transform = transforms.ElementAt(transformIndex); 59 | if (transform == null || chaControl != transform.GetComponentInParent()) 60 | continue; 61 | 62 | return transform; 63 | } 64 | 65 | return transform; 66 | } 67 | 68 | public static DynamicBone GetDynamicBoneOfChaControl(ChaControl chaControl, string dynamicBoneName) 69 | { 70 | DynamicBone dynamicBone = null; 71 | if (chaControl == null) 72 | return dynamicBone; 73 | 74 | var dynamicBones = chaControl.GetComponentsInChildren().Where(x => x.name != null && x.name.Equals(dynamicBoneName)); 75 | if (dynamicBones.Count() == 0) 76 | return dynamicBone; 77 | 78 | for (int boneIndex = dynamicBones.Count() - 1; boneIndex >= 0; boneIndex--) 79 | { 80 | dynamicBone = dynamicBones.ElementAt(boneIndex); 81 | if (dynamicBone != null && chaControl == dynamicBone.GetComponentInParent()) 82 | return dynamicBone; 83 | } 84 | 85 | return dynamicBone; 86 | } 87 | 88 | public static List GetCollidersOfChaControl(ChaControl chaControl, string colliderName) 89 | { 90 | List colliderList = new List(); 91 | if (chaControl == null) 92 | return colliderList; 93 | 94 | var colliders = chaControl.GetComponentsInChildren().Where(x => x.name != null && x.name.Contains(colliderName)); 95 | if (colliders.Count() == 0) 96 | return colliderList; 97 | 98 | foreach (var collider in colliders) 99 | { 100 | if (chaControl != collider.GetComponentInParent()) 101 | continue; 102 | 103 | colliderList.Add(collider); 104 | } 105 | 106 | return colliderList; 107 | } 108 | 109 | public static List GetCollidersOfChaControl(ChaControl chaControl, List colliderNames) 110 | { 111 | List colliderList = new List(); 112 | if (chaControl == null) 113 | return colliderList; 114 | 115 | var colliders = chaControl.GetComponentsInChildren().Where(x => x.name != null && colliderNames.Contains(x.name)); 116 | if (colliders.Count() == 0) 117 | return colliderList; 118 | 119 | foreach (var collider in colliders) 120 | { 121 | if (chaControl != collider.GetComponentInParent()) 122 | continue; 123 | 124 | colliderList.Add(collider); 125 | } 126 | 127 | return colliderList; 128 | } 129 | 130 | internal static DynamicBoneCollider InitializeCollider(Transform parent, float radius, float length, Vector3 centerOffset, 131 | DynamicBoneCollider.Direction direction = DynamicBoneCollider.Direction.X, 132 | DynamicBoneCollider.Bound bound = DynamicBoneCollider.Bound.Outside) 133 | { 134 | if (parent == null) 135 | return null; 136 | 137 | DynamicBoneCollider collider = parent.GetComponent(); 138 | 139 | if (collider == null) 140 | collider = parent.gameObject.AddComponent(typeof(DynamicBoneCollider)) as DynamicBoneCollider; 141 | 142 | collider.m_Direction = direction; 143 | collider.m_Center = centerOffset; 144 | collider.m_Bound = bound; 145 | collider.m_Radius = radius; 146 | collider.m_Height = length; 147 | 148 | return collider; 149 | } 150 | 151 | internal static float ComputeRadiusScale(Transform transform, DynamicBoneCollider.Direction direction) 152 | { 153 | if (direction == DynamicBoneCollider.Direction.X) 154 | return (transform.lossyScale.y + transform.lossyScale.z) / 2; 155 | 156 | if (direction == DynamicBoneCollider.Direction.Y) 157 | return (transform.lossyScale.x + transform.lossyScale.z) / 2; 158 | 159 | return (transform.lossyScale.x + transform.lossyScale.y) / 2; 160 | } 161 | 162 | internal static float ComputeHeightScale(Transform transform, DynamicBoneCollider.Direction direction) 163 | { 164 | if (direction == DynamicBoneCollider.Direction.X) 165 | return transform.lossyScale.x; 166 | 167 | if (direction == DynamicBoneCollider.Direction.Y) 168 | return transform.lossyScale.y; 169 | 170 | return transform.lossyScale.z; 171 | } 172 | 173 | internal static void RemoveCollidersFromCoordinate(ChaControl character) 174 | { 175 | var dynamicBones = character.GetComponentsInChildren(true); 176 | 177 | if (dynamicBones == null) 178 | return; 179 | 180 | foreach (var dynamicBone in dynamicBones) 181 | { 182 | if (!dynamicBone) 183 | continue; 184 | 185 | var dbName = dynamicBone.name; 186 | var dbColliders = dynamicBone.m_Colliders; 187 | 188 | if (dbName == null || dbColliders == null || dbColliders.Count <= 0) 189 | continue; 190 | 191 | bool bpBone = dbName.Contains("Vagina") || dbName.Contains("Belly") || dbName.Contains("Ana"); 192 | int last = 0; 193 | 194 | for (int collider = 0; collider < dbColliders.Count; ++collider) 195 | { 196 | if (dbColliders[collider]) 197 | { 198 | var colliderName = dbColliders[collider].name; 199 | 200 | if (colliderName != null) 201 | { 202 | bool bpCollider = colliderName.Contains("cm_J_vdan") || colliderName.Contains("cm_J_dan"); 203 | 204 | if (bpBone != bpCollider) 205 | continue; //remove collider 206 | } 207 | } 208 | 209 | //keep collider 210 | if (last != collider) 211 | dbColliders[last] = dbColliders[collider]; 212 | ++last; 213 | } 214 | 215 | dbColliders.RemoveRange(last, dbColliders.Count - last); 216 | } 217 | } 218 | } 219 | } -------------------------------------------------------------------------------- /Core_BetterPenetration/TwistedPlane.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | 4 | namespace Core_BetterPenetration 5 | { 6 | // Creates a Plane defined by two non-parallel lines 7 | class TwistedPlane 8 | { 9 | internal Vector3 firstOrigin; 10 | internal Vector3 firstVector; 11 | internal Vector3 secondOrigin; 12 | internal Vector3 secondVector; 13 | internal Vector3 forwardVector; 14 | internal Vector3 twistVector; 15 | 16 | public TwistedPlane(Vector3 firstOrig, Vector3 firstVec, Vector3 secondOrig, Vector3 secondVec) 17 | { 18 | firstOrigin = firstOrig; 19 | firstVector = firstVec; 20 | secondOrigin = secondOrig; 21 | secondVector = secondVec; 22 | forwardVector = secondOrig - firstOrig; 23 | twistVector = secondVec - firstVec; 24 | } 25 | 26 | internal bool FindIntersectValues(Vector3 lineVector, Vector3 offsetVector, Vector3 FVxLV, Vector3 TWxLV, Vector3 OFxLV, Vector3 LVxFW, double u, out double v, out double t) 27 | { 28 | double Cu = 0; 29 | v = 0; 30 | t = 0; 31 | 32 | for (MathHelpers.Axis axis = MathHelpers.Axis.X; axis <= MathHelpers.Axis.Z; axis++) 33 | { 34 | if (axis == MathHelpers.Axis.X) 35 | { 36 | Cu = FVxLV.x + TWxLV.x * u; 37 | if (!MathHelpers.ApproximatelyZero(Cu)) 38 | { 39 | v = (OFxLV.x + LVxFW.x * u) / Cu; 40 | break; 41 | } 42 | } 43 | else if (axis == MathHelpers.Axis.Y) 44 | { 45 | Cu = FVxLV.y + TWxLV.y * u; 46 | if (!MathHelpers.ApproximatelyZero(Cu)) 47 | { 48 | v = (OFxLV.y + LVxFW.y * u) / Cu; 49 | break; 50 | } 51 | } 52 | else 53 | { 54 | Cu = FVxLV.z + TWxLV.z * u; 55 | if (!MathHelpers.ApproximatelyZero(Cu)) 56 | { 57 | v = (OFxLV.z + LVxFW.z * u) / Cu; 58 | break; 59 | } 60 | } 61 | } 62 | 63 | if (MathHelpers.ApproximatelyZero(Cu)) 64 | return false; 65 | 66 | if (!MathHelpers.ApproximatelyZero(lineVector.x)) 67 | t = (-offsetVector.x + firstVector.x * v + (forwardVector.x + twistVector.x * v) * u) / lineVector.x; 68 | else if (!MathHelpers.ApproximatelyZero(lineVector.y)) 69 | t = (-offsetVector.y + firstVector.y * v + (forwardVector.y + twistVector.y * v) * u) / lineVector.y; 70 | else if (!MathHelpers.ApproximatelyZero(lineVector.z)) 71 | t = (-offsetVector.z + firstVector.z * v + (forwardVector.z + twistVector.z * v) * u) / lineVector.z; 72 | else 73 | return false; 74 | 75 | return true; 76 | } 77 | 78 | // return true if they interstect 79 | internal bool IntersectLineOnTwistedPlane(Vector3 lineStart, Vector3 lineEnd, bool bExtendPlaneBeyondFirstVector, bool bExtendPlaneBeyoneSecondVector, out Vector3 intersectionPoint, out Vector3 intersectForwardVector, out float distanceToSecondVector) 80 | { 81 | distanceToSecondVector = 0; 82 | intersectForwardVector = forwardVector; 83 | intersectionPoint = lineEnd; 84 | 85 | Vector3 lineVector = lineEnd - lineStart; 86 | Vector3 offsetVector = lineStart - firstOrigin; 87 | Vector3 TVxFW = Vector3.Cross(twistVector, forwardVector); 88 | Vector3 FVxFW = Vector3.Cross(firstVector, forwardVector); 89 | Vector3 OFxTW = Vector3.Cross(offsetVector, twistVector); 90 | Vector3 OFxFV = Vector3.Cross(offsetVector, firstVector); 91 | 92 | double quadraticA = lineVector.x * TVxFW.x + lineVector.y * TVxFW.y + lineVector.z * TVxFW.z; 93 | double quadraticB = lineVector.x * (FVxFW.x + OFxTW.x) + lineVector.y * (FVxFW.y + OFxTW.y) + lineVector.z * (FVxFW.z + OFxTW.z); 94 | double quadraticC = lineVector.x * OFxFV.x + lineVector.y * OFxFV.y + lineVector.z * OFxFV.z; 95 | if (MathHelpers.SolveQuadratic(quadraticA, quadraticB, quadraticC, out double u1, out double u2) == false) 96 | return false; 97 | 98 | double t1 = 0, t2 = 0; 99 | Vector3 FVxLV = Vector3.Cross(firstVector, lineVector); 100 | Vector3 TWxLV = Vector3.Cross(twistVector, lineVector); 101 | Vector3 OFxLV = Vector3.Cross(offsetVector, lineVector); 102 | Vector3 LVxFW = Vector3.Cross(lineVector, forwardVector); 103 | 104 | bool bIntersect1Found = false; 105 | if ((u1 < 4) && (u1 > -4) && (u1 >= 0 || bExtendPlaneBeyondFirstVector) && (u1 <= 1 || bExtendPlaneBeyoneSecondVector)) 106 | { 107 | bIntersect1Found = this.FindIntersectValues(lineVector, offsetVector, FVxLV, TWxLV, OFxLV, LVxFW, u1, out _, out t1); 108 | if (bIntersect1Found && t1 < 0 || t1 > 1) 109 | bIntersect1Found = false; 110 | 111 | } 112 | 113 | bool bIntersect2Found = false; 114 | if ((u2 < 4) && (u2 > -4) && (u2 >= 0 || bExtendPlaneBeyondFirstVector) && (u2 <= 1 || bExtendPlaneBeyoneSecondVector)) 115 | { 116 | bIntersect2Found = this.FindIntersectValues(lineVector, offsetVector, FVxLV, TWxLV, OFxLV, LVxFW, u2, out _, out t2); 117 | if (bIntersect2Found && t2 < 0 || t2 > 1) 118 | bIntersect2Found = false; 119 | } 120 | 121 | if (!bIntersect1Found && !bIntersect2Found) 122 | return false; 123 | 124 | float t = 0; 125 | if (bIntersect1Found) 126 | t = (float)t1; 127 | 128 | if (bIntersect2Found && (!bIntersect1Found || (bIntersect1Found && t2 < t1))) 129 | t = (float)t2; 130 | 131 | intersectionPoint = lineStart + lineVector * t; 132 | Vector3 intersectFirst = MathHelpers.CastSegmentToSegment(lineStart, lineVector, firstOrigin, firstVector); 133 | Vector3 intersectSecond = MathHelpers.CastSegmentToSegment(lineStart, lineVector, secondOrigin, secondVector); 134 | 135 | intersectForwardVector = Vector3.Normalize(intersectSecond - intersectFirst); 136 | distanceToSecondVector = Vector3.Distance(intersectSecond, intersectionPoint); 137 | 138 | return true; 139 | } 140 | 141 | internal Vector3 ConstrainLineToTwistedPlane(Vector3 lineStart, Vector3 lineEnd, float lineLength, ref bool bExtendPlaneBeyondStart, bool bExtendPlaneBeyondEnd, out bool bHitPointFound) 142 | { 143 | Vector3 newLineEnd; 144 | Vector3 lineVector = Vector3.Normalize(lineEnd - lineStart); 145 | bHitPointFound = false; 146 | 147 | // create a normal plane approximation to determine side, not the most accurate but close enough 148 | Vector3 planeRightVector = Vector3.Normalize(firstVector + secondVector); 149 | Vector3 planeUpVector = Vector3.Normalize(Vector3.Cross(planeRightVector, forwardVector)); 150 | Plane upPlane = new Plane(planeUpVector, firstOrigin + forwardVector / 2); 151 | bool bAbovePlane = upPlane.GetSide(lineEnd); 152 | if (!bAbovePlane) 153 | { 154 | bExtendPlaneBeyondStart = false; 155 | return lineEnd; 156 | } 157 | 158 | bool bIntersectFound = this.IntersectLineOnTwistedPlane(lineStart, lineEnd, bExtendPlaneBeyondStart, bExtendPlaneBeyondEnd, out Vector3 hitPoint, out Vector3 lineForwardVector, out float distanceToEdgeOfPlane); 159 | 160 | if (!bIntersectFound) 161 | { 162 | bExtendPlaneBeyondStart = false; 163 | return lineEnd; 164 | } 165 | 166 | double hitDistance = Vector3.Distance(hitPoint, lineStart); 167 | if (hitDistance > lineLength) 168 | { 169 | bExtendPlaneBeyondStart = false; 170 | return lineEnd; 171 | } 172 | 173 | double angleLineToPlane = (double)MathHelpers.DegToRad(Vector3.Angle(lineVector, -lineForwardVector)); 174 | MathHelpers.SolveSSATriangle(lineLength, hitDistance, angleLineToPlane, out double distanceAlongPlane, out _, out _); 175 | 176 | if (!bExtendPlaneBeyondEnd) 177 | { 178 | if (distanceAlongPlane > distanceToEdgeOfPlane) 179 | { 180 | newLineEnd = hitPoint + distanceToEdgeOfPlane * lineForwardVector; 181 | newLineEnd = lineStart + Vector3.Normalize(newLineEnd - lineStart) * lineLength; 182 | bExtendPlaneBeyondStart = true; 183 | return newLineEnd; 184 | } 185 | } 186 | 187 | newLineEnd = hitPoint + (float)distanceAlongPlane * lineForwardVector; 188 | bHitPointFound = true; 189 | bExtendPlaneBeyondStart = false; 190 | return newLineEnd; 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /Core_BetterPenetration/BoneNames.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Core_BetterPenetration 4 | { 5 | #if HS2 || AI 6 | static class LookTargets 7 | { 8 | internal const string HeadTarget = "k_f_head_00"; 9 | internal const string KokanTarget = "k_f_kokan_00"; 10 | internal const string BPKokanTarget = "cf_J_Vagina_root"; 11 | internal const string BPAnaTarget = "cf_J_Ana_Root"; 12 | internal const string AnaTarget = "k_f_ana_00"; 13 | internal const string InnerTarget = "cf_J_Kosi01"; 14 | internal const string InnerHeadTarget = "cf_J_FaceBase"; 15 | } 16 | 17 | static class BoneNames 18 | { 19 | 20 | internal const string KokanBone = "cf_J_Kokan"; 21 | internal const string TamaTop = "cm_J_dan_f_top"; 22 | internal const string BPBone = "cf_J_Vagina"; 23 | internal const string BellyBone = "cf_J_Belly"; 24 | internal const string BPDanBone = "cm_J_dan"; 25 | internal const string virtualBPDanBone = "cm_J_vdan"; 26 | internal const string BPDanEnd = "cm_J_dan119_00"; 27 | internal const string MouthPullBone = "cf_J_MouthMove"; 28 | internal const string ButtBoneL = "cf_J_Siri_s_L"; 29 | internal const string ButtBoneR = "cf_J_Siri_s_R"; 30 | 31 | internal const string BPKokanTarget = "cf_J_Vagina_root"; 32 | internal const string AnaTarget = "cf_J_Ana"; 33 | internal const string InnerTarget = "cf_J_Kosi01"; 34 | internal const string HeadTarget = "cf_J_MouthLow"; 35 | internal const string InnerHeadTarget = "cf_J_FaceBase"; 36 | internal const string BPDanEntryTarget = "k_f_dan_entry"; 37 | internal const string BPDanEndTarget = "k_f_dan_end"; 38 | 39 | internal static readonly List KokanPullBones = new List { "cf_J_Vagina_s_F", "cf_J_Vagina_s_F_L", "cf_J_Vagina_s_F_R", "cf_J_Vagina_s_M_F_L", "cf_J_Vagina_s_M_F_R", "cf_J_Vagina_s_M_L", "cf_J_Vagina_s_M_R", "cf_J_Vagina_s_M_B_L", "cf_J_Vagina_s_M_B_R", "cf_J_Vagina_s_B_L", "cf_J_Vagina_s_B_R", "cf_J_Vagina_s_B", 40 | "cf_J_Vagina_Outer_s_F", "cf_J_Vagina_Inner_s_F", "cf_J_Vagina_Inner_s_F_L", "cf_J_Vagina_Inner_s_F_R", "cf_J_Vagina_Inner_s_L", "cf_J_Vagina_Inner_s_R", "cf_J_Vagina_Inner_s_B_L", "cf_J_Vagina_Inner_s_B_R", "cf_J_Vagina_Inner_s_B"}; 41 | internal static readonly List KokanPullWeights = new List { 0.2f, 0.3f, 0.3f, 0.4f, 0.4f, 0.4f, 0.4f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 42 | 0.7f, 0.8f, 0.9f, 0.9f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}; 43 | 44 | /* internal static readonly List KokanPullBones = new List { "cf_J_Vagina_s_F", "cf_J_Vagina_s_F_L", "cf_J_Vagina_s_F_R", "cf_J_Vagina_s_M_F_L", "cf_J_Vagina_s_M_F_R", "cf_J_Vagina_s_M_L", "cf_J_Vagina_s_M_R", "cf_J_Vagina_s_M_B_L", "cf_J_Vagina_s_M_B_R", "cf_J_Vagina_s_B_L", "cf_J_Vagina_s_B_R", "cf_J_Vagina_s_B", 45 | "cf_J_Vagina_Outer_s_F", "cf_J_Vagina_Inner_s_F", "cf_J_Vagina_Inner_s_F_L", "cf_J_Vagina_Inner_s_F_R", "cf_J_Vagina_Inner_s_M_F_L", "cf_J_Vagina_Inner_s_M_F_R", "cf_J_Vagina_Inner_s_M_L", "cf_J_Vagina_Inner_s_M_R", "cf_J_Vagina_Inner_s_B_L", "cf_J_Vagina_Inner_s_M_B_R", "cf_J_Vagina_Inner_s_M_B_L", "cf_J_Vagina_Inner_s_B_R", "cf_J_Vagina_Inner_s_B"}; 46 | internal static readonly List KokanPullWeights = new List { 0.2f, 0.3f, 0.3f, 0.4f, 0.4f, 0.4f, 0.4f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 47 | 0.7f, 0.8f, 0.9f, 0.9f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}; 48 | */ 49 | internal static readonly List AnaPullBones = new List { "cf_J_Ana_s_F", "cf_J_Ana_s_B", "cf_J_Ana_s_L", "cf_J_Ana_s_R", "cf_J_Ana_s_F_L", "cf_J_Ana_s_F_R", "cf_J_Ana_s_B_L", "cf_J_Ana_s_B_R" }; 50 | internal static readonly List AnaPullWeights = new List { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; 51 | internal static readonly List DanBones = new List { "cm_J_dan101_00", "cm_J_dan102_00", "cm_J_dan103_00", "cm_J_dan104_00", "cm_J_dan105_00", "cm_J_dan106_00", "cm_J_dan107_00", "cm_J_dan108_00", "cm_J_dan109_00" }; 52 | internal static readonly List VirtualDanBones = new List { "cm_J_vdan102_00", "cm_J_vdan104_00", "cm_J_vdan106_00", "cm_J_vdan108_00"}; 53 | internal static readonly List TamaBones = new List { "cm_J_dan_Pivot_f_top", "cm_J_dan_Pivot_f_L", "cm_J_dan_Pivot_f_R"}; 54 | internal static readonly List FingerColliders = new List { "cf_J_Hand_Index02_R", "cf_J_Hand_Index03_R", "cf_J_Hand_Middle02_R", "cf_J_Hand_Middle03_R", "cf_J_Hand_Ring02_R", "cf_J_Hand_Ring03_R" }; 55 | internal static readonly List MidSectionColliders = new List { "cf_hit_Kosi02_s", "cf_hit_LegUp01_s_L", "cf_hit_LegUp01_s_R"}; 56 | internal static readonly List BodyColliders = new List { "cf_hit_Kosi02_s", "cf_hit_LegLow02_s_L", "cf_hit_LegLow02_s_R", "cf_hit_LegUp01_s_L", "cf_hit_LegUp01_s_R", 57 | "cf_J_Spine01_s", "cf_J_Spine02_s", "cf_J_Spine03_s", "cf_hit_Mune02_s_L", "cf_hit_Mune02_s_R", 58 | "ColFace01", "ColFace02", "cf_J_Neck_s", "cf_J_ArmUp02_s_L", "cf_J_ArmUp02_s_R" }; 59 | internal static readonly List frontCollisionList = new List { LookTargets.KokanTarget, "N_Waist_f"}; 60 | internal static readonly List backCollisionList = new List { LookTargets.AnaTarget, "N_Waist_b"}; 61 | internal static readonly List animationAdjustmentList = new List { "ais_f_00", "ais_f_01", "ais_f_12", "ais_f_19", "ais_f_20" }; 62 | internal static readonly List anaPullExceptionList = new List { "ais_f_13", "ais_f_31" }; 63 | 64 | internal static readonly List vibeBones = new List { "J_vibe_00", "J_vibe_01", "J_vibe_02", "J_vibe_03", "J_vibe_04", "J_vibe_05"}; 65 | internal static readonly List vibe2Bones = new List { "j_ai_hi_hitem02_02", "j_ai_hi_hitem02_03", "j_ai_hi_hitem02_04", "j_ai_hi_hitem02_05", "j_ai_hi_hitem02_06", "j_ai_hi_hitem02_07" }; 66 | internal static readonly List dildoBones = new List { "J_dildo_00", "J_dildo_01", "J_dildo_02", "J_dildo_03", "J_dildo_04", "J_dildo_05" }; 67 | internal static readonly List tentacleBones = new List { "j_S_kokan_01", "j_S_kokan_02", "j_S_kokan_03", "j_S_kokan_04", "j_S_kokan_05" }; 68 | internal static readonly List anaVibeBones = new List { "J_analvibe_ball_03", "J_analvibe_ball_04", "J_analvibe_ball_05", "J_analvibe_ball_06", "J_analvibe_ball_07", "J_analvibe_ball_08", "J_analvibe_ball_09", "J_analvibe_ball_10" }; 69 | internal static readonly List anaTentacleBones = new List { "j_S_ana_01", "j_S_ana_02", "j_S_ana_03", "j_S_ana_04", "j_S_ana_05" }; 70 | 71 | internal static readonly List vibeAnimationNames = new List { "ait_f_14", "aia_f_15", "aia_f_16", "aia_f_20" }; 72 | internal static readonly List vibe2AnimationNames = new List {"ait_f_13" }; 73 | internal static readonly List dildoAnimationNames = new List {"ait_f_02" }; 74 | internal static readonly List tentacleAnimationNames = new List {"h2t_f_09", "h2t_f_10" }; 75 | internal static readonly List anaVibeAnimationNames = new List { "ait_f_13", "ait_f_14", "aia_f_09", "aia_f_16" }; 76 | 77 | internal static readonly List maleFingerAnimationNames = new List { "aia_f_03", "aia_f_05", "aia_f_14", "aia_f_21", "h2a_f_00", "h2a_f_01", "h2_mf2_f1_02", "h2_mf2_f2_05" }; 78 | internal static readonly List femaleSelfFingerAnimationNames = new List { "aia_f_07", "ait_f_01", "ait_f_03", "ait_f_04", "ait_f_05", "ait_f_06", "ait_f_08", "ait_f_09"}; 79 | internal static readonly List lesbianFingerAnimationNames = new List { "ail_f1_01", "ail_f2_01" }; 80 | } 81 | #endif 82 | 83 | #if KK || KKS 84 | static class LookTargets 85 | { 86 | internal const string HeadTarget = "k_f_head_00"; 87 | internal const string KokanTarget = "k_f_kokan_00"; 88 | internal const string BPKokanTarget = "cf_J_Vagina_root"; 89 | internal const string BPAnaTarget = "cf_J_Ana_Root"; 90 | internal const string AnaTarget = "k_f_ana_00"; 91 | internal const string InnerTarget = "cf_j_waist01"; 92 | internal const string InnerHeadTarget = "cf_J_FaceBase"; 93 | } 94 | 95 | static class BoneNames 96 | { 97 | internal const string KokanBone = "cf_j_kokan"; 98 | internal const string TamaTop = "cm_J_dan_f_top"; 99 | internal const string BPBone = "cf_J_Vagina"; 100 | internal const string BellyBone = "cf_J_Belly"; 101 | internal const string BPDanBone = "cm_J_dan"; 102 | internal const string virtualBPDanBone = "cm_J_vdan"; 103 | internal const string BPDanEnd = "cm_J_dan119_00"; 104 | internal const string MouthPullBone = "cf_J_MouthMove"; 105 | internal const string ButtBoneL = "cf_J_Siri_s_L"; 106 | internal const string ButtBoneR = "cf_J_Siri_s_R"; 107 | 108 | internal const string BPKokanTarget = "cf_J_Vagina_root"; 109 | internal const string AnaTarget = "cf_j_ana"; 110 | internal const string InnerTarget = "cf_j_waist01"; 111 | internal const string HeadTarget = "cf_J_MouthLow"; 112 | internal const string InnerHeadTarget = "cf_J_FaceBase"; 113 | internal const string BPDanEntryTarget = "k_f_dan_entry"; 114 | internal const string BPDanEndTarget = "k_f_dan_end"; 115 | 116 | internal static readonly List KokanPullBones = new List { "cf_J_Vagina_L.011", "cf_J_Vagina_R.011", "cf_J_Vagina_L.012", "cf_J_Vagina_R.012", "cf_J_Vagina_L.013", "cf_J_Vagina_R.013", 117 | "cf_J_Vagina_L.014", "cf_J_Vagina_L.015", "cf_J_Vagina_R.014", "cf_J_Vagina_R.015", "cf_J_Vagina_B.010" }; 118 | internal static readonly List KokanPullWeights = new List { 0.1f, 0.25f, 0.25f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 119 | 0.4f, 0.7f, 0.7f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f}; 120 | 121 | internal static readonly List AnaPullBones = new List { "cf_J_Ana_s_F", "cf_J_Ana_s_B", "cf_J_Ana_s_L", "cf_J_Ana_s_R", "cf_J_Ana_s_F_L", "cf_J_Ana_s_F_R", "cf_J_Ana_s_B_L", "cf_J_Ana_s_B_R" }; 122 | internal static readonly List AnaPullWeights = new List { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f }; 123 | internal static readonly List AnaBones = new List { "cf_J_Ana_Pivot", "cf_J_Vagina_Pivot_B", "cf_J_Vagina_Pivot_L.005", "cf_J_Vagina_Pivot_R.005" }; 124 | internal static readonly List DanBones = new List { "cm_J_dan101_00", "cm_J_dan102_00", "cm_J_dan103_00", "cm_J_dan104_00", "cm_J_dan105_00", "cm_J_dan106_00", "cm_J_dan107_00", "cm_J_dan108_00", "cm_J_dan109_00" }; 125 | internal static readonly List VirtualDanBones = new List { "cm_J_vdan102_00", "cm_J_vdan104_00", "cm_J_vdan106_00", "cm_J_vdan108_00"}; 126 | internal static readonly List TamaBones = new List { "cm_J_dan_Pivot_f_L", "cm_J_dan_Pivot_f_R" }; 127 | internal static readonly List FingerColliders = new List { "cf_j_index02_R", "cf_j_index03_R", "cf_j_middle02_R", "cf_j_middle03_R", "cf_j_ring02_R", "cf_j_ring03_R" }; 128 | internal static readonly List MidSectionColliders = new List { "cm_J_dan101_00", "cm_J_dan103_00", "cf_hit_waist02", "cf_hit_LegLow02_s_L", "cf_hit_LegLow02_s_R" }; 129 | internal static readonly List BodyColliders = new List { "cf_hit_thigh01_L", "cf_hit_thigh01_R", "cf_hit_thigh02_L", "cf_hit_thigh02_R", 130 | "cf_hit_waist_L", "cf_hit_berry", "cf_hit_waist01", "cf_hit_waist02", 131 | "cf_hit_spine01", "cf_hit_spine02", "cf_hit_spine03", "cf_hit_spine03_2", 132 | "cf_hit_bust00", "cf_hit_bust02_L", "cf_hit_bust02_R", 133 | "cf_hit_neck", "cf_hit_head", 134 | "cf_hit_arm_L", "cf_hit_arm_R", "cf_hit_shoulder_L", "cf_hit_shoulder_R" }; 135 | 136 | internal static readonly List frontCollisionList = new List { LookTargets.KokanTarget, "a_n_waist_f", "a_n_bust_f" }; 137 | internal static readonly List backCollisionList = new List { LookTargets.AnaTarget, "a_n_waist_b", "a_n_back" }; 138 | internal static readonly List frontAnaCollisionList = new List { LookTargets.KokanTarget, "a_n_waist_f", "a_n_bust_f" }; 139 | internal static readonly List animationAdjustmentList = new List { "ais_f_00", "ais_f_01", "ais_f_12", "ais_f_19", "ais_f_20" }; 140 | internal static readonly List anaPullExceptionList = new List { "ais_f_13", "ais_f_31" }; 141 | 142 | internal static readonly List maleFingerAnimationNames = new List { "aia_f_03", "aia_f_05", "aia_f_14", "aia_f_21", "h2a_f_00", "h2_mf2_f1_02", "h2_mf2_f2_05" }; 143 | internal static readonly List femaleSelfFingerAnimationNames = new List { "ait_f_01", "ait_f_03", "ait_f_04", "ait_f_05", "ait_f_06", "ait_f_08", "ait_f_09"}; 144 | internal static readonly List lesbianFingerAnimationNames = new List { "ail_f1_01", "ail_f2_01" }; 145 | } 146 | #endif 147 | } 148 | -------------------------------------------------------------------------------- /Core_BetterPenetration/Studio_BetterPenetration.cs: -------------------------------------------------------------------------------- 1 | #if Studio 2 | using BepInEx; 3 | using BepInEx.Bootstrap; 4 | using HarmonyLib; 5 | using UnityEngine; 6 | using KKAPI.Studio; 7 | using KKAPI.Studio.UI; 8 | using KKAPI.Chara; 9 | using UniRx; 10 | using System; 11 | using System.Linq; 12 | using System.Reflection; 13 | #if AI || HS2 14 | using AIChara; 15 | #endif 16 | 17 | namespace Core_BetterPenetration 18 | { 19 | [BepInPlugin(GUID, PluginName, VERSION)] 20 | [BepInDependency("com.deathweasel.bepinex.uncensorselector", "3.10")] 21 | [BepInDependency("com.rclcircuit.bepinex.modboneimplantor", "1.1.1")] 22 | [BepInDependency("com.joan6694.illusionplugins.nodesconstraints")] 23 | [BepInProcess(KKAPI.KoikatuAPI.StudioProcessName)] 24 | public class Studio_BetterPenetration : BaseUnityPlugin 25 | { 26 | public const string GUID = "com.animal42069.studiobetterpenetration"; 27 | internal const string PluginName = "Studio Better Penetration"; 28 | public const string VERSION = Constants.PluginVersion; 29 | internal const string BEHAVIOR = "BetterPenetrationController"; 30 | internal const string StudioCategoryName = "Better Penetration"; 31 | internal static Harmony harmony; 32 | internal static BaseUnityPlugin nodeConstraintPlugin; 33 | internal static bool reloadConstraints = false; 34 | internal static int updateCount = 0; 35 | internal static int resetDelay = 0; 36 | 37 | internal void Main() 38 | { 39 | CharacterApi.RegisterExtraBehaviour(BEHAVIOR); 40 | 41 | harmony = new Harmony("Studio_BetterPenetration"); 42 | harmony.PatchAll(GetType()); 43 | 44 | Chainloader.PluginInfos.TryGetValue("com.deathweasel.bepinex.uncensorselector", out PluginInfo pluginInfo); 45 | if (pluginInfo == null || pluginInfo.Instance == null) 46 | return; 47 | 48 | Type nestedType = pluginInfo.Instance.GetType().GetNestedType("UncensorSelectorController", AccessTools.all); 49 | if (nestedType == null) 50 | return; 51 | 52 | MethodInfo methodInfo = AccessTools.Method(nestedType, "ReloadCharacterBody", null, null); 53 | if (methodInfo == null) 54 | return; 55 | 56 | harmony.Patch(methodInfo, prefix: new HarmonyMethod(GetType(), "BeforeCharacterReload")); 57 | UnityEngine.Debug.Log("Studio_BetterPenetration: patched UncensorSelector::ReloadCharacterBody correctly"); 58 | 59 | methodInfo = AccessTools.Method(nestedType, "ReloadCharacterBalls", null, null); 60 | if (methodInfo == null) 61 | return; 62 | 63 | harmony.Patch(methodInfo, postfix: new HarmonyMethod(GetType(), "AfterTamaCharacterReload")); 64 | UnityEngine.Debug.Log("Studio_BetterPenetration: patched UncensorSelectorController::ReloadCharacterBalls correctly"); 65 | 66 | Chainloader.PluginInfos.TryGetValue("com.joan6694.illusionplugins.nodesconstraints", out pluginInfo); 67 | if (pluginInfo == null || pluginInfo.Instance == null) 68 | return; 69 | 70 | nodeConstraintPlugin = pluginInfo.Instance; 71 | Type nodeConstraintType = nodeConstraintPlugin.GetType(); 72 | if (nodeConstraintType == null) 73 | return; 74 | 75 | // Find the most specific AddConstraint method since it's the one that always runs 76 | methodInfo = AccessTools.GetDeclaredMethods(nodeConstraintType).Where(x => x.Name == "AddConstraint").OrderByDescending(x => x.GetParameters().Length).FirstOrDefault(); 77 | if (methodInfo == null) 78 | return; 79 | 80 | harmony.Patch(methodInfo, postfix: new HarmonyMethod(GetType(), nameof(AfterAddConstraint))); 81 | UnityEngine.Debug.Log("Studio_BetterPenetration: patched NodeConstraints::AddConstraint correctly"); 82 | 83 | methodInfo = AccessTools.Method(nodeConstraintType, "ApplyNodesConstraints", null, null); 84 | if (methodInfo == null) 85 | return; 86 | 87 | harmony.Patch(methodInfo, postfix: new HarmonyMethod(GetType(), nameof(AfterApplyNodesConstraints))); 88 | UnityEngine.Debug.Log("Studio_BetterPenetration: patched NodeConstraints::ApplyNodesConstraints correctly"); 89 | 90 | methodInfo = AccessTools.Method(nodeConstraintType, "ApplyConstraints", null, null); 91 | if (methodInfo == null) 92 | return; 93 | 94 | harmony.Patch(methodInfo, postfix: new HarmonyMethod(GetType(), nameof(AfterApplyConstraints))); 95 | UnityEngine.Debug.Log("Studio_BetterPenetration: patched NodeConstraints::ApplyConstraints correctly"); 96 | 97 | RegisterStudioControllerBasic(); 98 | } 99 | 100 | public static void RegisterStudioControllerBasic() 101 | { 102 | if (!StudioAPI.InsideStudio) 103 | return; 104 | 105 | var bpEnable = new CurrentStateCategorySwitch("Enable BP Controller", c => StudioAPI.GetSelectedControllers().First().enabled); 106 | bpEnable.Value.Subscribe(value => 107 | { 108 | foreach (var controller in StudioAPI.GetSelectedControllers()) 109 | { 110 | if (value == false) 111 | { 112 | controller.ClearDanAgent(); 113 | controller.enabled = false; 114 | } 115 | else 116 | { 117 | controller.enabled = true; 118 | controller.InitializeDanAgent(); 119 | controller.AddDanConstraints(nodeConstraintPlugin); 120 | } 121 | } 122 | }); 123 | StudioAPI.GetOrCreateCurrentStateCategory(StudioCategoryName).AddControl(bpEnable); 124 | 125 | var colliderRadiusScale = new CurrentStateCategorySlider("Collilder Radius Scale", c => StudioAPI.GetSelectedControllers().First().DanColliderRadiusScale, 0.5f, 1.5f); 126 | colliderRadiusScale.Value.Subscribe(value => 127 | { 128 | foreach (var controller in StudioAPI.GetSelectedControllers()) 129 | controller.DanColliderRadiusScale = value; 130 | }); 131 | StudioAPI.GetOrCreateCurrentStateCategory(StudioCategoryName).AddControl(colliderRadiusScale); 132 | 133 | var colliderLengthScale = new CurrentStateCategorySlider("Collilder Length Scale", c => StudioAPI.GetSelectedControllers().First().DanColliderLengthScale, 0.5f, 1.5f); 134 | colliderLengthScale.Value.Subscribe(value => 135 | { 136 | foreach (var controller in StudioAPI.GetSelectedControllers()) 137 | controller.DanColliderLengthScale = value; 138 | }); 139 | StudioAPI.GetOrCreateCurrentStateCategory(StudioCategoryName).AddControl(colliderLengthScale); 140 | 141 | var lengthSlider = new CurrentStateCategorySlider("Length Squish", c => StudioAPI.GetSelectedControllers().First().DanLengthSquish, 0f, 1f); 142 | lengthSlider.Value.Subscribe(value => 143 | { 144 | foreach (var controller in StudioAPI.GetSelectedControllers()) 145 | controller.DanLengthSquish = value; 146 | }); 147 | StudioAPI.GetOrCreateCurrentStateCategory(StudioCategoryName).AddControl(lengthSlider); 148 | 149 | var girthSlider = new CurrentStateCategorySlider("Girth Squish", c => StudioAPI.GetSelectedControllers().First().DanGirthSquish, 0f, 2f); 150 | girthSlider.Value.Subscribe(value => 151 | { 152 | foreach (var controller in StudioAPI.GetSelectedControllers()) 153 | controller.DanGirthSquish = value; 154 | }); 155 | StudioAPI.GetOrCreateCurrentStateCategory(StudioCategoryName).AddControl(girthSlider); 156 | 157 | var thresholdSlider = new CurrentStateCategorySlider("Squish Threshold", c => StudioAPI.GetSelectedControllers().First().DanSquishThreshold, 0f, 1f); 158 | thresholdSlider.Value.Subscribe(value => 159 | { 160 | foreach (var controller in StudioAPI.GetSelectedControllers()) 161 | controller.DanSquishThreshold = value; 162 | }); 163 | StudioAPI.GetOrCreateCurrentStateCategory(StudioCategoryName).AddControl(thresholdSlider); 164 | 165 | var autoTargeter = new CurrentStateCategoryDropdown("Auto-Target", new string[] { "Off", "Vaginal", "Anal", "Oral" }, c => StudioAPI.GetSelectedControllers().First().DanAutoTarget); 166 | autoTargeter.Value.Subscribe(value => 167 | { 168 | foreach (var controller in StudioAPI.GetSelectedControllers()) 169 | controller.DanAutoTarget = value; 170 | }); 171 | StudioAPI.GetOrCreateCurrentStateCategory(StudioCategoryName).AddControl(autoTargeter); 172 | 173 | var maxPush = new CurrentStateCategorySlider("Max Push", c => StudioAPI.GetSelectedControllers().First().MaxPush, 0f, 0.3f); 174 | maxPush.Value.Subscribe(value => 175 | { 176 | foreach (var controller in StudioAPI.GetSelectedControllers()) 177 | controller.MaxPush = value; 178 | }); 179 | StudioAPI.GetOrCreateCurrentStateCategory(StudioCategoryName).AddControl(maxPush); 180 | 181 | var maxPull = new CurrentStateCategorySlider("Max Pull", c => StudioAPI.GetSelectedControllers().First().MaxPull, 0f, 0.3f); 182 | maxPull.Value.Subscribe(value => 183 | { 184 | foreach (var controller in StudioAPI.GetSelectedControllers()) 185 | controller.MaxPull = value; 186 | }); 187 | StudioAPI.GetOrCreateCurrentStateCategory(StudioCategoryName).AddControl(maxPull); 188 | 189 | var pullRate = new CurrentStateCategorySlider("Pull Rate", c => StudioAPI.GetSelectedControllers().First().PullRate, 0f, 50f); 190 | pullRate.Value.Subscribe(value => 191 | { 192 | foreach (var controller in StudioAPI.GetSelectedControllers()) 193 | controller.PullRate = value; 194 | }); 195 | StudioAPI.GetOrCreateCurrentStateCategory(StudioCategoryName).AddControl(pullRate); 196 | 197 | var returnRate = new CurrentStateCategorySlider("Return Rate", c => StudioAPI.GetSelectedControllers().First().ReturnRate, 0f, 1f); 198 | returnRate.Value.Subscribe(value => 199 | { 200 | foreach (var controller in StudioAPI.GetSelectedControllers()) 201 | controller.ReturnRate = value; 202 | }); 203 | StudioAPI.GetOrCreateCurrentStateCategory(StudioCategoryName).AddControl(returnRate); 204 | 205 | #if AI || HS2 206 | var bellyBulgeEnable = new CurrentStateCategorySwitch("Enable Belly Bulge", c => StudioAPI.GetSelectedControllers().First().EnableBellyBulge); 207 | bellyBulgeEnable.Value.Subscribe(value => 208 | { 209 | foreach (var controller in StudioAPI.GetSelectedControllers()) 210 | controller.EnableBellyBulge = value; 211 | }); 212 | StudioAPI.GetOrCreateCurrentStateCategory(StudioCategoryName).AddControl(bellyBulgeEnable); 213 | 214 | var bellyBulgeScale = new CurrentStateCategorySlider("Belly Bulge Scale", c => StudioAPI.GetSelectedControllers().First().BellyBulgeScale, 0.0f, 3.0f); 215 | bellyBulgeScale.Value.Subscribe(value => 216 | { 217 | foreach (var controller in StudioAPI.GetSelectedControllers()) 218 | controller.BellyBulgeScale = value; 219 | }); 220 | StudioAPI.GetOrCreateCurrentStateCategory(StudioCategoryName).AddControl(bellyBulgeScale); 221 | #endif 222 | } 223 | 224 | public static void RegisterStudioControls() 225 | { 226 | if (!StudioAPI.InsideStudio) 227 | return; 228 | } 229 | 230 | [HarmonyPostfix, HarmonyPatch(typeof(ChaControl), "UpdateAccessoryMoveFromInfo")] 231 | internal static void ChaControl_UpdateAccessoryMoveFromInfo(ChaControl __instance) 232 | { 233 | Tools.RemoveCollidersFromCoordinate(__instance); 234 | } 235 | 236 | [HarmonyPostfix, HarmonyPatch(typeof(ChaControl), "UpdateSiru")] 237 | internal static void ChaControl_UpdateSiru(ChaControl __instance, bool forceChange) 238 | { 239 | 240 | if (!forceChange) 241 | return; 242 | 243 | Tools.RemoveCollidersFromCoordinate(__instance); 244 | } 245 | 246 | internal static void BeforeCharacterReload() 247 | { 248 | foreach (var controller in BetterPenetrationController.controllers) 249 | { 250 | if (controller == null) 251 | continue; 252 | 253 | controller.ClearDanAgent(); 254 | } 255 | } 256 | 257 | internal static void AfterTamaCharacterReload() 258 | { 259 | reloadConstraints = true; 260 | } 261 | 262 | internal static void AfterAddConstraint(bool enabled, Transform parentTransform, Transform childTransform, 263 | bool linkPosition, Vector3 positionOffset, bool linkRotation, Quaternion rotationOffset, bool linkScale, 264 | Vector3 scaleOffset, string alias) 265 | { 266 | if (childTransform.name != BoneNames.BPDanEntryTarget && childTransform.name != BoneNames.BPDanEndTarget) 267 | return; 268 | 269 | var controller = childTransform.GetComponentInParent(); 270 | if (controller == null) 271 | return; 272 | 273 | var constrainParams = new object[] { enabled, parentTransform.name, childTransform, linkPosition, positionOffset, 274 | linkRotation, rotationOffset, linkScale, scaleOffset, alias}; 275 | 276 | controller.SaveConstraintParams(childTransform.name == BoneNames.BPDanEntryTarget, constrainParams); 277 | 278 | if (childTransform.name != BoneNames.BPDanEntryTarget) 279 | return; 280 | 281 | var targetChaControl = parentTransform.GetComponentInParent(); 282 | if (targetChaControl == null) 283 | return; 284 | 285 | controller.SetCollisionAgent(targetChaControl, parentTransform.name == BoneNames.BPKokanTarget, parentTransform.name == BoneNames.AnaTarget, parentTransform.name == BoneNames.HeadTarget); 286 | } 287 | 288 | internal static void AfterApplyConstraints() 289 | { 290 | if (!reloadConstraints) 291 | return; 292 | 293 | resetDelay = 60; 294 | reloadConstraints = false; 295 | } 296 | 297 | internal static void AfterApplyNodesConstraints() 298 | { 299 | if (!reloadConstraints) 300 | return; 301 | 302 | resetDelay = 60; 303 | reloadConstraints = false; 304 | } 305 | 306 | internal static void ReinitializeControllers() 307 | { 308 | if (nodeConstraintPlugin == null) 309 | return; 310 | 311 | foreach (var controller in BetterPenetrationController.controllers) 312 | { 313 | if (controller == null) 314 | continue; 315 | 316 | controller.InitializeDanAgent(); 317 | controller.AddDanConstraints(nodeConstraintPlugin); 318 | } 319 | } 320 | 321 | internal void Update() 322 | { 323 | if (nodeConstraintPlugin == null) 324 | return; 325 | 326 | if (resetDelay > 0 && --resetDelay <= 0) 327 | ReinitializeControllers(); 328 | 329 | if (++updateCount < 60) 330 | return; 331 | 332 | updateCount = 0; 333 | 334 | foreach (var controller in BetterPenetrationController.controllers) 335 | { 336 | if (controller == null) 337 | continue; 338 | 339 | controller.CheckAutoTarget(nodeConstraintPlugin); 340 | } 341 | } 342 | } 343 | } 344 | #endif -------------------------------------------------------------------------------- /Core_BetterPenetration/CoreGame.cs: -------------------------------------------------------------------------------- 1 | #if !Studio 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using System; 5 | using System.Linq; 6 | 7 | #if HS2 || AI 8 | using AIChara; 9 | #endif 10 | 11 | namespace Core_BetterPenetration 12 | { 13 | class CoreGame 14 | { 15 | internal static List danAgents; 16 | internal static List collisionAgents; 17 | internal static List danHasNewTarget; 18 | #if HS2 || AI 19 | internal static List itemColliderInfo; 20 | internal static List anaItemColliderInfo; 21 | internal static List m_itemColliders = new List(); 22 | internal static List m_anaItemColliders = new List(); 23 | #endif 24 | 25 | public static void InitializeAgents(List danCharacterList, List collisionCharacterList, List danOptions, List collisionOptions) 26 | { 27 | InitializeDanAgents(danCharacterList, danOptions); 28 | InitializeCollisionAgents(collisionCharacterList, collisionOptions); 29 | #if HS2 || AI 30 | InitializeItemColliderInfo(); 31 | #endif 32 | } 33 | 34 | public static void InitializeDanAgents(List danCharacterList, List danOptions) 35 | { 36 | danAgents = new List(); 37 | danHasNewTarget = new List(); 38 | 39 | int characterNum = 0; 40 | foreach (var character in danCharacterList) 41 | { 42 | if (character == null) 43 | continue; 44 | 45 | danAgents.Add(new DanAgent(character, danOptions[characterNum])); 46 | danHasNewTarget.Add(false); 47 | characterNum++; 48 | } 49 | } 50 | 51 | public static void ClearDanAgents() 52 | { 53 | if (danAgents == null) 54 | return; 55 | 56 | foreach (var agent in danAgents) 57 | agent.ClearDanAgent(); 58 | } 59 | 60 | internal static void ClearCollisionAgents() 61 | { 62 | if (collisionAgents == null) 63 | return; 64 | 65 | foreach (var collisionAgent in collisionAgents) 66 | collisionAgent.ClearColliders(); 67 | } 68 | 69 | public static void InitializeCollisionAgents(List collisionCharacterList, List collisionOptions) 70 | { 71 | collisionAgents = new List(); 72 | 73 | int characterNum = 0; 74 | foreach (var character in collisionCharacterList) 75 | { 76 | if (character == null) 77 | continue; 78 | 79 | collisionAgents.Add(new CollisionAgent(character, collisionOptions[characterNum++])); 80 | } 81 | } 82 | 83 | public static void LookAtDanUpdate(Transform lookAtTransform, string currentMotion, bool topStick, bool changingAnimation, int maleNum, int femaleNum, bool twoDans, bool isInScene) 84 | { 85 | if (maleNum >= danAgents.Count || femaleNum >= collisionAgents.Count) 86 | return; 87 | 88 | if (!changingAnimation) 89 | { 90 | collisionAgents[femaleNum].AdjustMissionaryAnimation(); 91 | 92 | if (topStick && lookAtTransform != null && (lookAtTransform.name == LookTargets.AnaTarget || lookAtTransform.name == LookTargets.BPAnaTarget)) 93 | collisionAgents[femaleNum].AdjustAnalAnimation(); 94 | } 95 | 96 | if (danHasNewTarget[maleNum] && !changingAnimation) 97 | LookAtDanSetup(lookAtTransform, currentMotion, topStick, maleNum, femaleNum, twoDans, isInScene); 98 | 99 | danAgents[maleNum].SetDanTarget(collisionAgents[femaleNum], twoDans); 100 | } 101 | 102 | public static void LookAtDanSetup(Transform lookAtTransform, string currentMotion, bool topStick, int maleNum, int femaleNum, bool twoDans, bool isInScene) 103 | { 104 | if (maleNum >= danAgents.Count || femaleNum >= collisionAgents.Count) 105 | return; 106 | 107 | if (!twoDans && danAgents.Count > 1 && danAgents[1] != null) 108 | { 109 | danAgents[1].RemoveDanColliders(collisionAgents[femaleNum]); 110 | #if HS2 || AI 111 | danAgents[1].RemoveMidsectionColliders(collisionAgents[femaleNum].m_collisionCharacter); 112 | danAgents[1].RemoveDanCollidersFromDB2(collisionAgents[femaleNum].m_collisionCharacter); 113 | #endif 114 | } 115 | 116 | if (maleNum == 1 && !twoDans) 117 | return; 118 | 119 | CollisionAgent firstAgent = collisionAgents[femaleNum]; 120 | CollisionAgent secondAgent = null; 121 | 122 | var secondFemaleNum = femaleNum == 0 ? 1 : 0; 123 | if (collisionAgents.Count > secondFemaleNum && collisionAgents[secondFemaleNum].m_collisionCharacter.visibleAll && collisionAgents[secondFemaleNum].m_collisionCharacter.objTop != null) 124 | secondAgent = collisionAgents[secondFemaleNum]; 125 | 126 | danAgents[maleNum].SetupNewDanTarget(lookAtTransform, currentMotion, topStick, isInScene, firstAgent, secondAgent, twoDans); 127 | danHasNewTarget[maleNum] = false; 128 | } 129 | 130 | public static void ClearKokanBones() 131 | { 132 | if (collisionAgents == null || collisionAgents.Count == 0) 133 | return; 134 | 135 | foreach (var agent in collisionAgents) 136 | { 137 | if (agent == null) 138 | continue; 139 | 140 | agent.ClearKokanDynamicBones(); 141 | } 142 | } 143 | 144 | public static void LookAtDanRelease(int maleNum, int femaleNum, bool twoDans) 145 | { 146 | if (maleNum >= danAgents.Count || femaleNum >= collisionAgents.Count) 147 | return; 148 | 149 | if (!twoDans && danAgents.Count > 1 && danAgents[1] != null) 150 | { 151 | danAgents[1].RemoveDanColliders(collisionAgents[femaleNum]); 152 | #if HS2 || AI 153 | danAgents[1].RemoveMidsectionColliders(collisionAgents[femaleNum].m_collisionCharacter); 154 | danAgents[1].RemoveDanCollidersFromDB2(collisionAgents[femaleNum].m_collisionCharacter); 155 | #endif 156 | } 157 | 158 | if (maleNum == 1 && !twoDans) 159 | return; 160 | 161 | if (collisionAgents.Count > 1 && collisionAgents[1].m_collisionCharacter.visibleAll && collisionAgents[1].m_collisionCharacter.objTop != null) 162 | { 163 | var secondTarget = 1 - femaleNum; 164 | if (secondTarget < 0) 165 | secondTarget = 0; 166 | 167 | danAgents[maleNum].ClearDanTarget(collisionAgents[femaleNum], collisionAgents[secondTarget]); 168 | } 169 | else 170 | { 171 | danAgents[maleNum].ClearDanTarget(collisionAgents[femaleNum]); 172 | } 173 | } 174 | 175 | public static void OnChangeAnimation(string newAnimationFile) 176 | { 177 | foreach (var socketAgent in collisionAgents) 178 | socketAgent.adjustFAnimation = false; 179 | 180 | SetDansHaveNewTarget(true); 181 | 182 | if (collisionAgents == null || collisionAgents[0] == null) 183 | return; 184 | 185 | collisionAgents[0].CheckForAdjustment(newAnimationFile); 186 | } 187 | 188 | public static void ResetParticles() 189 | { 190 | foreach (var agent in danAgents) 191 | agent.ResetParticles(); 192 | 193 | foreach (var agent in collisionAgents) 194 | agent.ResetParticles(); 195 | } 196 | 197 | public static void EnableParticles(bool enable) 198 | { 199 | foreach (var agent in collisionAgents) 200 | agent.EnableParticles(enable); 201 | } 202 | 203 | public static void SetDansHaveNewTarget(bool set) 204 | { 205 | for (int index = 0; index < danHasNewTarget.Count; index++) 206 | danHasNewTarget[index] = set; 207 | } 208 | 209 | public static void UpdateDanCollider(int maleNum, float danRadiusScale, float danLengthScale) 210 | { 211 | if (maleNum >= danAgents.Count || danAgents[maleNum] == null) 212 | return; 213 | 214 | danAgents[maleNum].UpdateDanColliders(danRadiusScale, danLengthScale); 215 | } 216 | 217 | public static void UpdateDanOptions(int maleNum, float danLengthSquish, float danGirthSquish, float squishThreshold, bool squishOralGirth, bool simplifyVaginal, bool simplifyOral, bool rotateTamaWithShaft, bool limitCorrection, float maxCorrection) 218 | { 219 | if (maleNum >= danAgents.Count || danAgents[maleNum] == null) 220 | return; 221 | 222 | danAgents[maleNum].UpdateDanOptions(danLengthSquish, danGirthSquish, squishThreshold, squishOralGirth, simplifyVaginal, simplifyOral, rotateTamaWithShaft, limitCorrection, maxCorrection); 223 | } 224 | 225 | public static void UpdateCollisionOptions(int femaleNum, CollisionOptions options) 226 | { 227 | if (femaleNum >= collisionAgents.Count || collisionAgents[femaleNum] == null) 228 | return; 229 | 230 | collisionAgents[femaleNum].UpdateCollisionOptions(options); 231 | } 232 | 233 | public static void OnEndScene() 234 | { 235 | ClearDanAgents(); 236 | ClearCollisionAgents(); 237 | danAgents = null; 238 | collisionAgents = null; 239 | danHasNewTarget = null; 240 | } 241 | 242 | internal static void SetAgentsBPBoneWeights(float weight) 243 | { 244 | SetDanAgentsBPBoneWeights(weight); 245 | SetCollisionAgentsBPBoneWeights(weight); 246 | } 247 | 248 | internal static void SetDanAgentsBPBoneWeights(float weight) 249 | { 250 | if (danAgents == null) 251 | return; 252 | 253 | foreach (var agent in danAgents) 254 | { 255 | if (agent?.m_danCharacter == null) 256 | continue; 257 | 258 | SetBPBoneWeights(agent.m_danCharacter, weight); 259 | } 260 | } 261 | 262 | internal static void SetCollisionAgentsBPBoneWeights(float weight) 263 | { 264 | if (collisionAgents == null) 265 | return; 266 | 267 | foreach (var agent in collisionAgents) 268 | { 269 | if (agent?.m_collisionCharacter == null) 270 | continue; 271 | 272 | SetBPBoneWeights(agent.m_collisionCharacter, weight); 273 | } 274 | } 275 | 276 | internal static void SetBPBoneWeights(ChaControl character, float weight) 277 | { 278 | var dynamicBones = character.GetComponentsInChildren(true); 279 | 280 | if (dynamicBones == null) 281 | return; 282 | 283 | foreach (var dynamicBone in dynamicBones) 284 | { 285 | if (dynamicBone == null || 286 | dynamicBone.m_Root == null || 287 | dynamicBone.name == null || 288 | character != dynamicBone.GetComponentInParent()) 289 | continue; 290 | 291 | if (dynamicBone.name.Contains(BoneNames.BPBone) || dynamicBone.name.Contains(BoneNames.BellyBone)) 292 | dynamicBone.SetWeight(weight); 293 | } 294 | } 295 | 296 | internal static void SetupFingerColliders(string animation) 297 | { 298 | DanAgent danAgent = null; 299 | 300 | if (danAgents != null && danAgents.Count > 0) 301 | danAgent = danAgents[0]; 302 | 303 | CollisionAgent firstAgent = collisionAgents[0]; 304 | CollisionAgent secondAgent = null; 305 | if (collisionAgents.Count > 1 && collisionAgents[1].m_collisionCharacter.visibleAll && collisionAgents[1].m_collisionCharacter.objTop != null) 306 | secondAgent = collisionAgents[1]; 307 | 308 | ClearFingerColliders(danAgent, firstAgent, secondAgent); 309 | AddFingerColliders(animation, danAgent, firstAgent, secondAgent); 310 | } 311 | 312 | internal static void ClearFingerColliders() 313 | { 314 | DanAgent danAgent = null; 315 | 316 | if (danAgents != null && danAgents.Count > 0) 317 | danAgent = danAgents[0]; 318 | 319 | CollisionAgent firstAgent = collisionAgents[0]; 320 | CollisionAgent secondAgent = null; 321 | if (collisionAgents.Count > 1) 322 | secondAgent = collisionAgents[1]; 323 | 324 | ClearFingerColliders(danAgent, firstAgent, secondAgent); 325 | } 326 | 327 | internal static void ClearFingerColliders(DanAgent danAgent, CollisionAgent firstAgent, CollisionAgent secondAgent = null) 328 | { 329 | if (firstAgent == null) 330 | return; 331 | 332 | firstAgent.RemoveFingerColliders(firstAgent); 333 | 334 | if (danAgent != null) 335 | danAgent.RemoveFingerColliders(firstAgent); 336 | 337 | if (secondAgent == null) 338 | return; 339 | 340 | firstAgent.RemoveFingerColliders(secondAgent); 341 | secondAgent.RemoveFingerColliders(firstAgent); 342 | 343 | if (danAgent != null) 344 | danAgent.RemoveFingerColliders(secondAgent); 345 | } 346 | 347 | internal static void AddFingerColliders(string animation, DanAgent danAgent, CollisionAgent firstAgent, CollisionAgent secondAgent = null) 348 | { 349 | if (animation == null || firstAgent == null) 350 | return; 351 | 352 | if (danAgent != null && BoneNames.maleFingerAnimationNames.Contains(animation)) 353 | { 354 | danAgent.AddFingerColliders(firstAgent); 355 | 356 | if (secondAgent != null) 357 | danAgent.AddFingerColliders(secondAgent); 358 | 359 | return; 360 | } 361 | 362 | if (BoneNames.femaleSelfFingerAnimationNames.Contains(animation)) 363 | { 364 | firstAgent.AddFingerColliders(firstAgent); 365 | return; 366 | } 367 | 368 | if (secondAgent == null) 369 | return; 370 | 371 | if (BoneNames.lesbianFingerAnimationNames.Contains(animation)) 372 | { 373 | firstAgent.AddFingerColliders(secondAgent); 374 | secondAgent.AddFingerColliders(firstAgent); 375 | return; 376 | } 377 | } 378 | 379 | #if HS2 || AI 380 | 381 | internal static void SetupItemColliders(string animation) 382 | { 383 | ClearItemColliders(); 384 | AddItemColliders(animation); 385 | } 386 | 387 | internal static void AddItemColliders(string animation) 388 | { 389 | if (danAgents == null || danAgents.Count <= 0 || danAgents[0] == null || collisionAgents == null || collisionAgents[0] == null) 390 | return; 391 | 392 | m_itemColliders = new List(); 393 | m_itemColliders.AddRange(GetCharacterItemColliders(danAgents[0].m_danCharacter, animation)); 394 | m_itemColliders.AddRange(GetCharacterItemColliders(collisionAgents[0].m_collisionCharacter, animation)); 395 | 396 | m_anaItemColliders = new List(); 397 | m_anaItemColliders.AddRange(GetCharacterAnaItemColliders(danAgents[0].m_danCharacter, animation)); 398 | m_anaItemColliders.AddRange(GetCharacterAnaItemColliders(collisionAgents[0].m_collisionCharacter, animation)); 399 | 400 | collisionAgents[0].AddCollidersToKokan(m_itemColliders); 401 | collisionAgents[0].AddCollidersToAna(m_anaItemColliders); 402 | } 403 | 404 | internal static List GetCharacterItemColliders(ChaControl character, string animation) 405 | { 406 | var itemList = new List(); 407 | 408 | if (character == null) 409 | return itemList; 410 | 411 | foreach (var boneInfo in itemColliderInfo) 412 | { 413 | if (!boneInfo.animationNames.Contains(animation)) 414 | continue; 415 | 416 | foreach (var boneName in boneInfo.itemBones) 417 | { 418 | var bones = character.GetComponentsInChildren().Where(bone => bone.name.Equals(boneName)); 419 | if (bones == null || bones.Count() == 0) 420 | break; 421 | 422 | foreach (var bone in bones) 423 | { 424 | float radiusScale = Tools.ComputeRadiusScale(bone, boneInfo.direction); 425 | float heightScale = Tools.ComputeHeightScale(bone, boneInfo.direction); 426 | 427 | var collider = Tools.InitializeCollider(bone, boneInfo.colliderRadius * radiusScale, boneInfo.colliderHeight * heightScale, Vector3.zero, boneInfo.direction); 428 | itemList.Add(collider); 429 | } 430 | } 431 | } 432 | 433 | return itemList; 434 | } 435 | 436 | internal static List GetCharacterAnaItemColliders(ChaControl character, string animation) 437 | { 438 | var itemList = new List(); 439 | 440 | if (character == null || anaItemColliderInfo == null) 441 | return itemList; 442 | 443 | foreach (var boneInfo in anaItemColliderInfo) 444 | { 445 | if (!boneInfo.animationNames.Contains(animation)) 446 | continue; 447 | 448 | foreach (var boneName in boneInfo.itemBones) 449 | { 450 | var bones = character.GetComponentsInChildren().Where(bone => bone.name.Equals(boneName)); 451 | if (bones == null || bones.Count() == 0) 452 | break; 453 | 454 | foreach (var bone in bones) 455 | { 456 | float radiusScale = Tools.ComputeRadiusScale(bone, boneInfo.direction); 457 | float heightScale = Tools.ComputeHeightScale(bone, boneInfo.direction); 458 | 459 | var collider = Tools.InitializeCollider(bone, boneInfo.colliderRadius * radiusScale, boneInfo.colliderHeight * heightScale, Vector3.zero, boneInfo.direction); 460 | itemList.Add(collider); 461 | } 462 | } 463 | } 464 | 465 | return itemList; 466 | } 467 | 468 | internal static void ClearItemColliders() 469 | { 470 | if (collisionAgents == null || collisionAgents[0] == null) 471 | return; 472 | 473 | collisionAgents[0].RemoveCollidersFromKokan(m_itemColliders); 474 | collisionAgents[0].RemoveCollidersFromAna(m_anaItemColliders); 475 | } 476 | 477 | internal static void InitializeItemColliderInfo() 478 | { 479 | InitializeKokanItemColliderInfo(); 480 | InitializeItemAnaColliderInfo(); 481 | } 482 | 483 | internal static void InitializeKokanItemColliderInfo() 484 | { 485 | itemColliderInfo = new List 486 | { 487 | #if HS2 488 | new ItemColliderInfo(BoneNames.vibeAnimationNames, BoneNames.vibeBones, DynamicBoneColliderBase.Direction.Y, 0.15f, 0.56f), 489 | new ItemColliderInfo(BoneNames.vibe2AnimationNames, BoneNames.vibe2Bones, DynamicBoneColliderBase.Direction.Y, 0.15f, 0.56f), 490 | new ItemColliderInfo(BoneNames.dildoAnimationNames, BoneNames.dildoBones, DynamicBoneColliderBase.Direction.Y, 0.172f, 0.576f), 491 | new ItemColliderInfo(BoneNames.tentacleAnimationNames, BoneNames.tentacleBones, DynamicBoneColliderBase.Direction.X, 0.164f, 0.656f) 492 | #else 493 | new ItemColliderInfo(BoneNames.vibeAnimationNames, BoneNames.vibeBones, DynamicBoneColliderBase.Direction.Y, 0.1f, 0.5f), 494 | new ItemColliderInfo(BoneNames.vibe2AnimationNames, BoneNames.vibe2Bones, DynamicBoneColliderBase.Direction.Y, 0.1f, 0.5f), 495 | new ItemColliderInfo(BoneNames.dildoAnimationNames, BoneNames.dildoBones, DynamicBoneColliderBase.Direction.Y, 0.115f, 0.5f), 496 | new ItemColliderInfo(BoneNames.tentacleAnimationNames, BoneNames.tentacleBones, DynamicBoneColliderBase.Direction.X, 0.115f, 0.625f), 497 | #endif 498 | }; 499 | } 500 | 501 | internal static void InitializeItemAnaColliderInfo() 502 | { 503 | anaItemColliderInfo = new List 504 | { 505 | #if HS2 506 | new ItemColliderInfo(BoneNames.anaVibeAnimationNames, BoneNames.anaVibeBones, DynamicBoneColliderBase.Direction.Y, 0.125f, 0.4f), 507 | new ItemColliderInfo(BoneNames.tentacleAnimationNames, BoneNames.anaTentacleBones, DynamicBoneColliderBase.Direction.X, 0.164f, 0.656f) 508 | #else 509 | new ItemColliderInfo(BoneNames.anaVibeAnimationNames, BoneNames.anaVibeBones, DynamicBoneColliderBase.Direction.Y, 0.09f, 0.36f), 510 | new ItemColliderInfo(BoneNames.tentacleAnimationNames, BoneNames.tentacleBones, DynamicBoneColliderBase.Direction.X, 0.115f, 0.625f) 511 | #endif 512 | }; 513 | } 514 | 515 | internal static void ToggleMaleColliders() 516 | { 517 | if (danAgents == null) 518 | return; 519 | 520 | foreach (var agent in danAgents) 521 | agent.ToggleMaleColliders(); 522 | } 523 | 524 | #endif 525 | } 526 | } 527 | #endif -------------------------------------------------------------------------------- /Core_BetterPenetration/CollisionAgent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using UnityEngine; 5 | #if HS2 || AI 6 | using AIChara; 7 | #endif 8 | 9 | namespace Core_BetterPenetration 10 | { 11 | class CollisionAgent 12 | { 13 | internal ChaControl m_collisionCharacter; 14 | internal CollisionPoints m_collisionPoints; 15 | internal CollisionOptions m_collisionOptions; 16 | internal bool m_collisionPointsFound = false; 17 | 18 | internal Transform m_bpKokanTarget; 19 | internal Transform m_bpAnaTarget; 20 | internal Transform m_innerTarget; 21 | internal Transform m_innerHeadTarget; 22 | internal Transform m_kokanBone; 23 | internal Transform m_siriBoneL; 24 | internal Transform m_siriBoneR; 25 | internal Transform m_oralPullBone; 26 | internal List m_kokanDynamicBones = new List(); 27 | internal DynamicBone m_anaDynamicBones; 28 | internal DynamicBone m_bellyDynamicBone; 29 | internal List m_fingerColliders = new List(); 30 | internal List m_kokanPullBones = new List(); 31 | internal List m_anaPullBones = new List(); 32 | internal Transform m_innerKokan; 33 | 34 | internal float currentKokanPull = 0f; 35 | internal float currentOralPull = 0f; 36 | internal float currentAnaPull = 0f; 37 | internal Vector3 currentKokanDanDirection = Vector3.up; 38 | internal Vector3 currentOralDanDirection = Vector3.Normalize(Vector3.up + Vector3.back); 39 | internal Vector3 currentAnaDanDirection = Vector3.up; 40 | internal bool adjustFAnimation = false; 41 | internal bool anaPullException = false; 42 | internal float m_bellyColliderRadius; 43 | 44 | public CollisionAgent(ChaControl character, CollisionOptions options) 45 | { 46 | Initialize(character, options); 47 | } 48 | 49 | internal void Initialize(ChaControl character, CollisionOptions options) 50 | { 51 | m_collisionOptions = options; 52 | currentKokanPull = 0f; 53 | currentOralPull = 0f; 54 | currentAnaPull = 0f; 55 | currentKokanDanDirection = Vector3.up; 56 | currentOralDanDirection = Vector3.Normalize(Vector3.up + Vector3.back); 57 | currentAnaDanDirection = Vector3.up; 58 | 59 | List frontCollisionPoints = new List(); 60 | List backCollisionPoints = new List(); 61 | m_collisionPointsFound = false; 62 | 63 | if (character == null) 64 | return; 65 | 66 | m_collisionCharacter = character; 67 | 68 | m_kokanBone = Tools.GetTransformOfChaControl(m_collisionCharacter, BoneNames.KokanBone); 69 | if (m_kokanBone == null) 70 | return; 71 | 72 | m_bpKokanTarget = Tools.GetTransformOfChaControl(m_collisionCharacter, LookTargets.BPKokanTarget); 73 | m_bpAnaTarget = Tools.GetTransformOfChaControl(m_collisionCharacter, LookTargets.BPAnaTarget); 74 | m_innerTarget = Tools.GetTransformOfChaControl(m_collisionCharacter, LookTargets.InnerTarget); 75 | m_innerHeadTarget = Tools.GetTransformOfChaControl(m_collisionCharacter, LookTargets.InnerHeadTarget); 76 | m_siriBoneL = Tools.GetTransformOfChaControl(m_collisionCharacter, BoneNames.ButtBoneL); 77 | m_siriBoneR = Tools.GetTransformOfChaControl(m_collisionCharacter, BoneNames.ButtBoneR); 78 | m_innerKokan = Tools.GetTransformOfChaControl(m_collisionCharacter, "cf_J_Vagina_Inner"); 79 | 80 | #if !Studio 81 | for (int index = 0; index < options.frontCollisionInfo.Count; index++) 82 | { 83 | Transform frontCollisionPoint = Tools.GetTransformOfChaControl(m_collisionCharacter, options.frontCollisionInfo[index].name); 84 | frontCollisionPoints.Add(new CollisionPoint(frontCollisionPoint, options.frontCollisionInfo[index])); 85 | } 86 | for (int index = 0; index < options.backCollisonInfo.Count; index++) 87 | { 88 | Transform backCollisionPoint = Tools.GetTransformOfChaControl(m_collisionCharacter, options.backCollisonInfo[index].name); 89 | backCollisionPoints.Add(new CollisionPoint(backCollisionPoint, options.backCollisonInfo[index])); 90 | } 91 | 92 | if (frontCollisionPoints.Count == options.frontCollisionInfo.Count && 93 | backCollisionPoints.Count == options.backCollisonInfo.Count && 94 | m_innerTarget != null && m_innerHeadTarget != null) 95 | { 96 | m_collisionPointsFound = true; 97 | m_collisionPoints = new CollisionPoints(frontCollisionPoints, backCollisionPoints); 98 | } 99 | #else 100 | m_collisionPoints = null; 101 | #endif 102 | UnityEngine.Debug.Log($"constrainPointsFound {m_collisionPointsFound}"); 103 | 104 | m_kokanDynamicBones = new List(); 105 | foreach (DynamicBone dynamicBone in m_collisionCharacter.GetComponentsInChildren()) 106 | { 107 | if (dynamicBone == null || 108 | dynamicBone.m_Root == null || 109 | dynamicBone.name == null || 110 | m_collisionCharacter != dynamicBone.GetComponentInParent()) 111 | continue; 112 | 113 | if (dynamicBone.name.Contains(BoneNames.BPBone)) 114 | { 115 | dynamicBone.m_Colliders.Clear(); 116 | m_kokanDynamicBones.Add(dynamicBone); 117 | } 118 | else if (dynamicBone.name.Contains(BoneNames.BellyBone)) 119 | { 120 | dynamicBone.m_Colliders.Clear(); 121 | m_bellyDynamicBone = dynamicBone; 122 | m_bellyColliderRadius = dynamicBone.m_Radius; 123 | UpdateBellyBones(options.bellyBulgeScale); 124 | } 125 | else if (dynamicBone.name.Contains(BoneNames.AnaTarget)) 126 | { 127 | dynamicBone.m_Colliders.Clear(); 128 | m_anaDynamicBones = dynamicBone; 129 | } 130 | } 131 | 132 | m_kokanPullBones = new List(); 133 | foreach (var boneName in BoneNames.KokanPullBones) 134 | { 135 | var kokanTransform = Tools.GetTransformOfChaControl(m_collisionCharacter, boneName); 136 | if (kokanTransform == null) 137 | continue; 138 | 139 | m_kokanPullBones.Add(kokanTransform); 140 | } 141 | 142 | m_oralPullBone = Tools.GetTransformOfChaControl(m_collisionCharacter, BoneNames.MouthPullBone); 143 | 144 | foreach (var boneName in BoneNames.AnaPullBones) 145 | { 146 | var anaTransform = Tools.GetTransformOfChaControl(m_collisionCharacter, boneName); 147 | if (anaTransform == null) 148 | continue; 149 | 150 | m_anaPullBones.Add(anaTransform); 151 | } 152 | 153 | #if !Studio 154 | #if AI || HS2 155 | InitializeFingerColliders(0.055f, 0.18f); 156 | #else 157 | InitializeFingerColliders(0.0055f, 0.018f); 158 | #endif 159 | #endif 160 | } 161 | 162 | internal void CheckForAdjustment(string animationFile) 163 | { 164 | adjustFAnimation = false; 165 | anaPullException = false; 166 | if (m_kokanBone != null && BoneNames.animationAdjustmentList.Contains(animationFile)) 167 | adjustFAnimation = true; 168 | if (m_bpAnaTarget != null && BoneNames.anaPullExceptionList.Contains(animationFile)) 169 | anaPullException = true; 170 | } 171 | 172 | internal void AdjustMissionaryAnimation() 173 | { 174 | if (!m_collisionOptions.kokan_adjust || !adjustFAnimation || m_kokanBone == null) 175 | return; 176 | 177 | m_kokanBone.localPosition += new Vector3(0, m_collisionOptions.kokan_adjust_position_y, m_collisionOptions.kokan_adjust_position_z); 178 | m_kokanBone.localEulerAngles += new Vector3(m_collisionOptions.kokan_adjust_rotation_x, 0, 0); 179 | } 180 | 181 | internal void AdjustAnalAnimation() 182 | { 183 | if (!m_collisionOptions.ana_adjust || m_siriBoneL == null || m_siriBoneR == null) 184 | return; 185 | 186 | m_siriBoneR.localPosition += m_collisionOptions.ana_adjust_position; 187 | m_siriBoneR.localEulerAngles += m_collisionOptions.ana_adjust_rotation; 188 | 189 | m_siriBoneL.localPosition += new Vector3(-m_collisionOptions.ana_adjust_position.x, m_collisionOptions.ana_adjust_position.y, m_collisionOptions.ana_adjust_position.z); 190 | m_siriBoneL.localEulerAngles += new Vector3(m_collisionOptions.ana_adjust_rotation.x, -m_collisionOptions.ana_adjust_rotation.y, -m_collisionOptions.ana_adjust_rotation.z); 191 | } 192 | 193 | internal void UpdateCollisionOptions(CollisionOptions options) 194 | { 195 | m_collisionOptions = options; 196 | 197 | if (m_collisionPoints == null) 198 | return; 199 | 200 | m_collisionPoints.UpdateCollisionOptions(options); 201 | UpdateBellyBones(options.bellyBulgeScale); 202 | } 203 | 204 | internal void UpdateBellyBones(float radiusScale) 205 | { 206 | if (m_bellyDynamicBone == null) 207 | return; 208 | 209 | m_bellyDynamicBone.m_Radius = m_bellyColliderRadius * radiusScale; 210 | #if HS2 || AIS 211 | m_bellyDynamicBone.UpdateParameters(); 212 | #endif 213 | } 214 | 215 | internal void ClearColliders() 216 | { 217 | foreach (DynamicBone dynamicBone in m_collisionCharacter.GetComponentsInChildren()) 218 | { 219 | if ((dynamicBone.name.Contains(BoneNames.BPBone) || dynamicBone.name.Contains(BoneNames.BellyBone)) && 220 | m_collisionCharacter == dynamicBone.GetComponentInParent()) 221 | dynamicBone.m_Colliders.Clear(); 222 | } 223 | } 224 | 225 | internal void ResetParticles() 226 | { 227 | ResetKokanParticles(); 228 | ResetAnaParticles(); 229 | ResetBellyParticles(); 230 | } 231 | 232 | internal void EnableParticles(bool enable) 233 | { 234 | EnableKokanParticles(enable); 235 | EnableAnaParticles(enable); 236 | EnableBellyParticles(enable); 237 | } 238 | 239 | internal void ResetKokanParticles() 240 | { 241 | foreach (var kokanBone in m_kokanDynamicBones) 242 | { 243 | if (kokanBone == null) 244 | continue; 245 | 246 | kokanBone.ResetParticlesPosition(); 247 | } 248 | } 249 | 250 | internal void EnableKokanParticles(bool enable) 251 | { 252 | foreach (var kokanBone in m_kokanDynamicBones) 253 | { 254 | if (kokanBone == null) 255 | continue; 256 | 257 | kokanBone.enabled = enable; 258 | } 259 | } 260 | 261 | internal void ResetAnaParticles() 262 | { 263 | if (m_anaDynamicBones == null) 264 | return; 265 | 266 | m_anaDynamicBones.ResetParticlesPosition(); 267 | } 268 | 269 | internal void EnableAnaParticles(bool enable) 270 | { 271 | if (m_anaDynamicBones == null) 272 | return; 273 | 274 | m_anaDynamicBones.enabled = enable; 275 | } 276 | 277 | internal void ResetBellyParticles() 278 | { 279 | if (m_bellyDynamicBone == null) 280 | return; 281 | 282 | m_bellyDynamicBone.ResetParticlesPosition(); 283 | } 284 | 285 | internal void EnableBellyParticles(bool enable) 286 | { 287 | if (m_bellyDynamicBone == null) 288 | return; 289 | 290 | m_bellyDynamicBone.enabled = enable; 291 | } 292 | 293 | internal void PullKokanBones(float pullAmount, Vector3 outerDirection, Vector3 insideDirection) 294 | { 295 | if (!m_collisionOptions.enableKokanPush || m_kokanPullBones == null) 296 | { 297 | ReturnKokanBones(); 298 | return; 299 | } 300 | 301 | Vector3 averageDirection = Vector3.Normalize(outerDirection + insideDirection); 302 | 303 | Vector3 outer = insideDirection; 304 | Vector3 inner = insideDirection; 305 | 306 | if (m_collisionOptions.outer == CollisionOptions.TargetType.Average) 307 | outer = averageDirection; 308 | else if (m_collisionOptions.outer == CollisionOptions.TargetType.Outer) 309 | outer = outerDirection; 310 | 311 | if (m_collisionOptions.inner == CollisionOptions.TargetType.Average) 312 | inner = averageDirection; 313 | else if (m_collisionOptions.inner == CollisionOptions.TargetType.Outer) 314 | inner = outerDirection; 315 | 316 | if (m_innerKokan != null) 317 | { 318 | Vector3 innerPosition = m_bpKokanTarget.position + inner; 319 | Vector3 upVector = Vector3.Normalize(innerPosition - m_bpKokanTarget.position); 320 | Quaternion innerQuaternion = Quaternion.LookRotation(m_innerKokan.transform.forward, upVector); 321 | m_innerKokan.transform.rotation = innerQuaternion; 322 | } 323 | 324 | currentKokanDanDirection = outer; 325 | currentKokanPull += pullAmount * m_collisionOptions.kokanPullRate * Time.deltaTime; 326 | if (currentKokanPull > m_collisionOptions.maxKokanPush) 327 | currentKokanPull = m_collisionOptions.maxKokanPush; 328 | if (currentKokanPull < -m_collisionOptions.maxKokanPull) 329 | currentKokanPull = -m_collisionOptions.maxKokanPull; 330 | 331 | for (var kokanBone = 0; kokanBone < m_kokanPullBones.Count; kokanBone++) 332 | { 333 | m_kokanPullBones[kokanBone].localPosition = BoneNames.KokanPullWeights[kokanBone] * currentKokanPull * m_kokanPullBones[kokanBone].InverseTransformDirection(currentKokanDanDirection); 334 | } 335 | } 336 | 337 | internal void ReturnKokanBones() 338 | { 339 | if (m_innerKokan != null) 340 | m_innerKokan.localRotation = Quaternion.identity; 341 | 342 | if (MathHelpers.ApproximatelyZero(currentKokanPull) || m_kokanPullBones == null) 343 | return; 344 | 345 | var returnRate = m_collisionOptions.kokanReturnRate * Time.deltaTime; 346 | 347 | if (currentKokanPull > returnRate) 348 | currentKokanPull -= returnRate; 349 | else if (currentKokanPull < -returnRate) 350 | currentKokanPull += returnRate; 351 | else 352 | currentKokanPull = 0; 353 | 354 | for (var kokanBone = 0; kokanBone < m_kokanPullBones.Count; kokanBone++) 355 | m_kokanPullBones[kokanBone].localPosition = BoneNames.KokanPullWeights[kokanBone] * currentKokanPull * Vector3.up; 356 | } 357 | 358 | internal void PullOralBone(float pullAmount, Vector3 danDirection) 359 | { 360 | if (!m_collisionOptions.enableOralPush || m_oralPullBone == null) 361 | { 362 | ReturnOralBones(); 363 | return; 364 | } 365 | 366 | currentOralPull += pullAmount * m_collisionOptions.oralPullRate * Time.deltaTime; 367 | if (currentOralPull > m_collisionOptions.maxOralPush) 368 | currentOralPull = m_collisionOptions.maxOralPush; 369 | if (currentOralPull < -m_collisionOptions.maxOralPull) 370 | currentOralPull = -m_collisionOptions.maxOralPull; 371 | 372 | currentOralDanDirection = danDirection; 373 | m_oralPullBone.localPosition = currentOralPull * m_oralPullBone.InverseTransformDirection(currentOralDanDirection); 374 | } 375 | 376 | internal void ReturnOralBones() 377 | { 378 | if (MathHelpers.ApproximatelyZero(currentOralPull) || m_oralPullBone == null) 379 | return; 380 | 381 | var returnRate = m_collisionOptions.oralReturnRate * Time.deltaTime; 382 | 383 | if (currentOralPull > returnRate) 384 | currentOralPull -= returnRate; 385 | else if (currentOralPull < -returnRate) 386 | currentOralPull += returnRate; 387 | else 388 | currentOralPull = 0; 389 | 390 | m_oralPullBone.localPosition = currentOralPull * m_oralPullBone.InverseTransformDirection(currentOralDanDirection); 391 | } 392 | 393 | internal void PullAnaBone(float pullAmount, Vector3 outerDirection, Vector3 insideDirection) 394 | { 395 | if (!m_collisionOptions.enableAnaPush || m_anaPullBones == null) 396 | { 397 | ReturnAnaBones(); 398 | return; 399 | } 400 | 401 | Vector3 averageDirection = Vector3.Normalize(outerDirection + insideDirection); 402 | 403 | Vector3 outer = insideDirection; 404 | Vector3 inner = insideDirection; 405 | 406 | if (m_collisionOptions.outer == CollisionOptions.TargetType.Average) 407 | outer = averageDirection; 408 | else if (m_collisionOptions.outer == CollisionOptions.TargetType.Outer) 409 | outer = outerDirection; 410 | 411 | if (m_collisionOptions.inner == CollisionOptions.TargetType.Average) 412 | inner = averageDirection; 413 | else if (m_collisionOptions.inner == CollisionOptions.TargetType.Outer) 414 | inner = outerDirection; 415 | 416 | if (m_bpAnaTarget != null) 417 | { 418 | Vector3 innerPosition = m_bpAnaTarget.position + inner; 419 | Vector3 upVector = Vector3.Normalize(innerPosition - m_bpAnaTarget.position); 420 | Quaternion innerQuaternion = Quaternion.LookRotation(m_bpAnaTarget.transform.forward, upVector); 421 | m_bpAnaTarget.transform.rotation = innerQuaternion; 422 | } 423 | 424 | currentAnaDanDirection = outer; 425 | currentAnaPull += pullAmount * m_collisionOptions.anaPullRate * Time.deltaTime; 426 | if (currentAnaPull > m_collisionOptions.maxAnaPush) 427 | currentAnaPull = m_collisionOptions.maxAnaPush; 428 | if (currentAnaPull < -m_collisionOptions.maxAnaPull) 429 | currentAnaPull = -m_collisionOptions.maxAnaPull; 430 | 431 | for (var anaBone = 0; anaBone < m_anaPullBones.Count; anaBone++) 432 | { 433 | m_anaPullBones[anaBone].localPosition = BoneNames.AnaPullWeights[anaBone] * currentAnaPull * m_anaPullBones[anaBone].InverseTransformDirection(currentAnaDanDirection); 434 | } 435 | } 436 | 437 | internal void ReturnAnaBones() 438 | { 439 | if (m_bpAnaTarget != null) 440 | m_bpAnaTarget.localRotation = Quaternion.identity; 441 | 442 | if (MathHelpers.ApproximatelyZero(currentAnaPull) || m_anaPullBones == null) 443 | return; 444 | 445 | var returnRate = m_collisionOptions.anaReturnRate * Time.deltaTime; 446 | 447 | if (currentAnaPull > returnRate) 448 | currentAnaPull -= returnRate; 449 | else if (currentAnaPull < -returnRate) 450 | currentAnaPull += returnRate; 451 | else 452 | currentAnaPull = 0; 453 | 454 | for (var anaBone = 0; anaBone < m_anaPullBones.Count; anaBone++) 455 | { 456 | m_anaPullBones[anaBone].localPosition = BoneNames.AnaPullWeights[anaBone] * currentAnaPull * Vector3.up; 457 | } 458 | } 459 | 460 | internal void InitializeFingerColliders(float fingerRadius, float fingerLength) 461 | { 462 | m_fingerColliders = new List(); 463 | foreach (var bone in BoneNames.FingerColliders) 464 | { 465 | var fingerTransform = Tools.GetTransformOfChaControl(m_collisionCharacter, bone); 466 | if (fingerTransform == null) 467 | continue; 468 | 469 | var fingerCollider = Tools.InitializeCollider(fingerTransform, fingerRadius * (fingerTransform.lossyScale.y + fingerTransform.lossyScale.z) / 2, fingerLength * fingerTransform.lossyScale.x, Vector3.zero); 470 | 471 | m_fingerColliders.Add(fingerCollider); 472 | } 473 | } 474 | 475 | internal void AddFingerColliders(CollisionAgent target) 476 | { 477 | if (m_fingerColliders == null || m_fingerColliders.Count == 0) 478 | return; 479 | 480 | foreach (DynamicBone dynamicBone in target.m_kokanDynamicBones) 481 | { 482 | foreach (var collider in m_fingerColliders) 483 | { 484 | if (collider != null && !dynamicBone.m_Colliders.Contains(collider)) 485 | dynamicBone.m_Colliders.Add(collider); 486 | } 487 | } 488 | } 489 | 490 | internal void RemoveFingerColliders(CollisionAgent target) 491 | { 492 | if (m_fingerColliders == null || m_fingerColliders.Count == 0) 493 | return; 494 | 495 | foreach (DynamicBone dynamicBone in target.m_kokanDynamicBones) 496 | { 497 | foreach (var collider in m_fingerColliders) 498 | { 499 | if (collider != null) 500 | dynamicBone.m_Colliders.Remove(collider); 501 | } 502 | } 503 | } 504 | 505 | internal void AddCollidersToKokan(List colliders) 506 | { 507 | if (colliders == null || colliders.Count == 0) 508 | return; 509 | 510 | foreach (var collider in colliders) 511 | { 512 | if (collider == null) 513 | continue; 514 | 515 | foreach (DynamicBone dynamicBone in m_kokanDynamicBones) 516 | { 517 | if (!dynamicBone.m_Colliders.Contains(collider)) 518 | dynamicBone.m_Colliders.Add(collider); 519 | } 520 | 521 | if (m_bellyDynamicBone == null) 522 | continue; 523 | 524 | m_bellyDynamicBone.m_Colliders.Add(collider); 525 | } 526 | } 527 | 528 | internal void RemoveCollidersFromKokan(List colliders) 529 | { 530 | if (colliders == null || colliders.Count == 0) 531 | return; 532 | 533 | foreach (var collider in colliders) 534 | { 535 | if (collider == null) 536 | continue; 537 | 538 | foreach (DynamicBone dynamicBone in m_kokanDynamicBones) 539 | dynamicBone.m_Colliders.Remove(collider); 540 | 541 | if (!m_collisionOptions.enableBellyBulge || m_bellyDynamicBone == null) 542 | continue; 543 | 544 | m_bellyDynamicBone.m_Colliders.Remove(collider); 545 | } 546 | } 547 | 548 | internal void AddCollidersToAna(List colliders) 549 | { 550 | if (colliders == null || colliders.Count == 0 || m_anaDynamicBones == null) 551 | return; 552 | 553 | foreach (var collider in colliders) 554 | { 555 | if (collider == null) 556 | continue; 557 | 558 | if (!m_anaDynamicBones.m_Colliders.Contains(collider)) 559 | m_anaDynamicBones.m_Colliders.Add(collider); 560 | } 561 | } 562 | 563 | internal void RemoveCollidersFromAna(List colliders) 564 | { 565 | if (colliders == null || colliders.Count == 0 || m_anaDynamicBones == null) 566 | return; 567 | 568 | foreach (var collider in colliders) 569 | { 570 | if (collider == null) 571 | continue; 572 | 573 | m_anaDynamicBones.m_Colliders.Remove(collider); 574 | } 575 | } 576 | 577 | internal void ClearKokanDynamicBones() 578 | { 579 | if (m_kokanDynamicBones == null || m_kokanDynamicBones.Count == 0) 580 | return; 581 | 582 | foreach (var kokanBone in m_kokanDynamicBones) 583 | { 584 | if (kokanBone == null) 585 | continue; 586 | 587 | kokanBone.m_Colliders.Clear(); 588 | } 589 | 590 | if (m_bellyDynamicBone == null) 591 | return; 592 | 593 | m_bellyDynamicBone.m_Colliders.Clear(); 594 | } 595 | 596 | internal void ClearAnaDynamicBones() 597 | { 598 | if (m_anaDynamicBones == null) 599 | return; 600 | 601 | m_anaDynamicBones.m_Colliders.Clear(); 602 | } 603 | } 604 | } 605 | //#endif -------------------------------------------------------------------------------- /KKS_BetterPenetration/KKS_BetterPenetration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Collections.Generic; 4 | using BepInEx; 5 | using BepInEx.Bootstrap; 6 | using BepInEx.Configuration; 7 | using HarmonyLib; 8 | using Core_BetterPenetration; 9 | using UnityEngine; 10 | 11 | namespace KKS_BetterPenetration 12 | { 13 | [BepInPlugin(GUID, "KKS Better Penetration", VERSION)] 14 | [BepInDependency("com.deathweasel.bepinex.uncensorselector", "3.11.1")] 15 | [BepInDependency("com.rclcircuit.bepinex.modboneimplantor", "1.1.1")] 16 | [BepInProcess(KKAPI.KoikatuAPI.GameProcessName)] 17 | [BepInProcess(KKAPI.KoikatuAPI.VRProcessName)] 18 | public class KKS_BetterPenetration : BaseUnityPlugin 19 | { 20 | public static KKS_BetterPenetration instance; 21 | 22 | public const string GUID = "animal42069.KKSbetterpenetration"; 23 | public const string VERSION = Constants.Version; 24 | internal const int MaleLimit = 2; 25 | internal const int FemaleLimit = 2; 26 | 27 | internal static readonly List frontOffsets = new List { -0.04f, 0.04f, 0.06f }; 28 | internal static readonly List backOffsets = new List { -0.04f, 0.02f, 0f }; 29 | internal static readonly List frontPointsInward = new List { false, false, false, }; 30 | internal static readonly List backPointsInward = new List { false, true, true }; 31 | 32 | internal static readonly ConfigEntry[] _danColliderLengthScale = new ConfigEntry[MaleLimit]; 33 | internal static readonly ConfigEntry[] _danColliderRadiusScale = new ConfigEntry[MaleLimit]; 34 | internal static readonly ConfigEntry[] _danLengthSquishFactor = new ConfigEntry[MaleLimit]; 35 | internal static readonly ConfigEntry[] _danGirthSquishFactor = new ConfigEntry[MaleLimit]; 36 | internal static readonly ConfigEntry[] _danSquishThreshold = new ConfigEntry[MaleLimit]; 37 | internal static readonly ConfigEntry[] _danSquishOralGirth = new ConfigEntry[MaleLimit]; 38 | internal static readonly ConfigEntry[] _simplifyVaginal = new ConfigEntry[MaleLimit]; 39 | internal static readonly ConfigEntry[] _simplifyOral = new ConfigEntry[MaleLimit]; 40 | internal static readonly ConfigEntry[] _simplifyAnal = new ConfigEntry[MaleLimit]; 41 | internal static readonly ConfigEntry[] _rotateTamaWithShaft = new ConfigEntry[MaleLimit]; 42 | internal static readonly ConfigEntry[] _maxCorrection = new ConfigEntry[MaleLimit]; 43 | internal static readonly ConfigEntry[] _limitCorrection = new ConfigEntry[MaleLimit]; 44 | 45 | internal static ConfigEntry _clippingDepth; 46 | internal static ConfigEntry _kokanOffset; 47 | internal static ConfigEntry _innerKokanOffset; 48 | internal static ConfigEntry _mouthOffset; 49 | internal static ConfigEntry _innerMouthOffset; 50 | internal static ConfigEntry _enableOralPushPull; 51 | internal static ConfigEntry _maxOralPush; 52 | internal static ConfigEntry _maxOralPull; 53 | internal static ConfigEntry _oralPullRate; 54 | internal static ConfigEntry _oralReturnRate; 55 | 56 | internal static readonly ConfigEntry[] _frontCollisionOffset = new ConfigEntry[frontOffsets.Count]; 57 | internal static readonly ConfigEntry[] _backCollisionOffset = new ConfigEntry[backOffsets.Count]; 58 | 59 | internal static Harmony harmony; 60 | 61 | //In VR, type "VRHScene" is used instead of "HSceneProc". Use type "BaseLoader" as the type for "hSceneProc" since it's inherited by both "HSceneProc" and "VRHScene". 62 | internal static MonoBehaviour hSceneProc; 63 | internal static Traverse hSceneProcTraverse; 64 | internal static bool hSceneStarted = false; 65 | internal static bool inHScene = false; 66 | internal static bool loadingCharacter = false; 67 | internal static bool changeAnimationStep1 = false; 68 | internal static bool changeAnimationStep2 = false; 69 | internal static int changeAnimationCount = 0; 70 | 71 | internal void Awake() 72 | { 73 | instance = this; 74 | 75 | for (int maleNum = 0; maleNum < MaleLimit; maleNum++) 76 | { 77 | (_danColliderLengthScale[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Penis Collider: Length Scale", 1.0f, new ConfigDescription("How much to scale collider length", new AcceptableValueRange(0.5f, 1.5f)))).SettingChanged += (s, e) => 78 | { UpdateDanColliders(); }; 79 | (_danColliderRadiusScale[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Penis Collider: Radius Scale", 1.0f, new ConfigDescription("How much to scale collider radius", new AcceptableValueRange(0.5f, 1.5f)))).SettingChanged += (s, e) => 80 | { UpdateDanColliders(); }; 81 | (_danLengthSquishFactor[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Penis: Squish Length Factor", 0.6f, new ConfigDescription("How much the length of the penis squishes after it has passed the squish threshold", new AcceptableValueRange(0, 1)))).SettingChanged += (s, e) => 82 | { UpdateDanOptions(); }; 83 | (_danGirthSquishFactor[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Penis: Squish Girth Factor", 0.2f, new ConfigDescription("How much the girth of the penis squishes after it has passed the squish threshold", new AcceptableValueRange(0, 1)))).SettingChanged += (s, e) => 84 | { UpdateDanOptions(); }; 85 | (_danSquishThreshold[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Penis: Squish Threshold", 0.2f, new ConfigDescription("Allows the penis to begin squishing (shorten length increase girth) after this amount of the penis has penetrated.", new AcceptableValueRange(0, 1)))).SettingChanged += (s, e) => 86 | { UpdateDanOptions(); }; 87 | (_danSquishOralGirth[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Penis: Squish Oral Girth", false, "Allows the penis to squish (increase girth) during oral.")).SettingChanged += (s, e) => 88 | { UpdateDanOptions(); }; 89 | (_simplifyVaginal[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Simplify Penetration Calculation", false, "Simplifys penetration calclation by always having it target the same internal point. Only valid for BP penis uncensors.")).SettingChanged += (s, e) => 90 | { UpdateDanOptions(); }; 91 | (_simplifyOral[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Simplify Oral Calculation", false, "Simplifys oral penetration calclation by always having it target the same internal point. Only valid for BP penis uncensors.")).SettingChanged += (s, e) => 92 | { UpdateDanOptions(); }; 93 | (_simplifyAnal[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Simplify Anal Calculation", false, "Simplifys anal penetration calclation by always having it target the same internal point. Only valid for BP penis uncensors.")).SettingChanged += (s, e) => 94 | { UpdateDanOptions(); }; 95 | (_rotateTamaWithShaft[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Rotate Balls with Shaft", true, "If enabled, the base of the balls will be locked to the base of the shaft")).SettingChanged += (s, e) => 96 | { UpdateDanOptions(); }; 97 | (_limitCorrection[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Limit Penis Movement", false, "Limit the penis from moving laterally too much from frame to frame.")).SettingChanged += (s, e) => 98 | { UpdateDanOptions(); }; 99 | (_maxCorrection[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Limit Penis Amount", 0.2f, "Amount of movement to limit the penis to. Smaller values result in smoother animations, but can cause clipping.")).SettingChanged += (s, e) => 100 | { UpdateDanOptions(); }; 101 | } 102 | 103 | (_clippingDepth = Config.Bind("Female Options", "Clipping Depth", 0.02f, "Set how close to body surface to limit penis for clipping purposes. Smaller values will result in more clipping through the body, larger values will make the shaft wander further away from the intended penetration point.")).SettingChanged += (s, e) => 104 | { UpdateCollisionOptions(); }; 105 | for (int offset = 0; offset < frontOffsets.Count; offset++) 106 | (_frontCollisionOffset[offset] = Config.Bind("Female Options", "Clipping Offset: Front Collision " + offset, frontOffsets[offset], "Individual offset on colision point, to improve clipping")).SettingChanged += (s, e) => 107 | { UpdateCollisionOptions(); }; 108 | for (int offset = 0; offset < backOffsets.Count; offset++) 109 | (_backCollisionOffset[offset] = Config.Bind("Female Options", "Clipping Offset: Back Collision " + offset, backOffsets[offset], "Individual offset on colision point, to improve clipping")).SettingChanged += (s, e) => 110 | { UpdateCollisionOptions(); }; 111 | (_kokanOffset = Config.Bind("Female Options", "Target Offset: Vagina Target", 0.0f, "Offset of the vagina target")).SettingChanged += (s, e) => 112 | { UpdateCollisionOptions(); }; 113 | (_innerKokanOffset = Config.Bind("Female Options", "Target Offset: Inner Vagina Target", 0.0f, "Offset of the simplified inner vagina target")).SettingChanged += (s, e) => 114 | { UpdateCollisionOptions(); }; 115 | (_mouthOffset = Config.Bind("Female Options", "Target Offset: Mouth Target", 0.0f, "Offset of the mouth target")).SettingChanged += (s, e) => 116 | { UpdateCollisionOptions(); }; 117 | (_innerMouthOffset = Config.Bind("Female Options", "Target Offset: Inner Mouth Target", 0.0f, "Offset of the simplified inner mouth target")).SettingChanged += (s, e) => 118 | { UpdateCollisionOptions(); }; 119 | (_enableOralPushPull = Config.Bind("Female Options", "Oral Push/Pull: Enable", true, "Enable mouth push/pull during penetration")).SettingChanged += (s, e) => 120 | { UpdateCollisionOptions(); }; 121 | (_maxOralPush = Config.Bind("Female Options", "Oral Push/Pull: Max Push", 0.002f, "Maximum amount to push the mouth inwards during penetration")).SettingChanged += (s, e) => 122 | { UpdateCollisionOptions(); }; 123 | (_maxOralPull = Config.Bind("Female Options", "Oral Push/Pull: Max Pull", 0.006f, "Maximum amount to pull the mouth outwards during penetration")).SettingChanged += (s, e) => 124 | { UpdateCollisionOptions(); }; 125 | (_oralPullRate = Config.Bind("Female Options", "Oral Push/Pull: Push/Pull Rate", 18f, "How quickly to push or pull the mouth during penetration")).SettingChanged += (s, e) => 126 | { UpdateCollisionOptions(); }; 127 | (_oralReturnRate = Config.Bind("Female Options", "Oral Push/Pull: Return Rate", 0.03f, "How quickly the mouth returns to its original shape when there is no penetration")).SettingChanged += (s, e) => 128 | { UpdateCollisionOptions(); }; 129 | 130 | harmony = new Harmony("KKS_BetterPenetration"); 131 | harmony.PatchAll(GetType()); 132 | 133 | Type VRHSceneType = Type.GetType("VRHScene, Assembly-CSharp"); 134 | if (VRHSceneType != null) 135 | { 136 | harmony.Patch(VRHSceneType.GetMethod("Start", AccessTools.all), postfix: new HarmonyMethod(GetType().GetMethod(nameof(HScene_PostStart), AccessTools.all))); 137 | harmony.Patch(VRHSceneType.GetMethod("EndProc", AccessTools.all), prefix: new HarmonyMethod(GetType().GetMethod(nameof(HSceneProc_EndProc), AccessTools.all))); 138 | harmony.Patch(VRHSceneType.GetMethod("Update", AccessTools.all), postfix: new HarmonyMethod(GetType().GetMethod(nameof(HScene_PostUpdate), AccessTools.all))); 139 | harmony.Patch(VRHSceneType.GetMethod("ChangeAnimator", AccessTools.all), prefix: new HarmonyMethod(GetType().GetMethod(nameof(HSceneProc_PreChangeAnimator), AccessTools.all))); 140 | } 141 | 142 | Chainloader.PluginInfos.TryGetValue("com.deathweasel.bepinex.uncensorselector", out PluginInfo pluginInfo); 143 | if (pluginInfo == null || pluginInfo.Instance == null) 144 | return; 145 | 146 | Type uncensorSelectorControllerType = pluginInfo.Instance.GetType().GetNestedType("UncensorSelectorController", AccessTools.all); 147 | if (uncensorSelectorControllerType == null) 148 | return; 149 | 150 | MethodInfo uncensorSelectorReloadCharacterBody = AccessTools.Method(uncensorSelectorControllerType, "ReloadCharacterBody"); 151 | if (uncensorSelectorReloadCharacterBody == null) 152 | return; 153 | 154 | harmony.Patch(uncensorSelectorReloadCharacterBody, prefix: new HarmonyMethod(GetType(), "BeforeCharacterReload"), postfix: new HarmonyMethod(GetType(), "AfterCharacterReload")); 155 | UnityEngine.Debug.Log("KK_BetterPenetration: patched UncensorSelector::ReloadCharacterBody correctly"); 156 | 157 | MethodInfo uncensorSelectorReloadCharacterPenis = AccessTools.Method(uncensorSelectorControllerType, "ReloadCharacterPenis"); 158 | if (uncensorSelectorReloadCharacterPenis == null) 159 | return; 160 | 161 | harmony.Patch(uncensorSelectorReloadCharacterPenis, prefix: new HarmonyMethod(GetType(), "BeforeDanCharacterReload"), postfix: new HarmonyMethod(GetType(), "AfterDanCharacterReload")); 162 | UnityEngine.Debug.Log("KK_BetterPenetration: patched UncensorSelector::ReloadCharacterPenis patched"); 163 | 164 | QualitySettings.skinWeights = SkinWeights.Unlimited; 165 | } 166 | 167 | [HarmonyPostfix, HarmonyPatch(typeof(ChaControl), "SetHideHairAccessory")] 168 | internal static void ChaControl_SetHideHairAccessory(ChaControl __instance) 169 | { 170 | Tools.RemoveCollidersFromCoordinate(__instance); 171 | } 172 | 173 | [HarmonyPostfix, HarmonyPatch(typeof(ChaControl), "UpdateSiru")] 174 | internal static void ChaControl_UpdateSiru(ChaControl __instance, bool forceChange) 175 | { 176 | if (!forceChange) 177 | return; 178 | 179 | Tools.RemoveCollidersFromCoordinate(__instance); 180 | } 181 | 182 | [HarmonyPostfix, HarmonyPatch(typeof(HSceneProc), "Start")] 183 | internal static void HScene_PostStart(MonoBehaviour __instance) 184 | { 185 | hSceneProc = __instance; 186 | hSceneProcTraverse = Traverse.Create(__instance); 187 | 188 | hSceneStarted = true; 189 | inHScene = false; 190 | } 191 | 192 | [HarmonyPrefix, HarmonyPatch(typeof(HSceneProc), "EndProc")] 193 | internal static void HSceneProc_EndProc() 194 | { 195 | CoreGame.SetAgentsBPBoneWeights(0f); 196 | CoreGame.OnEndScene(); 197 | 198 | inHScene = false; 199 | 200 | if (hSceneProc == null) 201 | return; 202 | 203 | var lookDan = hSceneProcTraverse.Field("lookDan").Value; 204 | if (lookDan != null) 205 | lookDan.transLookAtNull = null; 206 | 207 | hSceneProcTraverse = null; 208 | hSceneProc = null; 209 | } 210 | 211 | [HarmonyPostfix, HarmonyPatch(typeof(HSceneProc), "Update")] 212 | internal static void HScene_PostUpdate() 213 | { 214 | if (!hSceneStarted || inHScene || hSceneProc == null || !hSceneProc.enabled || hSceneProcTraverse == null) 215 | return; 216 | 217 | List danOptions = PopulateDanOptionsList(); 218 | List collisionOptions = PopulateCollisionOptionsList(); 219 | 220 | var lstFemale = hSceneProcTraverse.Field("lstFemale").GetValue>(); 221 | if (lstFemale == null || lstFemale.Count == 0) 222 | return; 223 | 224 | List femaleList = new List(); 225 | foreach (var female in lstFemale) 226 | if (female != null) 227 | femaleList.Add(female); 228 | 229 | List maleList = new List(); 230 | var male = hSceneProcTraverse.Field("male").GetValue(); 231 | if (male != null) 232 | maleList.Add(male); 233 | 234 | var male1 = hSceneProcTraverse.Field("male1").GetValue(); 235 | if (male1 != null) 236 | maleList.Add(male1); 237 | 238 | if (maleList.IsNullOrEmpty()) 239 | return; 240 | 241 | CoreGame.InitializeAgents(maleList, femaleList, danOptions, collisionOptions); 242 | CoreGame.SetAgentsBPBoneWeights(1f); 243 | inHScene = true; 244 | hSceneStarted = false; 245 | } 246 | 247 | [HarmonyPrefix, HarmonyPatch(typeof(HSceneProc), "ChangeAnimator")] 248 | internal static void HSceneProc_PreChangeAnimator(HSceneProc.AnimationListInfo _nextAinmInfo) 249 | { 250 | if (!inHScene || _nextAinmInfo == null || _nextAinmInfo.pathFemaleBase.file == null) 251 | return; 252 | 253 | CoreGame.OnChangeAnimation(_nextAinmInfo.pathFemaleBase.file); 254 | } 255 | 256 | [HarmonyPostfix, HarmonyPatch(typeof(HSceneProc), "ChangeAnimator")] 257 | internal static void HSceneProc_PostChangeAnimator() 258 | { 259 | if (!inHScene) 260 | return; 261 | 262 | changeAnimationStep1 = true; 263 | } 264 | 265 | [HarmonyPostfix, HarmonyPatch(typeof(ChaControl), "setPlay")] 266 | internal static void ChaControl_PostSetPlay() 267 | { 268 | changeAnimationStep1 = true; 269 | } 270 | 271 | [HarmonyPostfix, HarmonyPatch(typeof(Lookat_dan), "Release")] 272 | internal static void H_Lookat_dan_PostRelease(Lookat_dan __instance) 273 | { 274 | if (!inHScene || loadingCharacter || __instance.strPlayMotion == null || __instance.male == null) 275 | return; 276 | 277 | int maleNum = 0; 278 | if (__instance.male.chaID != 0) 279 | maleNum = 1; 280 | 281 | CoreGame.LookAtDanRelease(maleNum, __instance.numFemale, false); 282 | } 283 | 284 | [HarmonyPostfix, HarmonyPatch(typeof(Lookat_dan), "SetInfo")] 285 | internal static void H_Lookat_dan_PostSetInfo(Lookat_dan __instance) 286 | { 287 | if (!inHScene || loadingCharacter || __instance.strPlayMotion == null) 288 | return; 289 | 290 | int maleNum = 0; 291 | if (__instance.male != null && __instance.male.chaID != 0) 292 | maleNum = 1; 293 | 294 | CoreGame.LookAtDanSetup(__instance.transLookAtNull, __instance.strPlayMotion, __instance.bTopStick, maleNum, __instance.numFemale, false, true); 295 | } 296 | 297 | [HarmonyPostfix, HarmonyPatch(typeof(Lookat_dan), "LateUpdate")] 298 | internal static void Lookat_dan_PostLateUpdate(Lookat_dan __instance) 299 | { 300 | if (!inHScene || loadingCharacter || __instance.strPlayMotion == null || __instance.male == null) 301 | return; 302 | 303 | if (changeAnimationStep1) 304 | { 305 | CoreGame.ResetParticles(); 306 | CoreGame.EnableParticles(false); 307 | changeAnimationStep1 = false; 308 | changeAnimationStep2 = true; 309 | changeAnimationCount = 0; 310 | } 311 | 312 | if (changeAnimationStep2 && ++changeAnimationCount > 3) 313 | { 314 | CoreGame.EnableParticles(true); 315 | changeAnimationStep2 = false; 316 | } 317 | 318 | if (__instance.male.chaID != 0) 319 | return; 320 | 321 | CoreGame.LookAtDanUpdate(__instance.transLookAtNull, __instance.strPlayMotion, __instance.bTopStick, false, 0, __instance.numFemale, false, true); 322 | 323 | var lstFemale = hSceneProcTraverse.Field("lstFemale").GetValue>(); 324 | if (lstFemale == null || lstFemale.Count == 0) 325 | return; 326 | 327 | List femaleList = new List(); 328 | foreach (var female in lstFemale) 329 | if (female != null) 330 | femaleList.Add(female); 331 | } 332 | 333 | internal static void UpdateDanColliders() 334 | { 335 | if (!inHScene) 336 | return; 337 | 338 | for (int index = 0; index < MaleLimit; index++) 339 | CoreGame.UpdateDanCollider(index, _danColliderRadiusScale[index].Value, _danColliderLengthScale[index].Value); 340 | } 341 | 342 | internal static void UpdateDanOptions() 343 | { 344 | if (!inHScene) 345 | return; 346 | 347 | for (int maleNum = 0; maleNum < MaleLimit; maleNum++) 348 | CoreGame.UpdateDanOptions(maleNum, _danLengthSquishFactor[maleNum].Value, _danGirthSquishFactor[maleNum].Value, 349 | _danSquishThreshold[maleNum].Value, _danSquishOralGirth[maleNum].Value, 350 | _simplifyVaginal[maleNum].Value, _simplifyOral[maleNum].Value, _rotateTamaWithShaft[maleNum].Value, 351 | _limitCorrection[maleNum].Value, _maxCorrection[maleNum].Value); 352 | } 353 | 354 | internal static void UpdateCollisionOptions() 355 | { 356 | if (!inHScene) 357 | return; 358 | 359 | List collisionOptions = PopulateCollisionOptionsList(); 360 | for (int index = 0; index < FemaleLimit; index++) 361 | CoreGame.UpdateCollisionOptions(index, collisionOptions[index]); 362 | } 363 | 364 | internal static List PopulateDanOptionsList() 365 | { 366 | List danOptions = new List(); 367 | 368 | for (int maleNum = 0; maleNum < MaleLimit; maleNum++) 369 | { 370 | danOptions.Add(new DanOptions(_danColliderRadiusScale[maleNum].Value, _danColliderLengthScale[maleNum].Value, 371 | _danLengthSquishFactor[maleNum].Value, _danGirthSquishFactor[maleNum].Value, _danSquishThreshold[maleNum].Value, _danSquishOralGirth[maleNum].Value, 372 | _simplifyVaginal[maleNum].Value, _simplifyOral[maleNum].Value, _simplifyAnal[maleNum].Value, _rotateTamaWithShaft[maleNum].Value, 373 | _limitCorrection[maleNum].Value, _maxCorrection[maleNum].Value)); 374 | } 375 | 376 | return danOptions; 377 | } 378 | 379 | internal static List PopulateCollisionOptionsList() 380 | { 381 | List collisionOptions = new List(); 382 | 383 | List frontInfo = new List(); 384 | for (int info = 0; info < BoneNames.frontCollisionList.Count; info++) 385 | frontInfo.Add(new CollisionPointInfo(BoneNames.frontCollisionList[info], _frontCollisionOffset[info].Value, frontPointsInward[info])); 386 | 387 | List backInfo = new List(); 388 | for (int info = 0; info < BoneNames.backCollisionList.Count; info++) 389 | backInfo.Add(new CollisionPointInfo(BoneNames.backCollisionList[info], _backCollisionOffset[info].Value, backPointsInward[info])); 390 | 391 | for (int femaleNum = 0; femaleNum < FemaleLimit; femaleNum++) 392 | { 393 | collisionOptions.Add(new CollisionOptions(_kokanOffset.Value, _innerKokanOffset.Value, _mouthOffset.Value, _innerMouthOffset.Value, 394 | false, 0, 0, 0, 395 | false, Vector3.zero, Vector3.zero, 396 | _clippingDepth.Value, frontInfo, backInfo, 397 | false, 0, 0, 0, 0, 398 | _enableOralPushPull.Value, _maxOralPush.Value, _maxOralPull.Value, _oralPullRate.Value, _oralReturnRate.Value, 399 | false, 0, 0, 0, 0)); 400 | } 401 | 402 | return collisionOptions; 403 | } 404 | 405 | internal static void BeforeCharacterReload(object __instance) 406 | { 407 | if (!inHScene) 408 | return; 409 | 410 | ChaControl chaControl = (ChaControl)__instance.GetPrivateProperty("ChaControl"); 411 | if (chaControl == null || chaControl.sex == 0) 412 | return; 413 | 414 | loadingCharacter = true; 415 | CoreGame.SetDansHaveNewTarget(true); 416 | } 417 | 418 | internal static void AfterCharacterReload(object __instance) 419 | { 420 | ChaControl chaControl = (ChaControl)__instance.GetPrivateProperty("ChaControl"); 421 | if (chaControl == null) 422 | return; 423 | 424 | CoreGame.SetBPBoneWeights(chaControl, inHScene ? 1f : 0f); 425 | 426 | if (chaControl.sex == 0 || !inHScene || hSceneProc == null || hSceneProcTraverse == null) 427 | return; 428 | 429 | var lstFemale = hSceneProcTraverse.Field("lstFemale").GetValue>(); 430 | if (lstFemale == null || lstFemale.Count == 0) 431 | return; 432 | 433 | List femaleList = new List(); 434 | foreach (var female in lstFemale) 435 | if (female != null) 436 | femaleList.Add(female); 437 | 438 | List collisionOptions = PopulateCollisionOptionsList(); 439 | CoreGame.InitializeCollisionAgents(femaleList, collisionOptions); 440 | loadingCharacter = false; 441 | } 442 | 443 | internal static void BeforeDanCharacterReload(object __instance) 444 | { 445 | if (!inHScene) 446 | return; 447 | 448 | ChaControl chaControl = (ChaControl)__instance.GetPrivateProperty("ChaControl"); 449 | if (chaControl == null || chaControl.sex != 0) 450 | return; 451 | 452 | loadingCharacter = true; 453 | CoreGame.SetDansHaveNewTarget(true); 454 | CoreGame.ClearDanAgents(); 455 | } 456 | 457 | internal static void AfterDanCharacterReload(object __instance) 458 | { 459 | if (!inHScene || hSceneProc == null) 460 | return; 461 | 462 | ChaControl chaControl = (ChaControl)__instance.GetPrivateProperty("ChaControl"); 463 | if (chaControl == null || chaControl.sex != 0) 464 | return; 465 | 466 | List danOptions = PopulateDanOptionsList(); 467 | List maleList = new List(); 468 | var male = hSceneProcTraverse.Field("male").GetValue(); 469 | if (male != null) 470 | maleList.Add(male); 471 | 472 | var male1 = hSceneProcTraverse.Field("male1").GetValue(); 473 | if (male1 != null) 474 | maleList.Add(male1); 475 | 476 | if (maleList.IsNullOrEmpty()) 477 | return; 478 | 479 | CoreGame.InitializeDanAgents(maleList, danOptions); 480 | loadingCharacter = false; 481 | } 482 | 483 | } 484 | } -------------------------------------------------------------------------------- /KK_BetterPenetration/KK_BetterPenetration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Collections.Generic; 4 | using BepInEx; 5 | using BepInEx.Bootstrap; 6 | using BepInEx.Configuration; 7 | using HarmonyLib; 8 | using Core_BetterPenetration; 9 | using UnityEngine; 10 | 11 | namespace KK_BetterPenetration 12 | { 13 | [BepInPlugin(GUID, "KK Better Penetration", VERSION)] 14 | [BepInDependency("com.deathweasel.bepinex.uncensorselector", "3.11.1")] 15 | [BepInDependency("com.rclcircuit.bepinex.modboneimplantor", "1.1.1")] 16 | [BepInProcess(KKAPI.KoikatuAPI.GameProcessName)] 17 | [BepInProcess(KKAPI.KoikatuAPI.VRProcessName)] 18 | [BepInProcess(KKAPI.KoikatuAPI.GameProcessNameSteam)] 19 | [BepInProcess(KKAPI.KoikatuAPI.VRProcessNameSteam)] 20 | public class KK_BetterPenetration : BaseUnityPlugin 21 | { 22 | public static KK_BetterPenetration instance; 23 | 24 | public const string GUID = "animal42069.KKbetterpenetration"; 25 | public const string VERSION = Constants.Version; 26 | internal const int MaleLimit = 2; 27 | internal const int FemaleLimit = 2; 28 | 29 | internal static readonly List frontOffsets = new List { -0.04f, 0.04f, 0.06f }; 30 | internal static readonly List backOffsets = new List { -0.04f, 0.02f, 0f }; 31 | internal static readonly List frontPointsInward = new List { false, false, false, }; 32 | internal static readonly List backPointsInward = new List { false, true, true }; 33 | 34 | internal static readonly ConfigEntry[] _danColliderLengthScale = new ConfigEntry[MaleLimit]; 35 | internal static readonly ConfigEntry[] _danColliderRadiusScale = new ConfigEntry[MaleLimit]; 36 | internal static readonly ConfigEntry[] _danLengthSquishFactor = new ConfigEntry[MaleLimit]; 37 | internal static readonly ConfigEntry[] _danGirthSquishFactor = new ConfigEntry[MaleLimit]; 38 | internal static readonly ConfigEntry[] _danSquishThreshold = new ConfigEntry[MaleLimit]; 39 | internal static readonly ConfigEntry[] _danSquishOralGirth = new ConfigEntry[MaleLimit]; 40 | internal static readonly ConfigEntry[] _simplifyVaginal = new ConfigEntry[MaleLimit]; 41 | internal static readonly ConfigEntry[] _simplifyOral = new ConfigEntry[MaleLimit]; 42 | internal static readonly ConfigEntry[] _simplifyAnal = new ConfigEntry[MaleLimit]; 43 | internal static readonly ConfigEntry[] _rotateTamaWithShaft = new ConfigEntry[MaleLimit]; 44 | internal static readonly ConfigEntry[] _maxCorrection = new ConfigEntry[MaleLimit]; 45 | internal static readonly ConfigEntry[] _limitCorrection = new ConfigEntry[MaleLimit]; 46 | 47 | internal static ConfigEntry _clippingDepth; 48 | internal static ConfigEntry _kokanOffset; 49 | internal static ConfigEntry _innerKokanOffset; 50 | internal static ConfigEntry _mouthOffset; 51 | internal static ConfigEntry _innerMouthOffset; 52 | internal static ConfigEntry _enableOralPushPull; 53 | internal static ConfigEntry _maxOralPush; 54 | internal static ConfigEntry _maxOralPull; 55 | internal static ConfigEntry _oralPullRate; 56 | internal static ConfigEntry _oralReturnRate; 57 | 58 | internal static readonly ConfigEntry[] _frontCollisionOffset = new ConfigEntry[frontOffsets.Count]; 59 | internal static readonly ConfigEntry[] _backCollisionOffset = new ConfigEntry[backOffsets.Count]; 60 | 61 | internal static Harmony harmony; 62 | 63 | //In VR, type "VRHScene" is used instead of "HSceneProc". Use type "BaseLoader" as the type for "hSceneProc" since it's inherited by both "HSceneProc" and "VRHScene". 64 | internal static BaseLoader hSceneProc; 65 | internal static Traverse hSceneProcTraverse; 66 | internal static bool hSceneStarted = false; 67 | internal static bool inHScene = false; 68 | internal static bool loadingCharacter = false; 69 | internal static bool changeAnimationStep1 = false; 70 | internal static bool changeAnimationStep2 = false; 71 | internal static int changeAnimationCount = 0; 72 | 73 | internal void Awake() 74 | { 75 | instance = this; 76 | 77 | for (int maleNum = 0; maleNum < MaleLimit; maleNum++) 78 | { 79 | (_danColliderLengthScale[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Penis Collider: Length Scale", 1.0f, new ConfigDescription("How much to scale collider length", new AcceptableValueRange(0.5f, 1.5f)))).SettingChanged += (s, e) => 80 | { UpdateDanColliders(); }; 81 | (_danColliderRadiusScale[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Penis Collider: Radius Scale", 1.0f, new ConfigDescription("How much to scale collider radius", new AcceptableValueRange(0.5f, 1.5f)))).SettingChanged += (s, e) => 82 | { UpdateDanColliders(); }; 83 | (_danLengthSquishFactor[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Penis: Squish Length Factor", 0.6f, new ConfigDescription("How much the length of the penis squishes after it has passed the squish threshold", new AcceptableValueRange(0, 1)))).SettingChanged += (s, e) => 84 | { UpdateDanOptions(); }; 85 | (_danGirthSquishFactor[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Penis: Squish Girth Factor", 0.2f, new ConfigDescription("How much the girth of the penis squishes after it has passed the squish threshold", new AcceptableValueRange(0, 1)))).SettingChanged += (s, e) => 86 | { UpdateDanOptions(); }; 87 | (_danSquishThreshold[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Penis: Squish Threshold", 0.2f, new ConfigDescription("Allows the penis to begin squishing (shorten length increase girth) after this amount of the penis has penetrated.", new AcceptableValueRange(0, 1)))).SettingChanged += (s, e) => 88 | { UpdateDanOptions(); }; 89 | (_danSquishOralGirth[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Penis: Squish Oral Girth", false, "Allows the penis to squish (increase girth) during oral.")).SettingChanged += (s, e) => 90 | { UpdateDanOptions(); }; 91 | (_simplifyVaginal[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Simplify Penetration Calculation", false, "Simplifys penetration calclation by always having it target the same internal point. Only valid for BP penis uncensors.")).SettingChanged += (s, e) => 92 | { UpdateDanOptions(); }; 93 | (_simplifyOral[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Simplify Oral Calculation", false, "Simplifys oral penetration calclation by always having it target the same internal point. Only valid for BP penis uncensors.")).SettingChanged += (s, e) => 94 | { UpdateDanOptions(); }; 95 | (_simplifyAnal[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Simplify Anal Calculation", false, "Simplifys anal penetration calclation by always having it target the same internal point. Only valid for BP penis uncensors.")).SettingChanged += (s, e) => 96 | { UpdateDanOptions(); }; 97 | (_rotateTamaWithShaft[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Rotate Balls with Shaft", true, "If enabled, the base of the balls will be locked to the base of the shaft")).SettingChanged += (s, e) => 98 | { UpdateDanOptions(); }; 99 | (_limitCorrection[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Limit Penis Movement", false, "Limit the penis from moving laterally too much from frame to frame.")).SettingChanged += (s, e) => 100 | { UpdateDanOptions(); }; 101 | (_maxCorrection[maleNum] = Config.Bind("Male " + (maleNum + 1) + " Options", "Limit Penis Amount", 0.2f, "Amount of movement to limit the penis to. Smaller values result in smoother animations, but can cause clipping.")).SettingChanged += (s, e) => 102 | { UpdateDanOptions(); }; 103 | } 104 | 105 | (_clippingDepth = Config.Bind("Female Options", "Clipping Depth", 0.02f, "Set how close to body surface to limit penis for clipping purposes. Smaller values will result in more clipping through the body, larger values will make the shaft wander further away from the intended penetration point.")).SettingChanged += (s, e) => 106 | { UpdateCollisionOptions(); }; 107 | for (int offset = 0; offset < frontOffsets.Count; offset++) 108 | (_frontCollisionOffset[offset] = Config.Bind("Female Options", "Clipping Offset: Front Collision " + offset, frontOffsets[offset], "Individual offset on colision point, to improve clipping")).SettingChanged += (s, e) => 109 | { UpdateCollisionOptions(); }; 110 | for (int offset = 0; offset < backOffsets.Count; offset++) 111 | (_backCollisionOffset[offset] = Config.Bind("Female Options", "Clipping Offset: Back Collision " + offset, backOffsets[offset], "Individual offset on colision point, to improve clipping")).SettingChanged += (s, e) => 112 | { UpdateCollisionOptions(); }; 113 | (_kokanOffset = Config.Bind("Female Options", "Target Offset: Vagina Target", 0.0f, "Offset of the vagina target")).SettingChanged += (s, e) => 114 | { UpdateCollisionOptions(); }; 115 | (_innerKokanOffset = Config.Bind("Female Options", "Target Offset: Inner Vagina Target", 0.0f, "Offset of the simplified inner vagina target")).SettingChanged += (s, e) => 116 | { UpdateCollisionOptions(); }; 117 | (_mouthOffset = Config.Bind("Female Options", "Target Offset: Mouth Target", 0.0f, "Offset of the mouth target")).SettingChanged += (s, e) => 118 | { UpdateCollisionOptions(); }; 119 | (_innerMouthOffset = Config.Bind("Female Options", "Target Offset: Inner Mouth Target", 0.0f, "Offset of the simplified inner mouth target")).SettingChanged += (s, e) => 120 | { UpdateCollisionOptions(); }; 121 | (_enableOralPushPull = Config.Bind("Female Options", "Oral Push/Pull: Enable", true, "Enable mouth push/pull during penetration")).SettingChanged += (s, e) => 122 | { UpdateCollisionOptions(); }; 123 | (_maxOralPush = Config.Bind("Female Options", "Oral Push/Pull: Max Push", 0.002f, "Maximum amount to push the mouth inwards during penetration")).SettingChanged += (s, e) => 124 | { UpdateCollisionOptions(); }; 125 | (_maxOralPull = Config.Bind("Female Options", "Oral Push/Pull: Max Pull", 0.006f, "Maximum amount to pull the mouth outwards during penetration")).SettingChanged += (s, e) => 126 | { UpdateCollisionOptions(); }; 127 | (_oralPullRate = Config.Bind("Female Options", "Oral Push/Pull: Push/Pull Rate", 18f, "How quickly to push or pull the mouth during penetration")).SettingChanged += (s, e) => 128 | { UpdateCollisionOptions(); }; 129 | (_oralReturnRate = Config.Bind("Female Options", "Oral Push/Pull: Return Rate", 0.03f, "How quickly the mouth returns to its original shape when there is no penetration")).SettingChanged += (s, e) => 130 | { UpdateCollisionOptions(); }; 131 | 132 | harmony = new Harmony("KK_BetterPenetration"); 133 | harmony.PatchAll(GetType()); 134 | 135 | Type VRHSceneType = Type.GetType("VRHScene, Assembly-CSharp"); 136 | if (VRHSceneType != null) 137 | { 138 | harmony.Patch(VRHSceneType.GetMethod("Start", AccessTools.all), postfix: new HarmonyMethod(GetType().GetMethod(nameof(HScene_PostStart), AccessTools.all))); 139 | harmony.Patch(VRHSceneType.GetMethod("EndProc", AccessTools.all), prefix: new HarmonyMethod(GetType().GetMethod(nameof(HSceneProc_EndProc), AccessTools.all))); 140 | harmony.Patch(VRHSceneType.GetMethod("Update", AccessTools.all), postfix: new HarmonyMethod(GetType().GetMethod(nameof(HScene_PostUpdate), AccessTools.all))); 141 | harmony.Patch(VRHSceneType.GetMethod("ChangeAnimator", AccessTools.all), prefix: new HarmonyMethod(GetType().GetMethod(nameof(HSceneProc_PreChangeAnimator), AccessTools.all))); 142 | } 143 | 144 | Chainloader.PluginInfos.TryGetValue("com.deathweasel.bepinex.uncensorselector", out PluginInfo pluginInfo); 145 | if (pluginInfo == null || pluginInfo.Instance == null) 146 | return; 147 | 148 | Type uncensorSelectorControllerType = pluginInfo.Instance.GetType().GetNestedType("UncensorSelectorController", AccessTools.all); 149 | if (uncensorSelectorControllerType == null) 150 | return; 151 | 152 | MethodInfo uncensorSelectorReloadCharacterBody = AccessTools.Method(uncensorSelectorControllerType, "ReloadCharacterBody"); 153 | if (uncensorSelectorReloadCharacterBody == null) 154 | return; 155 | 156 | harmony.Patch(uncensorSelectorReloadCharacterBody, prefix: new HarmonyMethod(GetType(), "BeforeCharacterReload"), postfix: new HarmonyMethod(GetType(), "AfterCharacterReload")); 157 | UnityEngine.Debug.Log("KK_BetterPenetration: patched UncensorSelector::ReloadCharacterBody correctly"); 158 | 159 | MethodInfo uncensorSelectorReloadCharacterPenis = AccessTools.Method(uncensorSelectorControllerType, "ReloadCharacterPenis"); 160 | if (uncensorSelectorReloadCharacterPenis == null) 161 | return; 162 | 163 | harmony.Patch(uncensorSelectorReloadCharacterPenis, prefix: new HarmonyMethod(GetType(), "BeforeDanCharacterReload"), postfix: new HarmonyMethod(GetType(), "AfterDanCharacterReload")); 164 | UnityEngine.Debug.Log("KK_BetterPenetration: patched UncensorSelector::ReloadCharacterPenis patched"); 165 | } 166 | 167 | [HarmonyPostfix, HarmonyPatch(typeof(ChaControl), "SetHideHairAccessory")] 168 | internal static void ChaControl_SetHideHairAccessory(ChaControl __instance) 169 | { 170 | Tools.RemoveCollidersFromCoordinate(__instance); 171 | } 172 | 173 | [HarmonyPostfix, HarmonyPatch(typeof(ChaControl), "UpdateSiru")] 174 | internal static void ChaControl_UpdateSiru(ChaControl __instance, bool forceChange) 175 | { 176 | if (!forceChange) 177 | return; 178 | 179 | Tools.RemoveCollidersFromCoordinate(__instance); 180 | } 181 | 182 | [HarmonyPostfix, HarmonyPatch(typeof(HSceneProc), "Start")] 183 | internal static void HScene_PostStart(BaseLoader __instance) 184 | { 185 | hSceneProc = __instance; 186 | hSceneProcTraverse = Traverse.Create(__instance); 187 | 188 | hSceneStarted = true; 189 | inHScene = false; 190 | } 191 | 192 | [HarmonyPrefix, HarmonyPatch(typeof(HSceneProc), "EndProc")] 193 | internal static void HSceneProc_EndProc() 194 | { 195 | CoreGame.SetAgentsBPBoneWeights(0f); 196 | CoreGame.OnEndScene(); 197 | 198 | inHScene = false; 199 | 200 | if (hSceneProc == null) 201 | return; 202 | 203 | var lookDan = hSceneProcTraverse.Field("lookDan").Value; 204 | if (lookDan != null) 205 | lookDan.transLookAtNull = null; 206 | 207 | hSceneProcTraverse = null; 208 | hSceneProc = null; 209 | } 210 | 211 | [HarmonyPostfix, HarmonyPatch(typeof(HSceneProc), "Update")] 212 | internal static void HScene_PostUpdate() 213 | { 214 | if (!hSceneStarted || inHScene || hSceneProc == null || !hSceneProc.enabled || hSceneProcTraverse == null) 215 | return; 216 | 217 | List danOptions = PopulateDanOptionsList(); 218 | List collisionOptions = PopulateCollisionOptionsList(); 219 | 220 | var lstFemale = hSceneProcTraverse.Field("lstFemale").GetValue>(); 221 | if (lstFemale == null || lstFemale.Count == 0) 222 | return; 223 | 224 | List femaleList = new List(); 225 | foreach (var female in lstFemale) 226 | if (female != null) 227 | femaleList.Add(female); 228 | 229 | List maleList = new List(); 230 | var male = hSceneProcTraverse.Field("male").GetValue(); 231 | if (male != null) 232 | maleList.Add(male); 233 | 234 | var male1 = hSceneProcTraverse.Field("male1").GetValue(); 235 | if (male1 != null) 236 | maleList.Add(male1); 237 | 238 | if (maleList.IsNullOrEmpty()) 239 | return; 240 | 241 | CoreGame.InitializeAgents(maleList, femaleList, danOptions, collisionOptions); 242 | CoreGame.SetAgentsBPBoneWeights(1f); 243 | inHScene = true; 244 | hSceneStarted = false; 245 | } 246 | 247 | [HarmonyPrefix, HarmonyPatch(typeof(HSceneProc), "ChangeAnimator")] 248 | internal static void HSceneProc_PreChangeAnimator(HSceneProc.AnimationListInfo _nextAinmInfo) 249 | { 250 | if (!inHScene || _nextAinmInfo == null || _nextAinmInfo.pathFemaleBase.file == null) 251 | return; 252 | 253 | CoreGame.OnChangeAnimation(_nextAinmInfo.pathFemaleBase.file); 254 | } 255 | 256 | [HarmonyPostfix, HarmonyPatch(typeof(HSceneProc), "ChangeAnimator")] 257 | internal static void HSceneProc_PostChangeAnimator() 258 | { 259 | if (!inHScene) 260 | return; 261 | 262 | changeAnimationStep1 = true; 263 | } 264 | 265 | [HarmonyPostfix, HarmonyPatch(typeof(ChaControl), "setPlay")] 266 | internal static void ChaControl_PostSetPlay() 267 | { 268 | changeAnimationStep1 = true; 269 | } 270 | 271 | [HarmonyPostfix, HarmonyPatch(typeof(Lookat_dan), "Release")] 272 | internal static void H_Lookat_dan_PostRelease(Lookat_dan __instance) 273 | { 274 | if (!inHScene || loadingCharacter || __instance.strPlayMotion == null || __instance.male == null) 275 | return; 276 | 277 | int maleNum = 0; 278 | if (__instance.male.chaID != 0) 279 | maleNum = 1; 280 | 281 | CoreGame.LookAtDanRelease(maleNum, __instance.numFemale, false); 282 | } 283 | 284 | [HarmonyPostfix, HarmonyPatch(typeof(Lookat_dan), "SetInfo")] 285 | internal static void H_Lookat_dan_PostSetInfo(Lookat_dan __instance) 286 | { 287 | if (!inHScene || loadingCharacter || __instance.strPlayMotion == null) 288 | return; 289 | 290 | int maleNum = 0; 291 | if (__instance.male != null && __instance.male.chaID != 0) 292 | maleNum = 1; 293 | 294 | CoreGame.LookAtDanSetup(__instance.transLookAtNull, __instance.strPlayMotion, __instance.bTopStick, maleNum, __instance.numFemale, false, true); 295 | } 296 | 297 | [HarmonyPostfix, HarmonyPatch(typeof(Lookat_dan), "LateUpdate")] 298 | internal static void Lookat_dan_PostLateUpdate(Lookat_dan __instance) 299 | { 300 | if (!inHScene || loadingCharacter || __instance.strPlayMotion == null || __instance.male == null) 301 | return; 302 | 303 | if (changeAnimationStep1) 304 | { 305 | CoreGame.ResetParticles(); 306 | CoreGame.EnableParticles(false); 307 | changeAnimationStep1 = false; 308 | changeAnimationStep2 = true; 309 | changeAnimationCount = 0; 310 | } 311 | 312 | if (changeAnimationStep2 && ++changeAnimationCount > 3) 313 | { 314 | CoreGame.EnableParticles(true); 315 | changeAnimationStep2 = false; 316 | } 317 | 318 | if (__instance.male.chaID != 0) 319 | return; 320 | 321 | CoreGame.LookAtDanUpdate(__instance.transLookAtNull, __instance.strPlayMotion, __instance.bTopStick, false, 0, __instance.numFemale, false, true); 322 | 323 | var lstFemale = hSceneProcTraverse.Field("lstFemale").GetValue>(); 324 | if (lstFemale == null || lstFemale.Count == 0) 325 | return; 326 | 327 | List femaleList = new List(); 328 | foreach (var female in lstFemale) 329 | if (female != null) 330 | femaleList.Add(female); 331 | } 332 | 333 | internal static void UpdateDanColliders() 334 | { 335 | if (!inHScene) 336 | return; 337 | 338 | for (int index = 0; index < MaleLimit; index++) 339 | CoreGame.UpdateDanCollider(index, _danColliderRadiusScale[index].Value, _danColliderLengthScale[index].Value); 340 | } 341 | 342 | internal static void UpdateDanOptions() 343 | { 344 | if (!inHScene) 345 | return; 346 | 347 | for (int maleNum = 0; maleNum < MaleLimit; maleNum++) 348 | CoreGame.UpdateDanOptions(maleNum, _danLengthSquishFactor[maleNum].Value, _danGirthSquishFactor[maleNum].Value, 349 | _danSquishThreshold[maleNum].Value, _danSquishOralGirth[maleNum].Value, 350 | _simplifyVaginal[maleNum].Value, _simplifyOral[maleNum].Value, _rotateTamaWithShaft[maleNum].Value, 351 | _limitCorrection[maleNum].Value, _maxCorrection[maleNum].Value); 352 | } 353 | 354 | internal static void UpdateCollisionOptions() 355 | { 356 | if (!inHScene) 357 | return; 358 | 359 | List collisionOptions = PopulateCollisionOptionsList(); 360 | for (int index = 0; index < FemaleLimit; index++) 361 | CoreGame.UpdateCollisionOptions(index, collisionOptions[index]); 362 | } 363 | 364 | internal static List PopulateDanOptionsList() 365 | { 366 | List danOptions = new List(); 367 | 368 | for (int maleNum = 0; maleNum < MaleLimit; maleNum++) 369 | { 370 | danOptions.Add(new DanOptions(_danColliderRadiusScale[maleNum].Value, _danColliderLengthScale[maleNum].Value, 371 | _danLengthSquishFactor[maleNum].Value, _danGirthSquishFactor[maleNum].Value, _danSquishThreshold[maleNum].Value, _danSquishOralGirth[maleNum].Value, 372 | _simplifyVaginal[maleNum].Value, _simplifyOral[maleNum].Value, _simplifyAnal[maleNum].Value, _rotateTamaWithShaft[maleNum].Value, 373 | _limitCorrection[maleNum].Value, _maxCorrection[maleNum].Value)); 374 | } 375 | 376 | return danOptions; 377 | } 378 | 379 | internal static List PopulateCollisionOptionsList() 380 | { 381 | List collisionOptions = new List(); 382 | 383 | List frontInfo = new List(); 384 | for (int info = 0; info < BoneNames.frontCollisionList.Count; info++) 385 | frontInfo.Add(new CollisionPointInfo(BoneNames.frontCollisionList[info], _frontCollisionOffset[info].Value, frontPointsInward[info])); 386 | 387 | List backInfo = new List(); 388 | for (int info = 0; info < BoneNames.backCollisionList.Count; info++) 389 | backInfo.Add(new CollisionPointInfo(BoneNames.backCollisionList[info], _backCollisionOffset[info].Value, backPointsInward[info])); 390 | 391 | for (int femaleNum = 0; femaleNum < FemaleLimit; femaleNum++) 392 | { 393 | collisionOptions.Add(new CollisionOptions(_kokanOffset.Value, _innerKokanOffset.Value, _mouthOffset.Value, _innerMouthOffset.Value, 394 | false, 0, 0, 0, 395 | false, Vector3.zero, Vector3.zero, 396 | _clippingDepth.Value, frontInfo, backInfo, 397 | false, 0, 0, 0, 0, 398 | _enableOralPushPull.Value, _maxOralPush.Value, _maxOralPull.Value, _oralPullRate.Value, _oralReturnRate.Value, 399 | false, 0, 0, 0, 0)); 400 | } 401 | 402 | return collisionOptions; 403 | } 404 | 405 | internal static void BeforeCharacterReload(object __instance) 406 | { 407 | if (!inHScene) 408 | return; 409 | 410 | ChaControl chaControl = (ChaControl)__instance.GetPrivateProperty("ChaControl"); 411 | if (chaControl == null || chaControl.sex == 0) 412 | return; 413 | 414 | loadingCharacter = true; 415 | CoreGame.SetDansHaveNewTarget(true); 416 | } 417 | 418 | internal static void AfterCharacterReload(object __instance) 419 | { 420 | ChaControl chaControl = (ChaControl)__instance.GetPrivateProperty("ChaControl"); 421 | if (chaControl == null) 422 | return; 423 | 424 | CoreGame.SetBPBoneWeights(chaControl, inHScene ? 1f : 0f); 425 | 426 | if (chaControl.sex == 0 || !inHScene || hSceneProc == null || hSceneProcTraverse == null) 427 | return; 428 | 429 | var lstFemale = hSceneProcTraverse.Field("lstFemale").GetValue>(); 430 | if (lstFemale == null || lstFemale.Count == 0) 431 | return; 432 | 433 | List femaleList = new List(); 434 | foreach (var female in lstFemale) 435 | if (female != null) 436 | femaleList.Add(female); 437 | 438 | List collisionOptions = PopulateCollisionOptionsList(); 439 | CoreGame.InitializeCollisionAgents(femaleList, collisionOptions); 440 | loadingCharacter = false; 441 | } 442 | 443 | internal static void BeforeDanCharacterReload(object __instance) 444 | { 445 | if (!inHScene) 446 | return; 447 | 448 | ChaControl chaControl = (ChaControl)__instance.GetPrivateProperty("ChaControl"); 449 | if (chaControl == null || chaControl.sex != 0) 450 | return; 451 | 452 | loadingCharacter = true; 453 | CoreGame.SetDansHaveNewTarget(true); 454 | CoreGame.ClearDanAgents(); 455 | } 456 | 457 | internal static void AfterDanCharacterReload(object __instance) 458 | { 459 | if (!inHScene || hSceneProc == null) 460 | return; 461 | 462 | ChaControl chaControl = (ChaControl)__instance.GetPrivateProperty("ChaControl"); 463 | if (chaControl == null || chaControl.sex != 0) 464 | return; 465 | 466 | List danOptions = PopulateDanOptionsList(); 467 | List maleList = new List(); 468 | var male = hSceneProcTraverse.Field("male").GetValue(); 469 | if (male != null) 470 | maleList.Add(male); 471 | 472 | var male1 = hSceneProcTraverse.Field("male1").GetValue(); 473 | if (male1 != null) 474 | maleList.Add(male1); 475 | 476 | if (maleList.IsNullOrEmpty()) 477 | return; 478 | 479 | CoreGame.InitializeDanAgents(maleList, danOptions); 480 | loadingCharacter = false; 481 | } 482 | 483 | } 484 | } --------------------------------------------------------------------------------