Filter anal axis details for position compatibility

This commit is contained in:
2026-06-27 16:04:39 +02:00
parent d0f2670d9c
commit ff6195473b
6 changed files with 262 additions and 6 deletions
+93 -1
View File
@@ -292,6 +292,96 @@ def outercourse_axis_values_for_position(values: list[Any], position: str, axis_
return values
def anal_axis_values_for_position(values: list[Any], position: str, axis_name: str) -> list[Any]:
position_text = str(position or "").lower()
if not position_text:
return values
axis_name = str(axis_name or "").lower()
if axis_name not in {"body_contact", "hand_detail", "leg_detail", "thrust_detail", "visibility"}:
return values
def value_text(value: Any) -> str:
return entry_text(value).lower()
def filtered(terms: tuple[str, ...], excluded_terms: tuple[str, ...] = ()) -> list[Any]:
matches = [
value
for value in values
if any(term in value_text(value) for term in terms)
and not any(term in value_text(value) for term in excluded_terms)
]
if matches:
return matches
if excluded_terms:
non_excluded = [
value
for value in values
if not any(term in value_text(value) for term in excluded_terms)
]
if non_excluded:
return non_excluded
return values
if "side-lying" in position_text or "spooning" in position_text:
by_axis = {
"body_contact": ("bodies locked", "chests pressed", "sweaty", "hips pressed"),
"hand_detail": ("hips", "waist", "cheeks", "shoulders"),
"leg_detail": ("one leg lifted", "thighs held open", "legs spread"),
"thrust_detail": ("pelvis pressed", "bodies rocking", "wet skin", "hard grinding"),
"visibility": ("ass and penis", "anal penetration", "spread cheeks", "genital contact"),
}
return filtered(
by_axis.get(axis_name, ("side", "thigh", "hips")),
("standing", "kneeling", "draped over shoulders", "knees pressed to chest"),
)
if "standing" in position_text:
by_axis = {
"body_contact": ("hips pressed", "bodies locked", "one body bent over", "ass lifted", "sweaty"),
"hand_detail": ("hips", "waist", "cheeks", "shoulders"),
"leg_detail": ("standing", "one foot planted"),
"thrust_detail": ("hips", "pelvis", "hard grinding", "bodies rocking"),
"visibility": ("ass and penis", "anal penetration", "spread cheeks", "genital contact"),
}
return filtered(
by_axis.get(axis_name, ("standing", "hips")),
("kneeling", "draped over shoulders", "knees pressed to chest", "side-lying"),
)
if "edge-of-bed" in position_text or "bed-edge" in position_text or "edge supported" in position_text:
by_axis = {
"body_contact": ("thighs held open", "hips pressed", "bodies locked", "ass lifted"),
"hand_detail": ("hips", "waist", "cheeks", "thighs"),
"leg_detail": ("knees pressed", "legs draped", "thighs held open", "one foot planted"),
"thrust_detail": ("hips", "pelvis", "hard grinding", "bodies rocking"),
"visibility": ("ass and penis", "anal penetration", "open thighs", "genital contact"),
}
return filtered(by_axis.get(axis_name, ("thigh", "hips")), ("standing", "side-lying"))
if "kneeling" in position_text:
by_axis = {
"body_contact": ("ass lifted", "hips pressed", "bodies locked", "one body bent over"),
"hand_detail": ("hips", "waist", "cheeks", "thighs"),
"leg_detail": ("kneeling", "thighs held open", "legs spread"),
"thrust_detail": ("hips", "pelvis", "ass pushed", "hard grinding"),
"visibility": ("ass and penis", "anal penetration", "spread cheeks", "genital contact"),
}
return filtered(
by_axis.get(axis_name, ("kneeling", "hips")),
("standing", "draped over shoulders", "knees pressed to chest", "side-lying"),
)
if "doggy" in position_text or "face-down" in position_text or "bent-over" in position_text:
by_axis = {
"body_contact": ("ass lifted", "one body bent over", "hips pressed", "bodies locked"),
"hand_detail": ("hips", "waist", "cheeks", "thighs"),
"leg_detail": ("legs spread", "kneeling", "one foot planted", "standing"),
"thrust_detail": ("ass pushed", "hips", "pelvis", "hard grinding"),
"visibility": ("ass and penis", "anal penetration", "spread cheeks", "genital contact"),
}
excluded = ("side-lying", "draped over shoulders", "knees pressed to chest")
if "face-down" in position_text or "doggy" in position_text:
excluded = (*excluded, "standing")
return filtered(by_axis.get(axis_name, ("ass", "hips")), excluded)
return values
def _format(template: str, context: dict[str, Any]) -> str:
fields = {key for _, key, _, _ in Formatter().parse(template) if key}
safe_context = SafeFormatDict({key: "" for key in fields})
@@ -317,7 +407,7 @@ def compose_item(
unique_fields = list(dict.fromkeys(fields))
axis_values: dict[str, str] = {}
subcategory_slug = str(subcategory.get("slug") or "").lower()
if subcategory_slug in ("oral_sex", "outercourse_sex") and "position" in unique_fields and axes.get("position"):
if subcategory_slug in ("oral_sex", "outercourse_sex", "anal_double_penetration") and "position" in unique_fields and axes.get("position"):
position_values = category_policy.compatible_entries(axes["position"], women_count, men_count)
axis_values["position"] = entry_text(weighted_choice(rng, position_values))
for name in unique_fields:
@@ -337,6 +427,8 @@ def compose_item(
values = outercourse_acts_for_position(values, axis_values.get("position", ""))
if subcategory_slug == "outercourse_sex":
values = outercourse_axis_values_for_position(values, axis_values.get("position", ""), name)
if subcategory_slug == "anal_double_penetration":
values = anal_axis_values_for_position(values, axis_values.get("position", ""), name)
axis_values[name] = entry_text(weighted_choice(rng, values))
item_prompt = _format(template, axis_values).strip()
name = item_name(item) or subcategory["name"]