from __future__ import annotations import re from typing import Any try: from .krea_action_context import ( axis_values_text, is_climax_text, is_foreplay_text, is_oral_text, is_outercourse_text, is_toy_assisted_double_text, is_vaginal_penetration_text, normalize_hardcore_detail_density, ) from .krea_detail import limit_detail_for_density from .krea_action_positions import ( arrangement_duplicates_role, hardcore_pose_anchor, hardcore_pose_arrangement, ) from .krea_action_details import ( dedupe_anchor_detail, dedupe_oral_detail, dedupe_outercourse_detail, dedupe_penetration_detail, dedupe_toy_double_detail, hardcore_item_detail, sanitize_foreplay_detail, ) from .krea_action_climax import climax_role_graph, dedupe_climax_detail except ImportError: # Allows local smoke tests with `python -c`. from krea_action_context import ( axis_values_text, is_climax_text, is_foreplay_text, is_oral_text, is_outercourse_text, is_toy_assisted_double_text, is_vaginal_penetration_text, normalize_hardcore_detail_density, ) from krea_detail import limit_detail_for_density from krea_action_positions import ( arrangement_duplicates_role, hardcore_pose_anchor, hardcore_pose_arrangement, ) from krea_action_details import ( dedupe_anchor_detail, dedupe_oral_detail, dedupe_outercourse_detail, dedupe_penetration_detail, dedupe_toy_double_detail, hardcore_item_detail, sanitize_foreplay_detail, ) from krea_action_climax import climax_role_graph, dedupe_climax_detail def _clean(value: Any) -> str: text = "" if value is None else str(value) text = text.replace("\n", " ") text = re.sub(r"\s+", " ", text).strip() text = re.sub(r"\s+([,.;:])", r"\1", text) return text def _lowercase_for_inline_join(text: str) -> str: text = _clean(text) return text[:1].lower() + text[1:] if text else text def _with_indefinite_article(text: str) -> str: text = _clean(text) if not text or text.lower().startswith(("a ", "an ")): return text article = "an" if text[:1].lower() in "aeiou" else "a" return f"{article} {text}" def hardcore_action_sentence( role_graph: str, hard_item: str, composition: str = "", axis_values: Any = None, detail_density: str = "balanced", ) -> str: detail_density = normalize_hardcore_detail_density(detail_density) role_graph = _clean(role_graph).rstrip(".") hard_item = _clean(hard_item).rstrip(".") role_graph = re.sub( r"\bthe man penetrates the woman while a toy adds a second point of contact\b", "the man's penis thrusts into the woman while a toy is positioned at the second penetration point", role_graph, flags=re.IGNORECASE, ) role_graph = re.sub( r"\bthe man thrusts his penis into the woman while a toy adds a second penetration point\b", "the man's penis thrusts into the woman while a toy is positioned at the second penetration point", role_graph, flags=re.IGNORECASE, ) role_graph = re.sub( r"\bthe man thrusts his penis into the woman\b", "the man's penis thrusts into the woman", role_graph, flags=re.IGNORECASE, ) role_graph = re.sub( r"\bthe man penetrates the woman anally\b", "the man's penis thrusts into the woman's ass", role_graph, flags=re.IGNORECASE, ) role_graph = re.sub( r"\bthe man thrusts his penis into the woman's ass\b", "the man's penis thrusts into the woman's ass", role_graph, flags=re.IGNORECASE, ) role_graph = re.sub( r"\bthe man penetrates the woman\b", "the man's penis thrusts into the woman", role_graph, flags=re.IGNORECASE, ) role_graph = re.sub( r"\bthe woman and the man are in mutual oral contact with mouth-to-genital contact visible\b", "the woman has the man's penis in her mouth while the man uses his mouth on her pussy", role_graph, flags=re.IGNORECASE, ) role_graph = re.sub( r"\bthe woman gives oral to the man\b", "the woman takes the man's penis in her mouth", role_graph, flags=re.IGNORECASE, ) is_climax = is_climax_text(role_graph, hard_item, composition, axis_values_text(axis_values)) if is_climax: role_graph = climax_role_graph(role_graph, hard_item, axis_values) detail = hardcore_item_detail(hard_item) anchor = hardcore_pose_anchor(role_graph, hard_item, composition, axis_values) is_outercourse = is_outercourse_text(role_graph, hard_item, composition, axis_values_text(axis_values)) is_oral = is_oral_text(role_graph, hard_item, composition, axis_values_text(axis_values)) is_penetrative = is_vaginal_penetration_text(role_graph, hard_item, composition, axis_values_text(axis_values)) if is_toy_assisted_double_text(role_graph, hard_item, composition, axis_values_text(axis_values)): role_graph = re.sub( r"\s+while a toy adds (?:the|a) second penetration point\b", " while a toy is positioned at the second penetration point", role_graph, flags=re.IGNORECASE, ) if is_climax: anchor = "" detail = dedupe_climax_detail(detail, role_graph, detail_density) elif is_foreplay_text(role_graph, hard_item, composition, axis_values_text(axis_values)): anchor = "" detail = sanitize_foreplay_detail(detail, role_graph, composition) detail = limit_detail_for_density(detail, detail_density, False) elif is_outercourse: anchor = "" detail = dedupe_outercourse_detail(detail, role_graph, hard_item, axis_values) detail = limit_detail_for_density(detail, detail_density, False) elif is_oral and role_graph: anchor = "" detail = dedupe_oral_detail(detail, role_graph, hard_item, axis_values) detail = limit_detail_for_density(detail, detail_density, False) elif is_penetrative and role_graph: anchor = "" detail = dedupe_penetration_detail(detail, role_graph, hard_item, axis_values) detail = limit_detail_for_density(detail, detail_density, False) else: detail = dedupe_anchor_detail(detail, anchor) if anchor else detail if is_toy_assisted_double_text(role_graph, hard_item, composition, axis_values_text(axis_values)): detail = dedupe_toy_double_detail(detail) detail = limit_detail_for_density(detail, detail_density, False) arrangement = hardcore_pose_arrangement(anchor, role_graph, hard_item, composition, axis_values) anchor_phrase = _with_indefinite_article(anchor) if anchor else "" if arrangement and anchor_phrase and not arrangement_duplicates_role(arrangement, role_graph): anchor_phrase = f"{anchor_phrase} {arrangement}" if role_graph and anchor_phrase: sentence = f"In {anchor_phrase}, {_lowercase_for_inline_join(role_graph)}" elif role_graph: sentence = role_graph elif detail and anchor_phrase: sentence = f"In {anchor_phrase}, {detail}" detail = "" else: sentence = detail or hard_item detail = "" if detail: sentence = f"{sentence}; {detail}" return sentence