• Hi Guest!

    We are extremely excited to announce the release of our first Beta1.1 and the first release of our Public AddonKit!
    To participate in the Beta, a subscription to the Entertainer or Creator Tier is required. For access to the Public AddonKit you must be a Creator tier member. Once subscribed, download instructions can be found here.

    Click here for information and guides regarding the VaM2 beta. Join our Discord server for more announcements and community discussion about VaM2.
  • Hi Guest!

    VaM2 Resource Categories have now been added to the Hub! For information on posting VaM2 resources and details about VaM2 related changes to our Community Forums, please see our official announcement here.

DiviningReach for DiviningRog or DiviningForeskin or BodyLanguage

Dlesser

New member
Joined
Mar 28, 2025
Messages
28
Reactions
19
I can't publish my first script, but I want to try to share it at least here.

This script works in tandem with DiviningRog or DiviningForeskin or BodyLanguage. It directs the female hips to the male atom's penis. The controls are simple. It can be used on both woman and male atoms. In the plugin, select the woman and men, and enjoy. The license is free, CC BY. I would like the VAM community to develop further, so you can use this plugin in any form, both in paid and free content.

If you want, you can use the code for your own purposes, modify it or do whatever you want.

Enjoy.

Download link:
https://mega.nz/folder/hrAx2JoD#e4AtWJN7rokJsvJhEjhh7A

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using SimpleJSON;

