Touches
Make one person touch, hold or kiss another on the spot, almost anywhere on the body, from Lua, with nothing animated beforehand.
A touch is made on the spot: one person looks, comes to the right place, reaches and puts a hand (or their lips) on the other, on their skin, whoever they are, then lets go. Nothing is animated beforehand. The hand finds the other body (tall or short, slim or heavy) and the touch plays on top of what both are doing, so an idle keeps breathing under it.
luaif Actor:Interaction("touchShoulder", Player) then
Game:Say(Actor, "Hey. You okay?")
end
Actor:Interaction("holdHand", Player)
Actor:Interaction("kiss", Player)
Actor:StopInteraction() -- let go now
Interaction returns false when it cannot happen, with the reason in the log: the name is unknown, or the spot is out of reach from anywhere nearby. A person who cannot reach is refused, never left with a hand in the air.
Where the toucher goes
Before anything moves, the touch is tried, whole, from several places and postures, and the cheapest that reaches wins:
- Where they stand, or a step closer (up to 35 cm, never into the other person).
- Round to face the spot: the back of a thigh is touched from behind, the side of a hip from the side. The toucher walks round the other person, not through them, and the hand waits until they are there.
- Standing, on tiptoe, crouched or kneeling: a calf, a foot or a kiss on the belly bring the toucher down; a tall person's shoulder brings them up on their toes. The feet stay planted.
Staying put beats moving, standing beats tiptoe, tiptoe beats crouching, crouching beats kneeling. After a hug or a short step in, the toucher steps back; somewhere they walked round to, they stay. Further than about half a metre away, walk them over first with Actor:moveToPerson(Player).
Picking things up
The same walk, crouch and reach take things too: the pieces a room set's catalog.ulset marks handheld (mugs, cups, glasses, bottles, phones, books, cutlery...).
luaif Actor:PickUp("Coffee_Mug") then -- the nearest one within 3 m
Game:Say(Actor, "Coffee?")
end
-- later
Actor:PutDown() -- back where it was
Something standing (a mug, a bottle, a glass) is taken round its side, the fingers closing round it; something lying flat (a phone, a book, a fork) from above. It is carried upright in front of the body, the palm turned in, until PutDown. Something wider than about 30 cm needs two hands, which is not done yet.
The touches that ship
| Name | What it does |
|---|---|
touchArm |
A hand on the upper arm. |
touchShoulder |
A hand on the shoulder, looking at the face. |
caressFace |
A hand on the cheek, stroking. |
touchChin |
Fingers under the chin. |
strokeHair |
A hand stroking the back of the head. |
patHead |
A few pats on the top of the head. |
touchNape |
A hand on the back of the neck. |
touchChest |
A hand flat on the chest. |
touchBreast, caressBreast |
A hand on a breast, resting or stroking. |
cupBreasts |
Both hands, one on each breast. |
touchBelly |
A hand stroking the belly. |
holdHips |
Both hands on the hips. |
touchButt |
A hand on a buttock (goes round to reach it). |
grabButt |
Both hands on the buttocks. |
touchLeg |
A hand on the front of the thigh, stroking. |
touchInnerThigh |
A hand on the inside of the thigh. |
touchCalf |
A hand on the calf, crouching for it. |
hug |
Steps in chest to chest, both arms around the other's back; the other hugs back. |
holdHand |
Takes the other's hand; they take it back. Face to face, one hand lies palm up and the other rests on it; side by side, the palms press together. |
kiss |
Lips to lips, heads tilted, eyes closed; the other kisses back. |
kissTender |
The same with a hand on the cheek. |
kissCheek, kissForehead, kissNeck, kissShoulder, kissBreast, kissBelly |
A kiss on that place. |
Making your own
A touch is a file in your mod's Interactions/ folder, named after it: Interactions/squeezeArm.ulinteract.
json{ "zone": "upper_arm", "side": "near", "hand": "near",
"reach": 0.7, "hold": 2.0, "release": 0.6,
"motion": "rest", "look": "target", "reaction": "look", "grip": 0.4 }
| Field | What it means |
|---|---|
zone |
Where on the other body, a name from a .ulzones file (below). |
side |
Which side of the other body: near (the side facing the toucher), far, left, right. |
hand |
Which hand touches: near (the one closer to the spot), left, right. |
part |
What touches: hand (default) or mouth. With mouth the spine, neck and head bring the lips there: a kiss. |
reach |
Seconds to bring the hand there, once the toucher is in place. |
hold |
Seconds it stays. -1 stays until Actor:StopInteraction(). |
release |
Seconds to let go. |
motion |
What the hand does while there: rest, caress (strokes along the skin), pat (taps). |
look |
Where the toucher looks: target (the spot, then the face), face, none. |
reaction |
look: the touched person looks at the hand, then at the toucher. none: they do not react. |
gap |
Centimetres between palm (or lips) and skin (default 0.8). |
grip |
How far the fingers close while there, 0 (open) to 1 (a firm grip). |
closeEyes |
true: the toucher's eyes close while there (a kiss). |
tilt |
Degrees the head tilts for a kiss on the lips, so the noses pass. |
posture |
auto (default: stand if that reaches, else tiptoe, crouch, kneel), stand, tiptoe, crouch, kneel. With no zone and no contacts, also bend (see below). |
sit |
Kneeling: how far back she sits, 0 (upright on the knees) to 1 (on her heels). Bending: how far over, 0..1 of about 80 degrees (default 1). |
A posture alone
A touch with no zone and no contacts is only a posture, taken facing the other person (or, for bend, turned away from them), hands left to the animation playing. Use it to kneel before someone, or to bend over in front of them:
json{ "posture": "kneel", "sit": 0.7, "reach": 1.2, "hold": -1, "release": 1.0, "look": "face", "reaction": "look" }
json{ "posture": "bend", "sit": 1.0, "reach": 1.2, "hold": -1, "release": 1.0, "look": "none" }
posture is kneel, crouch or bend. Kneeling lowers the whole body and folds the legs under it; the hips and thighs then stay put while the upper body keeps its idle. Bending turns the body round and folds the spine forward from the waist, legs straight. Both ship in ul_Base as kneel and bend: Actor:StartInteraction("kneel", other), then Actor:StopInteraction() to get up.
Several hands, meeting, and touches given back
A touch can use several hands (and the mouth), each with its own place: list them under contacts. This is the hug that ships:
json{
"contacts": [
{ "zone": "back_side", "hand": "left", "side": "near", "elbow": "out" },
{ "zone": "back_side", "hand": "right", "side": "near", "elbow": "out" }
],
"close": 22, "mutual": true,
"reach": 0.9, "hold": 3.0, "release": 0.8,
"motion": "rest", "look": "none", "reaction": "none"
}
And the kiss, where the lips do not go onto a zone but meet the other's halfway:
json{ "contacts": [ { "part": "mouth", "meet": true } ],
"mutual": true, "close": 26, "closeEyes": true, "tilt": 14, "gap": 0.5,
"reach": 1.0, "hold": 2.5, "release": 0.8, "look": "none", "reaction": "none" }
| Field | What it means |
|---|---|
contacts |
One entry per hand or mouth, each with its own zone, side, hand, part, elbow and meet. With a hand named, near is the side facing that hand: the left hand goes to the other's right side. |
elbow |
natural (the way the elbow bends now) or out (away from the body, to wrap an arm around someone instead of through them). |
meet |
true: no zone; this hand or the lips meet the other person's halfway (a hand held, a kiss). Needs mutual, since the other has to come halfway too. |
close |
How far apart the chests end up, in cm; the toucher steps in for it (a hug is about 22). Without it, the toucher finds their own place (above). |
mutual |
The other person does the same touch back, a beat later, and lets go with the first. |
A file with the same name in a mod later in the load order replaces it.
Gestures in a sex scene
During a sex scene, a left click on the couple opens a half circle over them. Its first level is the gesture families, plus Slower, Faster and Finish. Its second level is the gestures of that family. A gesture is a touch with a family, and every mod's touches with one are offered.
A gesture plays on top of the position: both bodies keep playing their clip, and only the arm (or the head, for the mouth) of the one who does it leaves the clip for the touch. The player's person does it to the partner.
A gesture is started from where the body is, as the position holds it:
- there is no step, no turn and no crouch;
- if the spot is out of reach in that position, the petal is shown dimmed with the reason ("butt is 12 cm out of reach in this position");
- a hand that holds the body up (on all fours, propped on a hand) is never taken: the other hand is used, or the gesture is refused ("the left hand holds the body up").
A second click on the gesture lets go.
json{ "family": "hands", "label": "Spank",
"contacts": [ { "zone": "butt", "side": "near", "hand": "near" } ],
"reach": 0.45, "hold": -1, "release": 0.5,
"motion": "strike", "period": 0.85, "strike": 20,
"look": "target", "reaction": "flinch", "gap": 0.3 }
| Field | What it means |
|---|---|
family |
The half circle's first-level petal it goes under: hands, mouth, or your own. No family: not offered in scenes. |
label |
The petal's text (default: the file name). |
motion |
Besides rest, caress and pat: strike (drawn back off the skin, then brought down fast, again and again: a spank, a slap), squeeze (a grip that tightens in pulses), pull (held and drawn toward the toucher: hair). |
period |
strike: seconds per blow (default 0.9). |
strike |
strike: how far the hand is drawn back, cm (default 16). |
reaction |
Besides look and none: flinch (a jolt of the back and the head at every blow) and headBack (the head drawn back by pull degrees: hair pulled, a hand on the throat). On top of the touched person's clip. |
pull |
headBack: degrees (default 18). |
enter |
The parts of the other body the hands may go into, by hull part name (see below): ["pelvis"] for fingering. Everywhere else the hands are kept out of the skin. |
Gestures that ship (ul_Base/Interactions/Scene/):
| Name | Family | What it does |
|---|---|---|
spank |
hands | The nearest free hand spanks the buttock, again and again; she jolts at each blow. |
choke |
hands | One hand round the front of the throat (zone throat), tightening; her head tips back. |
chokeTwoHands |
hands | Both hands on the sides of the neck. Needs both hands free. |
pullHair |
hands | A fist in the hair, drawn back; the head comes with it. |
slap |
hands | An open hand across the cheek, repeated; the head jolts. |
grabHips |
hands | Both hands clamped on the hips, gripping in pulses. Needs both hands free. |
grabBreast |
hands | A hand closed round the breast, squeezing. |
coverMouth |
hands | A palm over the mouth, the head pushed back a little. |
sceneKiss |
mouth | The lips onto the other's, eyes closed. |
sceneKissNeck |
mouth | The mouth on the side of the neck, eyes closed. |
Lua cannot start a gesture in place yet: Actor:Interaction(...) still steps and turns the toucher, which breaks a position.
Bodies and joints
Two rules apply to every touch and every gesture, whatever the IK asked for:
- A hand never goes into a body, the other's or the toucher's own. Each body is measured as a set of tubes, one per part (
pelvis,abdomenLower,chestUpper,neckUpper,head, and per sidelShldrBend,lForearmBend,lHand,lThighBend,lShin,lFoot, and the same withr). The tubes follow the skin and the body's shape (a heavier belly, a bigger chest), and the arm bends so that the palm and the fingertips stay on the skin. A gesture'senterlists the parts it may go into. - No joint turns further than a person's can.
Anims/JointLimits.jsongives each joint its range, in degrees from the rest pose and relative to its parent:twistabout the bone,swing_aandswing_bacross it. A hinge (knee, elbow, finger) hashinge, the axis it bends about (left, forward, up), andswing_ais then its bend; any other joint names withaxiswhich body directionswing_aturns about (left,forwardorup). The shipped ranges were measured from every animation in the game (-JointLimitsAudit), with the twist and sideways bend of hinges capped by anatomy (Tools/Animation/joint_limits_caps.py). A mod's file replaces the joints it names.
The limits only hold back what the hand placement adds: a joint the animation itself already takes further is left as played. A hand that needs more turn than the wrist has gets it from the forearm, and an elbow bent sideways from the upper arm turning, as in a body. When no human pose reaches the spot, the hand stops short rather than breaking a joint to get there.
json{ "joints": {
"lForearmBend": { "hinge": [0.649, 0.258, 0.716], "twist": [-15, 15], "swing_a": [-8, 129], "swing_b": [-15, 15] },
"head": { "axis": "left", "twist": [-31, 33], "swing_a": [-35, 44], "swing_b": [-42, 41] }
} }
ul.JointLimits 0 and ul.BodyHull 0 turn either rule off (to compare); ul.JointLimitsLog 1 logs every correction.
Zones
Zones name places on a body. They live in .ulzones files in any mod's Interactions/ folder; zones from every mod are merged by name, so you can add your own or redefine one.
json{
"upper_arm": { "bone": "{s}ShldrBend", "toward": "{s}ForearmBend", "along": 0.45, "dir": [0.7, 0.7, 0.1] },
"head_top": { "bone": "head", "toward": "", "along": 0.0, "dir": [0.1, 0.0, 1.0] }
}
| Field | What it means |
|---|---|
bone |
The Genesis 8 bone the zone is on. {s} is the side, l or r. |
toward |
The next bone along the limb. Empty for a bone on its own, like the head. |
along |
How far from bone toward toward, 0 to 1. |
dir |
Which way the skin faces there, as [front, out, up] in the body's own frame: [1, 0, 0] is the front, [0, 1, 0] the outside of that side, [0, 0, 1] the top. |
The hand goes onto the outermost skin in that direction, found on the actual body with its shape, and follows it as the person moves. The palm always faces the skin, the fingers carrying on from the forearm.
Zones that ship:
- Head and neck:
head_top,hair,forehead,cheek,ear,chin,lips,neck,throat,nape. - Arms:
shoulder,collarbone,upper_arm,forearm,hand. - Front:
chest,breast,breast_under,breast_side,stomach,belly,waist,crotch. - Back:
upper_back,shoulder_blade,back_side,lower_back,butt,butt_low. - Legs:
hip,thigh_front,thigh_outer,thigh_inner,thigh_back,knee,shin,calf,ankle,foot.
Grips: how a hand takes a thing
Actor:pickUp puts a hand on an object. Without more information the game guesses from the object's size: something standing (a mug, a bottle) is taken round its side, something flat (a phone, a book) from above, and anything longer than a hand's span is refused. A piece can say better, with grip markers in its .ulmat (see Rooms, Surfaces of the generated furniture):
json"grips": [
{"name": "Main", "kind": "trigger", "hand": "r", "pos_cm": [-4.0, 0.0, 6.2], "normal": [-0.97, 0, 0.24], "fingers": [0, 1, 0]},
{"name": "Support", "kind": "two_hand", "hand": "l", "pos_cm": [57.0, 0.0, 4.9], "normal": [0, 0, -1], "fingers": [0, -1, 0], "curl": 0.7}
]
| Field | What it means |
|---|---|
name |
Main is the hand that holds it; Support the second hand of a long gun or a chainsaw (not used yet). |
kind |
How the fingers close, a name from a .ulgrips file (below): power, pinch, tripod, key, hook, platform, spherical, trigger, two_hand. |
hand |
r, l, or any. A grip for either hand has its fingers flipped when the left hand takes it. A pistol's grip is r: the person uses that hand. |
pos_cm |
Where the palm centre rests, on the surface, in centimetres from the piece's origin (x right, y back, z up, front toward −y, like points). |
normal |
The surface normal there, out of the object: the palm faces the other way. |
fingers |
Which way the straight fingers point before they curl, at right angles to normal. |
curl |
Optional, 0 to 1: overrides the kind's closure. |
Grip kinds live in .ulgrips files in any mod's Interactions/ folder, merged by name like zones:
json{
"power": { "curl": 0.85, "gap": 1.6 },
"pinch": { "curl": 0.35, "gap": 0.8 },
"trigger": { "curl": 0.80, "gap": 1.5, "index_out": true }
}
curl is how far the fingers close toward the palm, gap the centimetres the palm stays off the surface, and index_out keeps the index finger straight (a trigger finger along the frame). Add a kind, or redefine a shipped one, by shipping your own file.
A zone needs a bone the body's skin is weighted to: lGlute/rGlute only drive the jiggle, which is why butt sits on pelvis.
Rigged things: a whip that hangs, swings and cracks
A piece that bends (a whip, a belt, a leash, a chain, a flogger's falls, a cane that flexes) ships twice: the .glb the room places, posed as it lies (coiled on the table), and <piece>__rig.glb beside it, the same thing on a bone chain in its rest pose with named clips as glTF animations. The .ulmat names it:
json"rig": {"file": "Product_Whip_Bull_Black__rig", "bones": 32, "clips": {"Hang": 1.0, "Swing": 2.0, "Crack": 1.0, "Coiled": 0.1}}
When a person picks such a piece up, the game swaps the rigged twin in and loops its Hang clip, so the whip drops from the hand instead of staying coiled in it; putting it down brings the static back. Actor:heldClip("Crack") plays any other clip of the held thing once (Actor:heldClip("Swing", true) loops it). Clip names are the piece's own: the shipped families use Hang, Swing, Crack for hanging things, Flex for canes and crops, Coiled for the lying pose. A modder's rigged .glb needs only a skin and animations named in the sidecar; the materials come from the static's painted zones by slot name.
Limits
- A short person reaches a tall one's head only as far as tiptoes take them: the other does not bow their head yet.
- The hand rests on the skin, not on clothes: a thick garment is pressed into.
- Outside the scene reactions (
flinch,headBack), the touched person only looks; they do not react with the body.