Mirror softcore outfit in hard clothing state
This commit is contained in:
+104
-51
@@ -269,12 +269,36 @@ def hardcore_row_access_flags(row: dict[str, Any]) -> dict[str, bool]:
|
||||
|
||||
|
||||
def _outfit_without_lower_body_blockers(outfit: str) -> str:
|
||||
_removed, remaining = _outfit_split_by_terms(
|
||||
outfit,
|
||||
LOWER_BODY_CLOTHING_TERMS,
|
||||
replacements=(
|
||||
(r"\blingerie set\b", "lingerie top details"),
|
||||
(r"\bbrief set\b", "bra set"),
|
||||
(r"\bbodysuit with\b", "upper bodysuit detail with"),
|
||||
),
|
||||
)
|
||||
return remaining
|
||||
|
||||
|
||||
def _outfit_without_upper_body_blockers(outfit: str) -> str:
|
||||
_removed, remaining = _outfit_split_by_terms(
|
||||
outfit,
|
||||
UPPER_BODY_CLOTHING_TERMS,
|
||||
replacements=(
|
||||
(r"\blingerie set\b", "lingerie styling"),
|
||||
(r"\bbalconette bra and brief set\b", "briefs and garter styling"),
|
||||
),
|
||||
)
|
||||
return remaining
|
||||
|
||||
|
||||
def _split_outfit_fragments(outfit: str, replacements: tuple[tuple[str, str], ...] = ()) -> list[str]:
|
||||
text = str(outfit or "").strip()
|
||||
if not text:
|
||||
return ""
|
||||
text = re.sub(r"\blingerie set\b", "lingerie top details", text, flags=re.IGNORECASE)
|
||||
text = re.sub(r"\bbrief set\b", "bra set", text, flags=re.IGNORECASE)
|
||||
text = re.sub(r"\bbodysuit with\b", "upper bodysuit detail with", text, flags=re.IGNORECASE)
|
||||
return []
|
||||
for pattern, replacement in replacements:
|
||||
text = re.sub(pattern, replacement, text, flags=re.IGNORECASE)
|
||||
fragments = re.split(r"\s*,\s*|\s+\band\b\s+|\s+\bwith\b\s+|\s+\bunder\b\s+|\s+\bover\b\s+", text)
|
||||
kept = []
|
||||
for fragment in fragments:
|
||||
@@ -282,12 +306,7 @@ def _outfit_without_lower_body_blockers(outfit: str) -> str:
|
||||
fragment = re.sub(r"^(?:and|with|under|over)\s+", "", fragment, flags=re.IGNORECASE)
|
||||
if not fragment:
|
||||
continue
|
||||
lower = fragment.lower()
|
||||
if any(term in lower for term in LOWER_BODY_CLOTHING_TERMS):
|
||||
continue
|
||||
kept.append(fragment)
|
||||
if not kept:
|
||||
return ""
|
||||
deduped = []
|
||||
seen = set()
|
||||
for fragment in kept:
|
||||
@@ -295,36 +314,82 @@ def _outfit_without_lower_body_blockers(outfit: str) -> str:
|
||||
if key and key not in seen:
|
||||
deduped.append(fragment)
|
||||
seen.add(key)
|
||||
return ", ".join(deduped)
|
||||
return deduped
|
||||
|
||||
|
||||
def _outfit_without_upper_body_blockers(outfit: str) -> str:
|
||||
text = str(outfit or "").strip()
|
||||
if not text:
|
||||
return ""
|
||||
text = re.sub(r"\blingerie set\b", "lingerie styling", text, flags=re.IGNORECASE)
|
||||
text = re.sub(r"\bbalconette bra and brief set\b", "briefs and garter styling", text, flags=re.IGNORECASE)
|
||||
fragments = re.split(r"\s*,\s*|\s+\band\s+|\s+\bwith\s+|\s+\bunder\s+|\s+\bover\s+", text)
|
||||
kept = []
|
||||
for fragment in fragments:
|
||||
fragment = fragment.strip(" ,.;")
|
||||
fragment = re.sub(r"^(?:and|with|under|over)\s+", "", fragment, flags=re.IGNORECASE)
|
||||
if not fragment:
|
||||
continue
|
||||
def _join_fragments(fragments: list[str]) -> str:
|
||||
return ", ".join(fragment for fragment in fragments if fragment)
|
||||
|
||||
|
||||
def _outfit_split_by_terms(
|
||||
outfit: str,
|
||||
terms: tuple[str, ...],
|
||||
replacements: tuple[tuple[str, str], ...] = (),
|
||||
) -> tuple[str, str]:
|
||||
removed: list[str] = []
|
||||
remaining: list[str] = []
|
||||
for fragment in _split_outfit_fragments(outfit, replacements):
|
||||
lower = fragment.lower()
|
||||
if any(term in lower for term in UPPER_BODY_CLOTHING_TERMS):
|
||||
continue
|
||||
kept.append(fragment)
|
||||
if not kept:
|
||||
return ""
|
||||
deduped = []
|
||||
seen = set()
|
||||
for fragment in kept:
|
||||
key = re.sub(r"\W+", " ", fragment.lower()).strip()
|
||||
if key and key not in seen:
|
||||
deduped.append(fragment)
|
||||
seen.add(key)
|
||||
return ", ".join(deduped)
|
||||
if any(term in lower for term in terms):
|
||||
removed.append(fragment)
|
||||
else:
|
||||
remaining.append(fragment)
|
||||
return _join_fragments(removed), _join_fragments(remaining)
|
||||
|
||||
|
||||
def _is_plural_clothing_phrase(text: str) -> bool:
|
||||
lower = text.lower()
|
||||
if "," in text or " and " in lower:
|
||||
return True
|
||||
return any(term in lower for term in ("briefs", "panties", "shorts", "jeans", "trousers", "pants", "stockings"))
|
||||
|
||||
|
||||
def _partially_removed_outfit_state(outfit: str, woman_access: str, implied: bool = False) -> str:
|
||||
outfit = str(outfit or "").strip()
|
||||
if not outfit:
|
||||
return "Woman A's body is partly exposed" if implied else "Woman A's outfit is pushed aside where needed"
|
||||
if woman_access == "lower":
|
||||
removed, remaining = _outfit_split_by_terms(
|
||||
outfit,
|
||||
LOWER_BODY_CLOTHING_TERMS,
|
||||
replacements=(
|
||||
(r"\blingerie set\b", "lingerie top details"),
|
||||
(r"\bbrief set\b", "bra set"),
|
||||
(r"\bbodysuit with\b", "upper bodysuit detail with"),
|
||||
),
|
||||
)
|
||||
verb = "are" if _is_plural_clothing_phrase(removed) else "is"
|
||||
lead = (
|
||||
f"Woman A's {removed} {verb} pulled aside or removed below the hips"
|
||||
if removed
|
||||
else "Woman A's lower body is clear, with the outfit pulled aside below the hips"
|
||||
)
|
||||
if remaining:
|
||||
remain_verb = "remain" if _is_plural_clothing_phrase(remaining) else "remains"
|
||||
return f"{lead}; {remaining} {remain_verb} visible from the same outfit"
|
||||
return lead
|
||||
if woman_access == "upper":
|
||||
removed, remaining = _outfit_split_by_terms(
|
||||
outfit,
|
||||
UPPER_BODY_CLOTHING_TERMS,
|
||||
replacements=(
|
||||
(r"\blingerie set\b", "lingerie styling"),
|
||||
(r"\bbalconette bra and brief set\b", "briefs and garter styling"),
|
||||
),
|
||||
)
|
||||
verb = "are" if _is_plural_clothing_phrase(removed) else "is"
|
||||
lead = (
|
||||
f"Woman A's {removed} {verb} pulled open or pushed aside from her breasts and chest"
|
||||
if removed
|
||||
else "Woman A's upper body is clear, with the outfit pulled open at the chest"
|
||||
)
|
||||
if remaining:
|
||||
remain_verb = "remain" if _is_plural_clothing_phrase(remaining) else "remains"
|
||||
return f"{lead}; {remaining} {remain_verb} visible from the same outfit"
|
||||
return lead
|
||||
if implied:
|
||||
return f"Woman A's {outfit} is loosened and partly slipping off, leaving her body partly exposed"
|
||||
return f"Woman A's {outfit} is pushed aside and partly removed where needed"
|
||||
|
||||
|
||||
def hardcore_clothing_state(
|
||||
@@ -340,22 +405,10 @@ def hardcore_clothing_state(
|
||||
base = continuity_map[mode]
|
||||
if mode == "explicit_nude":
|
||||
return f"Body exposure: {base}."
|
||||
if mode == "implied_nude":
|
||||
return f"Body exposure: {base}."
|
||||
if mode == "partially_removed" and woman_access == "lower":
|
||||
detail = _outfit_without_lower_body_blockers(outfit)
|
||||
base = "Woman A's lower body is clear; any lower garment is pulled aside or removed below the hips"
|
||||
if detail:
|
||||
return f"Clothing state: {base}; visible remaining styling: {detail}."
|
||||
return f"Clothing state: {base}."
|
||||
if mode == "partially_removed" and woman_access == "upper":
|
||||
detail = _outfit_without_upper_body_blockers(outfit)
|
||||
base = "Woman A's breasts and upper body are clear; any bra cup, bodice, or top panel is pulled aside or removed"
|
||||
if detail:
|
||||
return f"Clothing state: {base}; visible remaining styling: {detail}."
|
||||
return f"Clothing state: {base}."
|
||||
if mode == "partially_removed":
|
||||
return f"Clothing state: Woman A keeps the outfit mostly on; teaser outfit detail: {outfit}."
|
||||
return f"Clothing state: {_partially_removed_outfit_state(outfit, woman_access)}."
|
||||
if mode == "implied_nude":
|
||||
return f"Clothing state: {_partially_removed_outfit_state(outfit, woman_access, implied=True)}."
|
||||
return f"Clothing state: {base}; teaser outfit detail: {outfit}."
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user