// This script aligns female hip and thigh controllers to male penis controllers in Virt-A-Mate (VAM).
// It dynamically adjusts the position and rotation of female hips and thighs based on the penis position,
// with a UI to select atoms, adjust settings, and enable alignment.
public class DeviningReach : MVRScript
{
// JSON storables for UI elements
private JSONStorableBool enableAlignment; // Toggle to enable/disable alignment
private JSONStorableString statusText; // Text field to display status messages
private JSONStorableFloat attractionStrength; // Slider for thigh attraction strength
private JSONStorableFloat rotationSpeed; // Slider for hip rotation speed
private JSONStorableFloat pelvisOffset; // Slider for pelvis position offset
private JSONStorableStringChooser maleAtomChooser; // Popup to select male atom
private JSONStorableStringChooser femaleAtomChooser; // Popup to select female atom
private JSONStorableString penisBaseID; // ID for penis base controller
private JSONStorableString penisMidID; // ID for penis mid controller
private JSONStorableString penisTipID; // ID for penis tip controller
private JSONStorableString hipID; // ID for female hip controller
private JSONStorableString pelvisID; // ID for female pelvis controller
private JSONStorableString leftThighID; // ID for left thigh controller
private JSONStorableString rightThighID; // ID for right thigh controller

// Runtime variables
private Atom maleAtom; // Selected male atom
private Atom femaleAtom; // Selected female atom
private bool hasValidTargets = false; // True if all required controllers are found
private bool isInitialized = false; // True after initialization is complete
private float initializationDelay = 1f; // Delay before initializing controllers
private Dictionary<string, FreeControllerV3> controllers = new Dictionary<string, FreeControllerV3>(); // Stores all controllers
private Dictionary<string, Atom> availableAtoms = new Dictionary<string, Atom>(); // Stores all Person atoms in the scene
private Vector3 vaginaPosition; // Position of the female hip (used as vagina reference)
private Vector3 vaginaDirection; // Forward direction of the female hip
private float updateLogTimer = 2f; // Timer for periodic logging
private Vector3 lastLeftThighPos; // Last position of left thigh for smooth movement
private Vector3 lastRightThighPos; // Last position of right thigh for smooth movement
private Quaternion lastLeftThighRot; // Last rotation of left thigh for smooth movement
private Quaternion lastRightThighRot; // Last rotation of right thigh for smooth movement

// Constants for controller IDs
private readonly string[] maleControllerIds = { "penisBaseControl", "penisMidControl", "penisTipControl" }; // Male controller IDs
private readonly string[] femaleControllerIds = { "hipControl", "pelvisControl", "lThighControl", "rThighControl" }; // Female controller IDs
private const string PluginName = "DeviningReach"; // Plugin name for logging
private const float MinPositionDelta = 0.001f; // Minimum position change to prevent jittering
private const float MinRotationDelta = 0.1f; // Minimum rotation change (in degrees) to prevent jittering

// Initializes the plugin, called when the plugin is loaded or reloaded
public override void Init()
{
try
{
SuperController.LogMessage($"{PluginName}: Starting Init");
// Reset atom choosers to prevent auto-initialization on reload
if (maleAtomChooser != null) maleAtomChooser.val = "None";
if (femaleAtomChooser != null) femaleAtomChooser.val = "None";
maleAtom = null;
femaleAtom = null;
isInitialized = false;
hasValidTargets = false;
controllers.Clear();
FindAllPersonAtoms(); // Find all Person atoms in the scene
CreateUI(); // Create the UI elements
SuperController.LogMessage($"{PluginName}: Init completed");
}
catch (Exception e)
{
SuperController.LogError($"{PluginName}: Init error: {e}");
}
}

// Creates the UI elements for the plugin (toggles, sliders, popups, buttons)
private void CreateUI()
{
try
{
// Create toggle to enable/disable alignment
enableAlignment = new JSONStorableBool("Enable Alignment", true);
RegisterBool(enableAlignment);
CreateToggle(enableAlignment);

// Create status text field
statusText = new JSONStorableString("Status", "Select one male and one female atom");
RegisterString(statusText);
CreateTextField(statusText).height = 100f;

// Populate atom names for choosers
List<string> atomNames = new List<string> { "None" };
atomNames.AddRange(availableAtoms.Keys);

// Create male atom chooser
maleAtomChooser = new JSONStorableStringChooser("Male Atom", atomNames, "None", "Male Atom",
new JSONStorableStringChooser.SetStringCallback(UpdateAtomSelection));
RegisterStringChooser(maleAtomChooser);
CreatePopup(maleAtomChooser);

// Create female atom chooser
femaleAtomChooser = new JSONStorableStringChooser("Female Atom", atomNames, "None", "Female Atom",
new JSONStorableStringChooser.SetStringCallback(UpdateAtomSelection));
RegisterStringChooser(femaleAtomChooser);
CreatePopup(femaleAtomChooser);

// Create attraction strength slider (controls thigh movement intensity)
attractionStrength = new JSONStorableFloat("Attraction Strength", 0.5f, 0.1f, 1.0f);
RegisterFloat(attractionStrength);
CreateSlider(attractionStrength);

// Create rotation speed slider (controls hip rotation speed)
rotationSpeed = new JSONStorableFloat("Rotation Speed", 2.0f, 0.1f, 10f);
RegisterFloat(rotationSpeed);
CreateSlider(rotationSpeed);

// Create pelvis offset slider (controls pelvis position offset from hip)
pelvisOffset = new JSONStorableFloat("Pelvis Offset", 0.1f, 0.05f, 0.2f);
RegisterFloat(pelvisOffset);
CreateSlider(pelvisOffset);

// Initialize controller ID storables
penisBaseID = new JSONStorableString("Penis Base ID", "penisBaseControl");
RegisterString(penisBaseID);
penisMidID = new JSONStorableString("Penis Mid ID", "penisMidControl");
RegisterString(penisMidID);
penisTipID = new JSONStorableString("Penis Tip ID", "penisTipControl");
RegisterString(penisTipID);
hipID = new JSONStorableString("Hip ID", "hipControl");
RegisterString(hipID);
pelvisID = new JSONStorableString("Pelvis ID", "pelvisControl");
RegisterString(pelvisID);
leftThighID = new JSONStorableString("Left Thigh ID", "lThighControl");
RegisterString(leftThighID);
rightThighID = new JSONStorableString("Right Thigh ID", "rThighControl");
RegisterString(rightThighID);

// Create button to enable all female control points
JSONStorableAction enableAllPoints = new JSONStorableAction("Enable All Points", EnableAllPoints);
RegisterAction(enableAllPoints);
var enableButton = CreateButton("Enable All Points");
enableButton.button.onClick.AddListener(() => { EnableAllPoints(); });
SuperController.LogMessage($"{PluginName}: CreateUI - Enable All Points button registered");
}
catch (Exception e)
{
SuperController.LogError($"{PluginName}: CreateUI error: {e}");
}
}

// Updates atom selection when male or female atom is chosen in UI
private void UpdateAtomSelection(string val)
{
try
{
SuperController.LogMessage($"{PluginName}: UpdateAtomSelection called with value: {val}");
// Set male and female atoms based on chooser values
maleAtom = maleAtomChooser.val != "None" && availableAtoms.ContainsKey(maleAtomChooser.val) ? availableAtoms[maleAtomChooser.val] : null;
femaleAtom = femaleAtomChooser.val != "None" && availableAtoms.ContainsKey(femaleAtomChooser.val) ? availableAtoms[femaleAtomChooser.val] : null;

// Prevent selecting the same atom for both male and female
if (maleAtom != null && femaleAtom != null && maleAtom == femaleAtom)
{
SuperController.LogError($"{PluginName}: Cannot select the same atom for both male and female!");
statusText.val = "Error: Cannot select the same atom for both male and female!";
femaleAtom = null;
femaleAtomChooser.val = "None";
}

// Reset initialization to trigger controller setup
isInitialized = false;
initializationDelay = 1f;
SuperController.LogMessage($"{PluginName}: Atom selection updated: Male = {maleAtom?.uid ?? "None"}, Female = {femaleAtom?.uid ?? "None"}");
}
catch (Exception e)
{
SuperController.LogError($"{PluginName}: UpdateAtomSelection error: {e}");
}
}

// Finds all Person atoms in the scene
private void FindAllPersonAtoms()
{
try
{
SuperController.LogMessage($"{PluginName}: Finding all Person atoms");
availableAtoms.Clear();
foreach (Atom atom in SuperController.singleton.GetAtoms())
{
if (atom.type == "Person")
{
availableAtoms[atom.uid] = atom;
SuperController.LogMessage($"{PluginName}: Found Person atom: {atom.uid}");
}
}

if (availableAtoms.Count == 0)
{
SuperController.LogError($"{PluginName}: No Person atoms found in scene!");
statusText.val = "Error: Person atoms not found!";
}
}
catch (Exception e)
{
SuperController.LogError($"{PluginName}: FindAllPersonAtoms error: {e}");
}
}

// Initializes controllers for male and female atoms
private void InitializeControllers()
{
try
{
SuperController.LogMessage($"{PluginName}: Initializing controllers");
controllers.Clear();
hasValidTargets = false;
List<string> missingControllers = new List<string>();

// Load male controllers without changing their states
if (maleAtom != null)
{
foreach (string controlId in maleControllerIds)
{
FreeControllerV3 controller = maleAtom.GetStorableByID(controlId) as FreeControllerV3;
if (controller != null)
{
controllers[controlId] = controller;
// Log current states without modifying them
SuperController.LogMessage($"{PluginName}: Found {controlId} for {maleAtom.uid}, Current PositionState: {controller.currentPositionState}, Current RotationState: {controller.currentRotationState}, no state changes applied");
}
else
{
SuperController.LogMessage($"{PluginName}: Control {controlId} not found for {maleAtom.uid}");
missingControllers.Add(controlId);
}
}
}

// Load and set states for female controllers
if (femaleAtom != null)
{
foreach (string controlId in femaleControllerIds)
{
FreeControllerV3 controller = femaleAtom.GetStorableByID(controlId) as FreeControllerV3;
if (controller != null)
{
controllers[controlId] = controller;
if (controlId.ToLower().Contains("hip"))
{
controller.currentPositionState = FreeControllerV3.PositionState.On;
controller.currentRotationState = FreeControllerV3.RotationState.On;
SuperController.LogMessage($"{PluginName}: Initialized {controlId} for {femaleAtom.uid}, Position: On, Rotation: On");
}
else if (controlId.ToLower().Contains("pelvis"))
{
controller.currentPositionState = FreeControllerV3.PositionState.ParentLink;
controller.currentRotationState = FreeControllerV3.RotationState.ParentLink;
SuperController.LogMessage($"{PluginName}: Initialized {controlId} for {femaleAtom.uid}, Position: ParentLink, Rotation: ParentLink");
}
else
{
controller.currentPositionState = FreeControllerV3.PositionState.Comply;
controller.currentRotationState = FreeControllerV3.RotationState.Comply;
SuperController.LogMessage($"{PluginName}: Initialized {controlId} for {femaleAtom.uid}, Position: Comply, Rotation: Comply");
}
}
else
{
SuperController.LogMessage($"{PluginName}: Control {controlId} not found for {femaleAtom.uid}");
missingControllers.Add(controlId);
}
}
}

// Check if all required controllers are present
hasValidTargets = controllers.ContainsKey(penisBaseID.val) &&
controllers.ContainsKey(penisTipID.val) &&
controllers.ContainsKey(hipID.val) &&
controllers.ContainsKey(leftThighID.val) &&
controllers.ContainsKey(rightThighID.val);

if (hasValidTargets)
{
SuperController.LogMessage($"{PluginName}: InitializeControllers succeeded");
if (missingControllers.Count > 0)
{
statusText.val = $"Warning: Controllers not found: {string.Join(", ", missingControllers.ToArray())}";
}
else
{
statusText.val = "All female points successfully initialized";
}
// Initialize thigh positions and rotations
if (controllers.ContainsKey(leftThighID.val) && controllers[leftThighID.val].control != null)
{
lastLeftThighPos = controllers[leftThighID.val].control.position;
lastLeftThighRot = controllers[leftThighID.val].control.rotation;
}
if (controllers.ContainsKey(rightThighID.val) && controllers[rightThighID.val].control != null)
{
lastRightThighPos = controllers[rightThighID.val].control.position;
lastRightThighRot = controllers[rightThighID.val].control.rotation;
}
UpdatePelvisAlignment(); // Align pelvis initially
}
else
{
statusText.val = $"Error: Required controllers not found: {string.Join(", ", missingControllers.ToArray())}";
SuperController.LogError($"{PluginName}: InitializeControllers failed - minimum required controllers not found");
}
}
catch (Exception e)
{
SuperController.LogError($"{PluginName}: InitializeControllers error: {e}");
statusText.val = "Error initializing controllers";
}
}

// Main update loop, runs every frame
private void Update()
{
try
{
updateLogTimer -= Time.deltaTime;

// Initialize controllers after delay, only if atoms are selected
if (!isInitialized && initializationDelay > 0f)
{
initializationDelay -= Time.deltaTime;
if (initializationDelay <= 0f && maleAtom != null && femaleAtom != null)
{
InitializeControllers();
isInitialized = true;
SuperController.LogMessage($"{PluginName}: Delayed initialization completed");
}
return;
}

// Skip update if alignment is disabled or targets are invalid
if (!enableAlignment.val || !hasValidTargets)
{
if (updateLogTimer <= 0f)
{
SuperController.LogMessage($"{PluginName}: Update skipped - alignment disabled or invalid targets");
updateLogTimer = 2f;
}
return;
}

// Perform alignment calculations
CalculateVaginaPoints();
AdaptiveHipsAlignment();
UpdatePelvisAlignment();
UpdateThighMovement();
UpdateStatusText();

// Reset log timer
if (updateLogTimer <= 0f)
{
updateLogTimer = 2f;
}
}
catch (Exception e)
{
SuperController.LogError($"{PluginName}: Update error: {e}");
}
}

// Calculates the position and direction of the vagina based on hip controller
private void CalculateVaginaPoints()
{
if (controllers.ContainsKey(hipID.val))
{
FreeControllerV3 hip = controllers[hipID.val];
vaginaPosition = hip.control.position;
vaginaDirection = hip.control.forward;
}
}

// Aligns female hips to penis direction
private void AdaptiveHipsAlignment()
{
try
{
FreeControllerV3 penisBaseControl = controllers[penisBaseID.val];
FreeControllerV3 penisMidControl = controllers.ContainsKey(penisMidID.val) ? controllers[penisMidID.val] : null;
FreeControllerV3 penisTipControl = controllers[penisTipID.val];
FreeControllerV3 femaleHipControl = controllers[hipID.val];

if (penisBaseControl == null || penisTipControl == null || femaleHipControl == null) return;
if (penisBaseControl.control == null || penisTipControl.control == null || femaleHipControl.control == null) return;

// Calculate penis direction from base to tip
Vector3 penisDirection = (penisBaseControl.control.position - penisTipControl.control.position).normalized;

if (float.IsNaN(penisDirection.x) || float.IsNaN(penisDirection.y) || float.IsNaN(penisDirection.z))
{
SuperController.LogMessage($"{PluginName}: Invalid penis direction, skipping alignment");
return;
}

// Adjust direction using mid controller if available and aligned
if (penisMidControl != null && penisMidControl.control != null)
{
Vector3 tipToMid = (penisMidControl.control.position - penisTipControl.control.position).normalized;
Vector3 midToBase = (penisBaseControl.control.position - penisMidControl.control.position).normalized;
float alignmentAngle = Vector3.Angle(tipToMid, midToBase);
if (alignmentAngle > 10f)
{
SuperController.LogMessage($"{PluginName}: penisMidControl not aligned with base and tip, using tip-to-base direction");
}
else
{
penisDirection = (tipToMid + midToBase).normalized;
}
}

// Rotate hips to align with penis direction
Quaternion currentRotation = femaleHipControl.control.rotation;
Quaternion targetRotation = Quaternion.FromToRotation(currentRotation * Vector3.down, penisDirection) * currentRotation;
Quaternion newRotation = Quaternion.Slerp(currentRotation, targetRotation, Time.deltaTime * rotationSpeed.val);
femaleHipControl.control.rotation = newRotation;
}
catch (Exception e)
{
SuperController.LogError($"{PluginName}: AdaptiveHipsAlignment error: {e}");
}
}

// Aligns pelvis position and rotation to hip controller
private void UpdatePelvisAlignment()
{
try
{
if (!controllers.ContainsKey(hipID.val) || !controllers.ContainsKey(pelvisID.val)) return;

FreeControllerV3 hipControl = controllers[hipID.val];
FreeControllerV3 pelvisControl = controllers[pelvisID.val];

if (hipControl == null || pelvisControl == null || hipControl.control == null || pelvisControl.control == null) return;

// Position pelvis with offset from hip
Vector3 offset = hipControl.control.rotation * Vector3.up * pelvisOffset.val;
pelvisControl.control.position = hipControl.control.position + offset;
pelvisControl.control.rotation = hipControl.control.rotation;
}
catch (Exception e)
{
SuperController.LogError($"{PluginName}: UpdatePelvisAlignment error: {e}");
}
}

// Moves thighs towards penis tip with smooth interpolation
private void UpdateThighMovement()
{
try
{
// Check if all required controllers are present
if (!controllers.ContainsKey(leftThighID.val) || !controllers.ContainsKey(rightThighID.val) ||
!controllers.ContainsKey(penisTipID.val) || !controllers.ContainsKey(hipID.val))
{
return;
}

FreeControllerV3 leftThigh = controllers[leftThighID.val];
FreeControllerV3 rightThigh = controllers[rightThighID.val];
FreeControllerV3 penisTip = controllers[penisTipID.val];
FreeControllerV3 hip = controllers[hipID.val];

if (leftThigh.control == null || rightThigh.control == null || penisTip.control == null || hip.control == null)
{
return;
}

// Calculate dynamic max distance and correction speed based on attraction strength
float effectiveMaxDistance = Mathf.Lerp(0.2f, 1.0f, attractionStrength.val);
float effectiveThighCorrectionSpeed = Mathf.Lerp(1.0f, 10.0f, attractionStrength.val);

// Skip if penis is too far from vagina
float distanceToVagina = Vector3.Distance(penisTip.control.position, vaginaPosition);
if (distanceToVagina > effectiveMaxDistance)
{
return;
}

// Calculate directions to penis tip
Vector3 toPenisLeft = (penisTip.control.position - lastLeftThighPos).normalized;
Vector3 toPenisRight = (penisTip.control.position - lastRightThighPos).normalized;

if (float.IsNaN(toPenisLeft.x) || float.IsNaN(toPenisRight.x))
{
return;
}

// Calculate target positions for thighs
float thighDistance = 0.3f;
Vector3 targetLeftPos = penisTip.control.position + toPenisLeft * thighDistance;
Vector3 targetRightPos = penisTip.control.position + toPenisRight * thighDistance;

// Calculate position deltas to prevent jittering
float leftPosDelta = Vector3.Distance(leftThigh.control.position, targetLeftPos);
float rightPosDelta = Vector3.Distance(rightThigh.control.position, targetRightPos);

float smoothFactor = Time.deltaTime * effectiveThighCorrectionSpeed * 0.3f;

// Move left thigh if delta is significant
if (leftPosDelta > MinPositionDelta)
{
leftThigh.control.position = Vector3.Lerp(
leftThigh.control.position,
targetLeftPos,
smoothFactor
);
lastLeftThighPos = leftThigh.control.position;
}

// Move right thigh if delta is significant
if (rightPosDelta > MinPositionDelta)
{
rightThigh.control.position = Vector3.Lerp(
rightThigh.control.position,
targetRightPos,
smoothFactor
);
lastRightThighPos = rightThigh.control.position;
}

// Align thigh rotations with hip
Quaternion targetLeftRot = Quaternion.LookRotation(hip.control.forward, hip.control.up);
Quaternion targetRightRot = Quaternion.LookRotation(hip.control.forward, hip.control.up);

float leftRotDelta = Quaternion.Angle(leftThigh.control.rotation, targetLeftRot);
float rightRotDelta = Quaternion.Angle(rightThigh.control.rotation, targetRightRot);

// Rotate left thigh if delta is significant
if (leftRotDelta > MinRotationDelta)
{
leftThigh.control.rotation = Quaternion.Slerp(
leftThigh.control.rotation,
targetLeftRot,
smoothFactor
);
lastLeftThighRot = leftThigh.control.rotation;
}

// Rotate right thigh if delta is significant
if (rightRotDelta > MinRotationDelta)
{
rightThigh.control.rotation = Quaternion.Slerp(
rightThigh.control.rotation,
targetRightRot,
smoothFactor
);
lastRightThighRot = rightThigh.control.rotation;
}
}
catch (Exception e)
{
SuperController.LogError($"{PluginName}: UpdateThighMovement error: {e}");
}
}

// Updates the status text with alignment information
private void UpdateStatusText()
{
try
{
if (!hasValidTargets)
{
statusText.val = "Error: Required controllers not found";
return;
}

FreeControllerV3 penisTip = controllers[penisTipID.val];
FreeControllerV3 penisBase = controllers[penisBaseID.val];
FreeControllerV3 hip = controllers[hipID.val];

if (penisTip == null || penisBase == null || hip == null || penisTip.control == null || penisBase.control == null || hip.control == null)
{
statusText.val = "Error: Controllers not initialized";
return;
}

// Calculate and display alignment metrics
float distance = Vector3.Distance(penisTip.control.position, hip.control.position);
Vector3 angles = hip.control.rotation.eulerAngles;
Vector3 downDirection = hip.control.rotation * Vector3.down;
Vector3 penisDirection = (penisBase.control.position - penisTip.control.position).normalized;
float alignmentAngle = Vector3.Angle(downDirection, penisDirection);

statusText.val = $"Distance: {distance:F2} m\n" +
$"Hip Angles: X={angles.x:F1}° Y={angles.y:F1}° Z={angles.z:F1}°\n" +
$"Alignment: {alignmentAngle:F1}°\n" +
$"Attraction Strength: {attractionStrength.val:F2}\n" +
$"Male: {maleAtom?.uid ?? "None"}\n" +
$"Female: {femaleAtom?.uid ?? "None"}";
}
catch (Exception e)
{
SuperController.LogError($"{PluginName}: UpdateStatusText error: {e}");
}
}

// Enables all female control points via button click
private void EnableAllPoints()
{
try
{
SuperController.LogMessage($"{PluginName}: Enabling all female control points");
controllers.Clear();
hasValidTargets = false;
enableAlignment.val = true; // Enable alignment toggle
InitializeControllers(); // Reinitialize controllers
if (hasValidTargets)
{
SuperController.LogMessage($"{PluginName}: EnableAllPoints succeeded");
}
else
{
SuperController.LogError($"{PluginName}: EnableAllPoints failed");
}
}
catch (Exception e)
{
SuperController.LogError($"{PluginName}: EnableAllPoints error: {e}");
statusText.val = "Error enabling points";
}
}

// Called when the plugin is unloaded or reloaded, clears controllers without modifying states
public void OnDestroy()
{
try
{
SuperController.LogMessage($"{PluginName}: OnDestroy called, clearing controllers without changing states");
controllers.Clear(); // Clear controller dictionary without touching controller states
}
catch (Exception e)
{
SuperController.LogError($"{PluginName}: OnDestroy error: {e}");
}
}
}
 

