Improve oral hardcore pose variation
This commit is contained in:
+59
-20
@@ -55,7 +55,7 @@ HARDCORE_ENVIRONMENT_ANCHOR_REPLACEMENTS = (
|
||||
(r"\bsitting on the edge of the bed\b", "sitting on a raised edge"),
|
||||
(r"\blying at the bed edge with thighs open\b", "lying near a raised edge with thighs open"),
|
||||
(r"\bedge[- ]of[- ]bed\b", "edge-supported"),
|
||||
(r"\bbed[- ]edge\b", "edge-supported"),
|
||||
(r"\bbed[- ]edge\b", "raised edge"),
|
||||
(r"\bedge of (?:the )?bed\b", "raised edge"),
|
||||
(r"\bbed edge\b", "raised edge"),
|
||||
(r"\bhands? braced on the bed\b", "hands braced beside the body"),
|
||||
@@ -544,6 +544,9 @@ def _mentions_rear_entry(text: str) -> bool:
|
||||
def _hardcore_pose_anchor(role_graph: str, hard_item: str, composition: str = "", axis_values: Any = None) -> str:
|
||||
text = _position_context_text(role_graph, hard_item, composition, axis_values)
|
||||
item_text = " ".join(part for part in (_clean(hard_item).lower(), _axis_values_text(axis_values).lower()) if part)
|
||||
position_text = ""
|
||||
if isinstance(axis_values, dict):
|
||||
position_text = _clean(axis_values.get("position", "")).lower()
|
||||
if not text:
|
||||
return ""
|
||||
if _is_toy_assisted_double_text(role_graph, hard_item, composition, _axis_values_text(axis_values)):
|
||||
@@ -582,10 +585,30 @@ def _hardcore_pose_anchor(role_graph: str, hard_item: str, composition: str = ""
|
||||
if "kneeling" in text:
|
||||
return "kneeling front-and-back double-penetration pose"
|
||||
return "front-and-back double-penetration pose"
|
||||
if "sixty-nine" in text:
|
||||
if "sixty-nine" in position_text or ("sixty-nine" in text and not position_text):
|
||||
return "sixty-nine oral pose"
|
||||
if "face-sitting" in text:
|
||||
if "face-sitting" in position_text or ("face-sitting" in text and not position_text):
|
||||
return "face-sitting oral pose"
|
||||
if "side-lying oral" in position_text or (("side-lying oral position" in item_text or "side-lying oral" in text) and not position_text):
|
||||
return "side-lying oral pose"
|
||||
if (
|
||||
"edge-of-bed oral" in position_text
|
||||
or "edge-supported oral" in position_text
|
||||
or (("edge-of-bed oral position" in item_text or "edge-of-bed oral" in text or "edge-supported oral" in text) and not position_text)
|
||||
):
|
||||
return "edge-supported oral pose"
|
||||
if "standing oral" in position_text or (("standing oral position" in item_text or "standing oral" in text) and not position_text):
|
||||
return "standing oral pose"
|
||||
if "chair oral" in position_text or (("chair oral position" in item_text or "chair oral" in text) and not position_text):
|
||||
return "chair oral pose"
|
||||
if "kneeling oral" in position_text or (("kneeling oral position" in item_text or "kneeling oral" in text) and not position_text):
|
||||
return "kneeling oral pose"
|
||||
if "straddled oral" in position_text or (("straddled oral position" in item_text or "straddled oral" in text) and not position_text):
|
||||
return "straddled cunnilingus pose"
|
||||
if "reclining cunnilingus" in position_text or (("reclining cunnilingus position" in item_text or "reclining cunnilingus" in text) and not position_text):
|
||||
return "reclining cunnilingus pose"
|
||||
if "spread-leg oral" in position_text or (("spread-leg oral position" in item_text or "spread-leg oral" in text) and not position_text):
|
||||
return "spread-leg oral pose"
|
||||
if "cunnilingus" in text or "pussy licking" in text or "mouth on her pussy" in text:
|
||||
if "reclining" in text:
|
||||
return "reclining cunnilingus pose"
|
||||
@@ -598,7 +621,7 @@ def _hardcore_pose_anchor(role_graph: str, hard_item: str, composition: str = ""
|
||||
if "spread-leg oral position" in item_text:
|
||||
return "spread-leg oral pose"
|
||||
if "edge-of-bed oral position" in item_text:
|
||||
return "edge-of-bed oral pose"
|
||||
return "edge-supported oral pose"
|
||||
if "standing oral position" in item_text:
|
||||
return "standing oral pose"
|
||||
if "chair oral position" in item_text:
|
||||
@@ -610,7 +633,7 @@ def _hardcore_pose_anchor(role_graph: str, hard_item: str, composition: str = ""
|
||||
if "side-lying" in text:
|
||||
return "side-lying oral pose"
|
||||
if "edge-of-bed" in text or "bed-edge" in text:
|
||||
return "edge-of-bed oral pose"
|
||||
return "edge-supported oral pose"
|
||||
if "spread-leg" in text:
|
||||
return "spread-leg oral pose"
|
||||
if "chair oral" in text:
|
||||
@@ -660,6 +683,9 @@ def _hardcore_pose_anchor(role_graph: str, hard_item: str, composition: str = ""
|
||||
|
||||
def _hardcore_pose_arrangement(anchor: str, role_graph: str, hard_item: str, composition: str = "", axis_values: Any = None) -> str:
|
||||
text = _position_context_text(anchor, f"{role_graph} {hard_item}", composition, axis_values)
|
||||
position_text = ""
|
||||
if isinstance(axis_values, dict):
|
||||
position_text = _clean(axis_values.get("position", "")).lower()
|
||||
if not text:
|
||||
return ""
|
||||
mixed_woman_man = "the woman" in text and "the man" in text
|
||||
@@ -671,17 +697,21 @@ def _hardcore_pose_arrangement(anchor: str, role_graph: str, hard_item: str, com
|
||||
def double_tail() -> str:
|
||||
return "" if "toy" in text else ", with the second penetration point aligned"
|
||||
|
||||
if "sixty-nine" in text:
|
||||
if "sixty-nine" in position_text or ("sixty-nine" in text and not position_text):
|
||||
return cast_phrase(
|
||||
"with the woman and man inverted head-to-hips so both mouths align with genitals",
|
||||
"with both bodies inverted head-to-hips so both mouths align with genitals",
|
||||
)
|
||||
if "face-sitting" in text:
|
||||
if "face-sitting" in position_text or ("face-sitting" in text and not position_text):
|
||||
return cast_phrase(
|
||||
"with the man lying back while the woman straddles his face",
|
||||
"with one partner lying back while the other straddles the face",
|
||||
)
|
||||
if "reclining cunnilingus" in text or "spread-leg oral" in text:
|
||||
if (
|
||||
"reclining cunnilingus" in position_text
|
||||
or "spread-leg oral" in position_text
|
||||
or (("reclining cunnilingus" in text or "spread-leg oral" in text) and not position_text)
|
||||
):
|
||||
if "takes the man's penis" in text or "penis in her mouth" in text:
|
||||
return cast_phrase(
|
||||
"with the man seated with legs apart and the woman positioned at his hips",
|
||||
@@ -691,22 +721,30 @@ def _hardcore_pose_arrangement(anchor: str, role_graph: str, hard_item: str, com
|
||||
"with the woman lying back, thighs spread, and the man positioned between her legs",
|
||||
"with the receiving partner lying back, thighs spread, and the giver positioned between the legs",
|
||||
)
|
||||
if "straddled cunnilingus" in text or "straddled oral" in text:
|
||||
if (
|
||||
"straddled oral" in position_text
|
||||
or (("straddled cunnilingus" in text or "straddled oral" in text) and not position_text)
|
||||
):
|
||||
return cast_phrase(
|
||||
"with the woman straddling above the man's mouth and her thighs framing his face",
|
||||
"with the receiver straddling above the giver's mouth",
|
||||
)
|
||||
if "edge-of-bed oral" in text:
|
||||
if (
|
||||
"edge-of-bed oral" in position_text
|
||||
or "edge-supported oral" in position_text
|
||||
or ("edge-of-bed oral" in text and not position_text)
|
||||
or ("edge-supported oral" in text and not position_text)
|
||||
):
|
||||
if "takes the man's penis" in text or "penis in her mouth" in text:
|
||||
return cast_phrase(
|
||||
"with the man at the bed edge and the woman kneeling at his hips",
|
||||
"with the receiver at the bed edge and the giver positioned at hip height",
|
||||
"with the man at a raised edge and the woman kneeling at his hips",
|
||||
"with the receiver at a raised edge and the giver positioned at hip height",
|
||||
)
|
||||
return cast_phrase(
|
||||
"with the woman lying at the bed edge and the man positioned between her open thighs",
|
||||
"with the receiver lying at the bed edge and the giver positioned between open thighs",
|
||||
"with the woman lying at a raised edge and the man positioned between her open thighs",
|
||||
"with the receiver lying at a raised edge and the giver positioned between open thighs",
|
||||
)
|
||||
if "standing oral" in text:
|
||||
if "standing oral" in position_text or ("standing oral" in text and not position_text):
|
||||
if "takes the man's penis" in text or "penis in her mouth" in text:
|
||||
return cast_phrase(
|
||||
"with the man standing and the woman kneeling in front of his hips",
|
||||
@@ -716,7 +754,7 @@ def _hardcore_pose_arrangement(anchor: str, role_graph: str, hard_item: str, com
|
||||
"with the woman standing braced and the man kneeling between her thighs",
|
||||
"with the receiver standing braced and the giver kneeling between the thighs",
|
||||
)
|
||||
if "chair oral" in text:
|
||||
if "chair oral" in position_text or ("chair oral" in text and not position_text):
|
||||
if "takes the man's penis" in text or "penis in her mouth" in text:
|
||||
return cast_phrase(
|
||||
"with the man seated in the chair and the woman kneeling between his legs at hip level",
|
||||
@@ -726,9 +764,9 @@ def _hardcore_pose_arrangement(anchor: str, role_graph: str, hard_item: str, com
|
||||
"with one partner seated in a chair and the other kneeling between the open thighs",
|
||||
"with the receiver seated in a chair and the giver kneeling between the open thighs",
|
||||
)
|
||||
if "side-lying oral" in text:
|
||||
if "side-lying oral" in position_text or ("side-lying oral" in text and not position_text):
|
||||
return "with both bodies lying on their sides and mouth aligned to genitals"
|
||||
if "kneeling oral" in text:
|
||||
if "kneeling oral" in position_text or ("kneeling oral" in text and not position_text):
|
||||
if "takes the man's penis" in text or "penis in her mouth" in text:
|
||||
return cast_phrase(
|
||||
"with the woman kneeling in front of the man's hips, her mouth at penis level",
|
||||
@@ -917,7 +955,7 @@ def _hardcore_item_detail(hard_item: str) -> str:
|
||||
r"missionary position|cowgirl position|reverse cowgirl position|doggy style position|"
|
||||
r"standing sex position|spooning sex position|edge-of-bed position|kneeling straddle position|"
|
||||
r"lotus sex position|bent-over position|kneeling oral position|face-sitting position|"
|
||||
r"sixty-nine position|edge-of-bed oral position|standing oral position|reclining cunnilingus position|"
|
||||
r"sixty-nine position|edge-of-bed oral position|edge-supported oral position|standing oral position|reclining cunnilingus position|"
|
||||
r"straddled oral position|side-lying oral position|spread-leg oral position|chair oral position"
|
||||
)
|
||||
text = re.sub(
|
||||
@@ -962,7 +1000,8 @@ def _dedupe_hardcore_detail(detail: str, anchor: str) -> str:
|
||||
r"spread-leg oral position",
|
||||
r"chair oral position",
|
||||
),
|
||||
"edge-of-bed oral": (r"edge-of-bed oral position",),
|
||||
"edge-supported oral": (r"edge-of-bed oral position", r"edge-supported oral position"),
|
||||
"edge-of-bed oral": (r"edge-of-bed oral position", r"edge-supported oral position"),
|
||||
"standing oral": (r"standing oral position",),
|
||||
"spread-leg oral": (r"spread-leg oral position",),
|
||||
"chair oral": (r"chair oral position",),
|
||||
|
||||
+142
-26
@@ -6,7 +6,7 @@ import random
|
||||
import re
|
||||
from pathlib import Path
|
||||
from string import Formatter
|
||||
from typing import Any
|
||||
from typing import Any, Callable
|
||||
|
||||
try:
|
||||
from . import generate_prompt_batches as g
|
||||
@@ -327,7 +327,10 @@ HARDCORE_POSITION_KEY_CHOICES = [
|
||||
"lotus_lap",
|
||||
"face_sitting",
|
||||
"sixty_nine",
|
||||
"reclining_oral",
|
||||
"straddled_oral",
|
||||
"spread_leg_oral",
|
||||
"chair_oral",
|
||||
"open_thighs",
|
||||
"front_back",
|
||||
]
|
||||
@@ -361,7 +364,10 @@ HARDCORE_POSITION_KEY_MATCHES = {
|
||||
"lotus_lap": ("lotus", "lap", "seated in a partner's lap"),
|
||||
"face_sitting": ("face-sitting", "face sitting"),
|
||||
"sixty_nine": ("sixty-nine", "69"),
|
||||
"reclining_oral": ("reclining cunnilingus",),
|
||||
"straddled_oral": ("straddled oral",),
|
||||
"spread_leg_oral": ("spread-leg", "spread leg", "reclining cunnilingus"),
|
||||
"chair_oral": ("chair oral",),
|
||||
"open_thighs": ("thighs open", "legs spread", "open thighs", "legs open", "reclining with thighs open"),
|
||||
"front_back": ("front-and-back", "front and back", "one behind and one in front", "between two partners"),
|
||||
}
|
||||
@@ -863,7 +869,7 @@ HARDCORE_ENVIRONMENT_ANCHOR_REPLACEMENTS = (
|
||||
(r"\bsitting on the edge of the bed\b", "sitting on a raised edge"),
|
||||
(r"\blying at the bed edge with thighs open\b", "lying near a raised edge with thighs open"),
|
||||
(r"\bedge[- ]of[- ]bed\b", "edge-supported"),
|
||||
(r"\bbed[- ]edge\b", "edge-supported"),
|
||||
(r"\bbed[- ]edge\b", "raised edge"),
|
||||
(r"\bedge of (?:the )?bed\b", "raised edge"),
|
||||
(r"\bbed edge\b", "raised edge"),
|
||||
(r"\bhands? braced on the bed\b", "hands braced beside the body"),
|
||||
@@ -960,6 +966,33 @@ def _merged_axes(category: dict[str, Any], subcategory: dict[str, Any], item: An
|
||||
return axes
|
||||
|
||||
|
||||
def _oral_acts_for_position(values: list[Any], position: str) -> list[Any]:
|
||||
position_text = str(position or "").lower()
|
||||
if not position_text:
|
||||
return values
|
||||
|
||||
def act_text(value: Any) -> str:
|
||||
return _entry_text(value).lower()
|
||||
|
||||
def filtered(predicate: Callable[[str], bool]) -> list[Any]:
|
||||
matches = [value for value in values if predicate(act_text(value))]
|
||||
return matches or values
|
||||
|
||||
penis_terms = ("fellatio", "blowjob", "deepthroat", "penis sucking", "penis in mouth")
|
||||
cunnilingus_terms = ("cunnilingus", "pussy licking", "tongue on pussy", "oral sex with tongue and fingers", "mouth on genitals")
|
||||
if "sixty-nine" in position_text:
|
||||
return filtered(lambda text: "sixty-nine" in text)
|
||||
if "face-sitting" in position_text:
|
||||
return filtered(lambda text: "face-sitting" in text or any(term in text for term in cunnilingus_terms))
|
||||
if "straddled oral" in position_text or "reclining cunnilingus" in position_text:
|
||||
return filtered(lambda text: "sixty-nine" not in text and not any(term in text for term in penis_terms))
|
||||
if "spread-leg oral" in position_text:
|
||||
return filtered(lambda text: "sixty-nine" not in text and "face-sitting" not in text)
|
||||
if any(term in position_text for term in ("standing oral", "kneeling oral", "edge-of-bed oral", "chair oral", "side-lying oral")):
|
||||
return filtered(lambda text: "sixty-nine" not in text and "face-sitting" not in text)
|
||||
return values
|
||||
|
||||
|
||||
def _compose_item(
|
||||
rng: random.Random,
|
||||
category: dict[str, Any],
|
||||
@@ -972,12 +1005,19 @@ def _compose_item(
|
||||
axes = _merged_axes(category, subcategory, item)
|
||||
if templates and axes:
|
||||
template = _entry_text(_weighted_choice(rng, _compatible_entries(templates, women_count, men_count)))
|
||||
fields = {key for _, key, _, _ in Formatter().parse(template) if key}
|
||||
axis_values = {
|
||||
name: _entry_text(_weighted_choice(rng, _compatible_entries(axes[name], women_count, men_count)))
|
||||
for name in fields
|
||||
if name in axes and axes[name]
|
||||
}
|
||||
fields = [key for _, key, _, _ in Formatter().parse(template) if key]
|
||||
unique_fields = list(dict.fromkeys(fields))
|
||||
axis_values: dict[str, str] = {}
|
||||
if str(subcategory.get("slug") or "").lower() == "oral_sex" and "position" in unique_fields and axes.get("position"):
|
||||
position_values = _compatible_entries(axes["position"], women_count, men_count)
|
||||
axis_values["position"] = _entry_text(_weighted_choice(rng, position_values))
|
||||
for name in unique_fields:
|
||||
if name in axis_values or name not in axes or not axes[name]:
|
||||
continue
|
||||
values = _compatible_entries(axes[name], women_count, men_count)
|
||||
if str(subcategory.get("slug") or "").lower() == "oral_sex" and name == "oral_act":
|
||||
values = _oral_acts_for_position(values, axis_values.get("position", ""))
|
||||
axis_values[name] = _entry_text(_weighted_choice(rng, values))
|
||||
item_text = _format(template, axis_values).strip()
|
||||
item_name = _item_name(item) or subcategory["name"]
|
||||
return item_text, item_name, axis_values
|
||||
@@ -1736,6 +1776,12 @@ def _hardcore_position_config_active(config: dict[str, Any]) -> bool:
|
||||
return bool(config.get("enabled"))
|
||||
|
||||
|
||||
def _hardcore_position_template_required(config: dict[str, Any]) -> bool:
|
||||
if not _hardcore_position_config_active(config):
|
||||
return False
|
||||
return bool(config.get("positions")) or _normalize_hardcore_position_family(config.get("family")) != "any"
|
||||
|
||||
|
||||
def _is_hardcore_sexual_category(category: dict[str, Any]) -> bool:
|
||||
return str(category.get("slug") or "").strip() == "hardcore_sexual_poses" or str(category.get("name") or "").strip().lower() == "hardcore sexual poses"
|
||||
|
||||
@@ -1841,7 +1887,7 @@ def _hardcore_position_entry_conflicts(entry: Any, config: dict[str, Any]) -> bo
|
||||
|
||||
|
||||
def _hardcore_subcategory_supports_positions(subcategory: dict[str, Any], config: dict[str, Any]) -> bool:
|
||||
if not config.get("positions"):
|
||||
if not _hardcore_position_template_required(config):
|
||||
return True
|
||||
axes = subcategory.get("item_axes")
|
||||
if not isinstance(axes, dict):
|
||||
@@ -1875,7 +1921,7 @@ def _filter_hardcore_templates(templates: list[Any], config: dict[str, Any]) ->
|
||||
for template in templates:
|
||||
text = _entry_text(template)
|
||||
fields = {key for _, key, _, _ in Formatter().parse(text) if key}
|
||||
blocked = bool(config.get("positions")) and not bool(fields & HARDCORE_POSITION_AXIS_KEYS)
|
||||
blocked = _hardcore_position_template_required(config) and not bool(fields & HARDCORE_POSITION_AXIS_KEYS)
|
||||
blocked = blocked or any(_hardcore_text_blocked_by_action(text, field, config) for field in fields | {""})
|
||||
if not blocked:
|
||||
filtered.append(template)
|
||||
@@ -4881,6 +4927,91 @@ def _role_graph(
|
||||
return f"{woman} kneels forward with hips raised while {man} kneels behind her and thrusts his penis into her ass."
|
||||
return f"{woman} is on all fours with hips raised while {man} is positioned behind her and thrusts his penis into her ass."
|
||||
|
||||
def oral_position_graph(woman: str, man: str) -> str:
|
||||
position_text = str((item_axis_values or {}).get("position") or "").lower()
|
||||
text = " ".join(
|
||||
str(part or "").lower()
|
||||
for part in (
|
||||
item_text,
|
||||
*((item_axis_values or {}).values()),
|
||||
)
|
||||
)
|
||||
woman_gives = any(
|
||||
term in text
|
||||
for term in (
|
||||
"fellatio",
|
||||
"blowjob",
|
||||
"deepthroat",
|
||||
"penis sucking",
|
||||
"penis in mouth",
|
||||
"penis in her mouth",
|
||||
"mouth stretched around a penis",
|
||||
"lips wrapped",
|
||||
)
|
||||
)
|
||||
man_gives = any(
|
||||
term in text
|
||||
for term in (
|
||||
"cunnilingus",
|
||||
"pussy licking",
|
||||
"tongue on pussy",
|
||||
"mouth on pussy",
|
||||
"pussy and tongue",
|
||||
"face-sitting",
|
||||
"tongue contact clearly visible",
|
||||
)
|
||||
)
|
||||
if "mouth on genitals" in text and not woman_gives and not man_gives:
|
||||
if any(term in text for term in ("face-sitting", "reclining", "straddled", "spread-leg", "open thighs")):
|
||||
man_gives = True
|
||||
else:
|
||||
woman_gives = True
|
||||
|
||||
if "sixty-nine" in position_text or ("sixty-nine" in text and not position_text):
|
||||
return f"{woman} and {man} lie head-to-hips in a sixty-nine position, with {woman}'s mouth on {man}'s penis and {man}'s mouth on {woman}'s pussy."
|
||||
if "face-sitting" in position_text or ("face-sitting" in text and not position_text):
|
||||
return f"{man} lies on his back while {woman} straddles his face with her thighs around his head and {man}'s mouth pressed to her pussy."
|
||||
if "straddled oral" in position_text or ("straddled oral" in text and not position_text):
|
||||
if woman_gives and not man_gives:
|
||||
return f"{man} straddles forward near {woman}'s face while {woman} kneels below him with her mouth on his penis."
|
||||
return f"{woman} straddles above {man}'s face with her thighs framing his head while {man}'s mouth stays pressed to her pussy."
|
||||
if "side-lying oral" in position_text or ("side-lying oral" in text and not position_text):
|
||||
if woman_gives and not man_gives:
|
||||
return f"{man} lies on his side with hips angled toward {woman} while {woman} lies beside his thighs and takes his penis in her mouth."
|
||||
return f"{woman} lies on her side with her top thigh lifted while {man} lies beside her hips with his mouth pressed to her pussy."
|
||||
if (
|
||||
"edge-of-bed oral" in position_text
|
||||
or "edge of bed oral" in position_text
|
||||
or "edge-supported oral" in position_text
|
||||
or (("edge-of-bed oral" in text or "edge of bed oral" in text or "edge-supported oral" in text) and not position_text)
|
||||
):
|
||||
if woman_gives and not man_gives:
|
||||
return f"{man} sits at a raised edge with legs apart while {woman} kneels between his thighs and takes his penis in her mouth."
|
||||
return f"{woman} lies at a raised edge with thighs open while {man} kneels between her legs with his mouth on her pussy."
|
||||
if "standing oral" in position_text or ("standing oral" in text and not position_text):
|
||||
if man_gives and not woman_gives:
|
||||
return f"{woman} stands braced with one thigh lifted while {man} kneels between her legs with his mouth on her pussy."
|
||||
return f"{man} stands with hips forward while {woman} kneels in front of him at hip height and takes his penis in her mouth."
|
||||
if "chair oral" in position_text or ("chair oral" in text and not position_text):
|
||||
if man_gives and not woman_gives:
|
||||
return f"{woman} sits in a chair with thighs open while {man} kneels between her legs with his mouth pressed to her pussy."
|
||||
return f"{man} sits in a chair with legs apart while {woman} kneels between his thighs and takes his penis in her mouth."
|
||||
if (
|
||||
"reclining cunnilingus" in position_text
|
||||
or "spread-leg oral" in position_text
|
||||
or (("reclining cunnilingus" in text or "spread-leg oral" in text) and not position_text)
|
||||
):
|
||||
if woman_gives and not man_gives:
|
||||
return f"{man} reclines with legs apart while {woman} kneels between his thighs and takes his penis in her mouth."
|
||||
return f"{woman} reclines on her back with thighs spread while {man} kneels between her legs with his mouth on her pussy."
|
||||
if "kneeling oral" in position_text or ("kneeling oral" in text and not position_text):
|
||||
if man_gives and not woman_gives:
|
||||
return f"{woman} kneels with thighs parted and hips angled forward while {man} kneels in front of her with his mouth on her pussy."
|
||||
return f"{woman} kneels in front of {man}'s hips with her mouth at penis level while {man} stands or sits close above her."
|
||||
if man_gives and not woman_gives:
|
||||
return f"{woman} lies on her back with thighs open while {man} kneels between her legs with his mouth pressed to her pussy."
|
||||
return f"{woman} kneels in front of {man}'s hips and takes his penis in her mouth while {man} keeps his hips aligned with her face."
|
||||
|
||||
if people_count == 1:
|
||||
solo = people[0]
|
||||
if women_count == 1:
|
||||
@@ -4932,22 +5063,7 @@ def _role_graph(
|
||||
man = any_man()
|
||||
third = any_person({woman, man}) if people_count >= 3 else ""
|
||||
if "oral" in slug:
|
||||
if "sixty-nine" in item_text or ("blowjob" in item_text and ("cunnilingus" in item_text or "pussy" in item_text)):
|
||||
graph = f"{woman} has {man}'s penis in her mouth while {man} uses his mouth on {woman}'s pussy, with both mouths pressed to genitals."
|
||||
elif any(
|
||||
term in item_text
|
||||
for term in (
|
||||
"cunnilingus",
|
||||
"pussy licking",
|
||||
"tongue on pussy",
|
||||
"mouth on pussy",
|
||||
"pussy and tongue",
|
||||
"tongue contact",
|
||||
)
|
||||
) or ("pussy" in item_text and "penis" not in item_text):
|
||||
graph = f"{man} gives oral to {woman}, mouth on her pussy while {woman}'s thighs are held open for the camera."
|
||||
else:
|
||||
graph = f"{woman} takes {man}'s penis in her mouth while {man} holds her hair and hips."
|
||||
graph = oral_position_graph(woman, man)
|
||||
elif "anal" in slug or "double" in slug:
|
||||
if "double" in item_text or "toy" in item_text:
|
||||
if people_count >= 3:
|
||||
|
||||
Reference in New Issue
Block a user