Attachments

  • online-screen-recorder-2025-09-03--20-26-54.mp4
    45.8 MB
Last edited:
I also thought about adding a penis slip blocker or a rigid fixation in the vagina so that it doesn't pass through the shaders if something goes wrong. Or it comes back if your pose is poorly saved. And in principle I did it, but not yet completely. Here I had to work with vectors. And this can simply replace, for example, the deepplugin completely. I also thought about adding scales for male and female arousal with adjustments and triggers so that events could be added, but for now I'll leave it like this. I don't want to complicate it too much. But most of all I want to achieve the same mastery and automation as CheeseFX in BodyLanguage and I'm working on other plugins now. I hope I'll have more time and my plugins will be useful to Cheese.
 
Last edited:
Why can't you publish it?
When I try to upload a file to vamhub, I select my var file, but instead of the upload button appearing, I have an open button and I fall into this var file as if it were an archive. Inside it is empty, cs files are not visible. I wrote to tech support and am waiting for a response from vamhub

1756976467982.png


You can copy my code into a txt file and replace the file extension with cs, this file needs to be placed in the path of your game ... \ Virt-a-Mate_1.22.0.3 \ VaM_1.22.0.3 \ Custom \ Scripts. Then you can open it in the plugin manager in the game

And I don't mind if anyone publishes this plugin and I'll even be happy about it. It's important to me that the community has the opportunity to use this plugin for free. I don't need copyright for this file, I just like making plugins for VAM
 
Last edited:
I can only see from your Mega link, but what is there is not a VAR. A VAR has this kind of filename pattern: creator.name.version.var
When you create a VAR with the Package Builder the VAR is in the AddonPackages folder, but perhaps you zipped the contents of AddonPackagesBuilder which is just the building files?
 
I can only see from your Mega link, but what is there is not a VAR. A VAR has this kind of filename pattern: creator.name.version.var
When you create a VAR with the Package Builder the VAR is in the AddonPackages folder, but perhaps you zipped the contents of AddonPackagesBuilder which is just the building files?
 

Attachments

  • online-screen-recorder-2025-09-04--09-50-48.mp4
    11.3 MB
Maybe the file association is wrong, I can't figure it out yet. But instead of loading the file, I just fall into the archive var.
 
You are selecting a directory/folder, not the single .var file. Your directory is just named like that. You can see the dir icon with the name in the first row in your video at ~0:51. I guess at one point you extracted your var so you got the directory.

If you complete the packaging process you will have a file with the var extension, and that can be uploaded then.

And the right place is the second one in your video, AddonPackages. The builder folder is the extracted plugin in a folder, so you can check if everything is there and okay during the package building process.
 
As was said earlier, you VAR is in the AddonPackages folder, not AddonPackagesBuilder
 
Back
Top Bottom