Add character-level InstaOF overrides
This commit is contained in:
@@ -113,6 +113,17 @@ character-driven. For configured casts, matching enabled slots emit
|
||||
per-character expression text such as `Woman A has ...; Man A has ...`; Krea
|
||||
formatting naturalizes those labels in pair prompts.
|
||||
|
||||
For Insta/OF pairs, slots also expose character-level overrides:
|
||||
|
||||
- `softcore_expression_intensity` and `hardcore_expression_intensity`: override
|
||||
the option-node expression fallback for that character and that output half.
|
||||
- `softcore_outfit`: overrides the character's softcore clothing. For `Woman A`
|
||||
this replaces the generated teaser outfit; for partners it replaces random
|
||||
partner styling.
|
||||
- `hardcore_clothing`: adds direct character clothing/nudity wording in the
|
||||
hardcore output. A `Woman A` hardcore clothing override replaces the global
|
||||
`hardcore_clothing_continuity` text to avoid contradictory clothing prompts.
|
||||
|
||||
Slots are chainable through the `character_cast` input/output. In automatic
|
||||
label mode, the slot closest to the final generator becomes `A` for its gender,
|
||||
the next upstream slot becomes `B`, then `C`, and so on. Example:
|
||||
@@ -311,11 +322,12 @@ Options:
|
||||
microwear, or shirtless partner styling. `explicit_nude` is available when
|
||||
you want visible nude creator-shot framing without a sex act.
|
||||
- `hardcore_level`: `explicit` or `hardcore`.
|
||||
- `softcore_expression_intensity`: `0.0` is mild/controlled, `0.5` is sensual,
|
||||
- `softcore_expression_intensity`: fallback when no connected character slot
|
||||
sets `softcore_expression_intensity`. `0.0` is mild/controlled, `0.5` is sensual,
|
||||
`1.0` strongly favors more heated softcore faces.
|
||||
- `hardcore_expression_intensity`: `0.0` is controlled, `0.5` is balanced
|
||||
hardcore, `1.0` strongly favors ahegao-style, drooling, fucked-out, climax,
|
||||
and messy orgasm expressions.
|
||||
- `hardcore_expression_intensity`: fallback when no connected character slot
|
||||
sets `hardcore_expression_intensity`. `0.0` is controlled, `0.5` is balanced
|
||||
hardcore, `1.0` strongly favors stronger hardcore expressions.
|
||||
- `softcore_expression_enabled` and `hardcore_expression_enabled`: disable the
|
||||
expression sentence for that half of the Insta/OF pair. The intensity values
|
||||
are fallbacks; `SxCP Woman Slot` / `SxCP Man Slot` `expression_intensity`
|
||||
@@ -328,7 +340,8 @@ Options:
|
||||
- `hardcore_clothing_continuity`: `none`, `same_outfit`, `partially_removed`,
|
||||
`implied_nude`, or `explicit_nude`. This controls whether the hardcore prompt
|
||||
references the softcore outfit, uses it displaced/removed, or makes Woman A
|
||||
explicitly nude.
|
||||
explicitly nude. It is a fallback for Woman A; `hardcore_clothing` on
|
||||
`SxCP Woman Slot` or `SxCP Man Slot` takes priority for that character.
|
||||
- `softcore_camera_mode`: base camera mode for the softcore output.
|
||||
- `hardcore_camera_mode`: `from_camera_config`, `same_as_softcore`, or a
|
||||
separate base camera mode for the hardcore output. `from_camera_config` is
|
||||
|
||||
+36
@@ -614,6 +614,10 @@ class SxCPCharacterSlot:
|
||||
"expression_enabled": ("BOOLEAN", {"default": True}),
|
||||
"expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
"presence_mode": (character_presence_choices(), {"default": "visible"}),
|
||||
"softcore_expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
"hardcore_expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
"softcore_outfit": ("STRING", {"default": ""}),
|
||||
"hardcore_clothing": ("STRING", {"default": ""}),
|
||||
},
|
||||
"optional": {
|
||||
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
||||
@@ -644,6 +648,10 @@ class SxCPCharacterSlot:
|
||||
expression_enabled=True,
|
||||
expression_intensity=-1.0,
|
||||
presence_mode="visible",
|
||||
softcore_expression_intensity=-1.0,
|
||||
hardcore_expression_intensity=-1.0,
|
||||
softcore_outfit="",
|
||||
hardcore_clothing="",
|
||||
character_cast="",
|
||||
):
|
||||
result = build_character_slot_json(
|
||||
@@ -663,6 +671,10 @@ class SxCPCharacterSlot:
|
||||
expression_enabled=expression_enabled,
|
||||
expression_intensity=expression_intensity,
|
||||
presence_mode=presence_mode,
|
||||
softcore_expression_intensity=softcore_expression_intensity,
|
||||
hardcore_expression_intensity=hardcore_expression_intensity,
|
||||
softcore_outfit=softcore_outfit,
|
||||
hardcore_clothing=hardcore_clothing,
|
||||
enabled=enabled,
|
||||
character_cast=character_cast or "",
|
||||
)
|
||||
@@ -689,6 +701,10 @@ class SxCPWomanSlot:
|
||||
"descriptor_detail": (character_descriptor_detail_choices(), {"default": "auto"}),
|
||||
"expression_enabled": ("BOOLEAN", {"default": True}),
|
||||
"expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
"softcore_expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
"hardcore_expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
"softcore_outfit": ("STRING", {"default": ""}),
|
||||
"hardcore_clothing": ("STRING", {"default": ""}),
|
||||
},
|
||||
"optional": {
|
||||
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
||||
@@ -717,6 +733,10 @@ class SxCPWomanSlot:
|
||||
descriptor_detail="auto",
|
||||
expression_enabled=True,
|
||||
expression_intensity=-1.0,
|
||||
softcore_expression_intensity=-1.0,
|
||||
hardcore_expression_intensity=-1.0,
|
||||
softcore_outfit="",
|
||||
hardcore_clothing="",
|
||||
character_cast="",
|
||||
):
|
||||
result = build_character_slot_json(
|
||||
@@ -735,6 +755,10 @@ class SxCPWomanSlot:
|
||||
descriptor_detail=descriptor_detail,
|
||||
expression_enabled=expression_enabled,
|
||||
expression_intensity=expression_intensity,
|
||||
softcore_expression_intensity=softcore_expression_intensity,
|
||||
hardcore_expression_intensity=hardcore_expression_intensity,
|
||||
softcore_outfit=softcore_outfit,
|
||||
hardcore_clothing=hardcore_clothing,
|
||||
enabled=enabled,
|
||||
character_cast=character_cast or "",
|
||||
)
|
||||
@@ -761,6 +785,10 @@ class SxCPManSlot:
|
||||
"expression_enabled": ("BOOLEAN", {"default": True}),
|
||||
"expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
"presence_mode": (character_presence_choices(), {"default": "visible"}),
|
||||
"softcore_expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
"hardcore_expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||
"softcore_outfit": ("STRING", {"default": ""}),
|
||||
"hardcore_clothing": ("STRING", {"default": ""}),
|
||||
},
|
||||
"optional": {
|
||||
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
||||
@@ -789,6 +817,10 @@ class SxCPManSlot:
|
||||
expression_enabled=True,
|
||||
expression_intensity=-1.0,
|
||||
presence_mode="visible",
|
||||
softcore_expression_intensity=-1.0,
|
||||
hardcore_expression_intensity=-1.0,
|
||||
softcore_outfit="",
|
||||
hardcore_clothing="",
|
||||
character_cast="",
|
||||
):
|
||||
result = build_character_slot_json(
|
||||
@@ -808,6 +840,10 @@ class SxCPManSlot:
|
||||
expression_enabled=expression_enabled,
|
||||
expression_intensity=expression_intensity,
|
||||
presence_mode=presence_mode,
|
||||
softcore_expression_intensity=softcore_expression_intensity,
|
||||
hardcore_expression_intensity=hardcore_expression_intensity,
|
||||
softcore_outfit=softcore_outfit,
|
||||
hardcore_clothing=hardcore_clothing,
|
||||
enabled=enabled,
|
||||
character_cast=character_cast or "",
|
||||
)
|
||||
|
||||
+16
-1
@@ -307,6 +307,18 @@ def _pov_action_phrase(action: Any, pov_labels: list[str]) -> str:
|
||||
rendered = re.sub(r"\bhe\b", "the POV viewer", rendered, flags=re.IGNORECASE)
|
||||
rendered = re.sub(r"\bhim\b", "the POV viewer", rendered, flags=re.IGNORECASE)
|
||||
rendered = re.sub(r"\bhis\b", "the POV viewer's", rendered, flags=re.IGNORECASE)
|
||||
rendered = re.sub(
|
||||
r"\bthe POV viewer lies on the POV viewer's back under her\b",
|
||||
"the POV viewer reclines underneath her",
|
||||
rendered,
|
||||
flags=re.IGNORECASE,
|
||||
)
|
||||
rendered = re.sub(
|
||||
r"\bthe POV viewer lies on the POV viewer's back\b",
|
||||
"the POV viewer reclines",
|
||||
rendered,
|
||||
flags=re.IGNORECASE,
|
||||
)
|
||||
rendered = re.sub(r"\bthe POV viewer is positioned\b", "the POV camera is positioned", rendered, flags=re.IGNORECASE)
|
||||
return rendered
|
||||
|
||||
@@ -1595,7 +1607,10 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
||||
hard_parts = [
|
||||
hard_action,
|
||||
_pov_camera_phrase(pov_labels),
|
||||
_natural_clothing_state(row.get("hardcore_clothing_state")),
|
||||
_natural_label_text(
|
||||
_filter_pov_labeled_clauses(_natural_clothing_state(row.get("hardcore_clothing_state")), pov_labels),
|
||||
hard_labels,
|
||||
),
|
||||
hard_cast_prose,
|
||||
f"set in {hard_scene}" if hard_scene else "",
|
||||
_expression_phrase(hard_expression),
|
||||
|
||||
+124
-9
@@ -1906,6 +1906,17 @@ def _slot_expression_intensity(slot: dict[str, Any] | None) -> float | None:
|
||||
return intensity if intensity >= 0 else None
|
||||
|
||||
|
||||
def _slot_expression_intensity_for_phase(slot: dict[str, Any] | None, phase: str = "") -> float | None:
|
||||
if not slot or not _slot_expression_enabled(slot):
|
||||
return None
|
||||
phase_key = f"{phase}_expression_intensity" if phase in ("softcore", "hardcore") else ""
|
||||
if phase_key:
|
||||
intensity = _normalize_slot_expression_intensity(slot.get(phase_key))
|
||||
if intensity >= 0:
|
||||
return intensity
|
||||
return _slot_expression_intensity(slot)
|
||||
|
||||
|
||||
def _mean(values: list[float]) -> float:
|
||||
return sum(values) / len(values)
|
||||
|
||||
@@ -1915,6 +1926,7 @@ def _cast_expression_intensity_override(
|
||||
label_map: dict[str, dict[str, Any]],
|
||||
women_count: int,
|
||||
men_count: int,
|
||||
expression_phase: str = "",
|
||||
) -> tuple[float | None, str]:
|
||||
groups: list[tuple[str, list[str]]] = [
|
||||
("women", [f"Woman {chr(ord('A') + index)}" for index in range(max(0, women_count))]),
|
||||
@@ -1931,7 +1943,7 @@ def _cast_expression_intensity_override(
|
||||
continue
|
||||
if slot:
|
||||
matching_slots.append(slot)
|
||||
value = _slot_expression_intensity(slot)
|
||||
value = _slot_expression_intensity_for_phase(slot, expression_phase)
|
||||
if value is not None:
|
||||
values.append(value)
|
||||
value_labels.append(label)
|
||||
@@ -1954,6 +1966,7 @@ def _character_expression_entries(
|
||||
label_map: dict[str, dict[str, Any]],
|
||||
women_count: int,
|
||||
men_count: int,
|
||||
expression_phase: str = "",
|
||||
) -> list[str]:
|
||||
labels = [
|
||||
*[f"Woman {chr(ord('A') + index)}" for index in range(max(0, women_count))],
|
||||
@@ -1969,7 +1982,7 @@ def _character_expression_entries(
|
||||
continue
|
||||
if not _slot_expression_enabled(slot):
|
||||
continue
|
||||
intensity = _slot_expression_intensity(slot)
|
||||
intensity = _slot_expression_intensity_for_phase(slot, expression_phase)
|
||||
if intensity is None:
|
||||
intensity = fallback_intensity
|
||||
entries = _compatible_entries(
|
||||
@@ -2075,8 +2088,12 @@ def _normalize_character_slot(slot: dict[str, Any]) -> dict[str, Any]:
|
||||
"eyes": _slot_value(slot.get("eyes")),
|
||||
"descriptor_detail": _normalize_descriptor_detail(slot.get("descriptor_detail")),
|
||||
"presence_mode": _normalize_presence_mode(slot.get("presence_mode"), subject_type),
|
||||
"softcore_outfit": _slot_value(slot.get("softcore_outfit")),
|
||||
"hardcore_clothing": _slot_value(slot.get("hardcore_clothing") or slot.get("hardcore_outfit")),
|
||||
"expression_enabled": not _is_false(slot.get("expression_enabled", True)),
|
||||
"expression_intensity": _normalize_slot_expression_intensity(slot.get("expression_intensity")),
|
||||
"softcore_expression_intensity": _normalize_slot_expression_intensity(slot.get("softcore_expression_intensity")),
|
||||
"hardcore_expression_intensity": _normalize_slot_expression_intensity(slot.get("hardcore_expression_intensity")),
|
||||
}
|
||||
normalized["summary"] = _character_slot_summary(normalized)
|
||||
return normalized
|
||||
@@ -2129,6 +2146,16 @@ def _character_slot_summary(slot: dict[str, Any]) -> str:
|
||||
expression_intensity = _slot_expression_intensity(slot)
|
||||
if expression_intensity is not None:
|
||||
parts.append(f"expression={expression_intensity:.2f}")
|
||||
softcore_expression_intensity = _slot_expression_intensity_for_phase(slot, "softcore")
|
||||
hardcore_expression_intensity = _slot_expression_intensity_for_phase(slot, "hardcore")
|
||||
if softcore_expression_intensity is not None and softcore_expression_intensity != expression_intensity:
|
||||
parts.append(f"soft_expr={softcore_expression_intensity:.2f}")
|
||||
if hardcore_expression_intensity is not None and hardcore_expression_intensity != expression_intensity:
|
||||
parts.append(f"hard_expr={hardcore_expression_intensity:.2f}")
|
||||
if slot.get("softcore_outfit"):
|
||||
parts.append(f"soft_outfit={slot['softcore_outfit']}")
|
||||
if slot.get("hardcore_clothing"):
|
||||
parts.append(f"hard_clothing={slot['hardcore_clothing']}")
|
||||
for key in ("body_phrase", "skin", "hair", "eyes"):
|
||||
value = slot.get(key)
|
||||
if value:
|
||||
@@ -2155,6 +2182,10 @@ def build_character_slot_json(
|
||||
enabled: bool = True,
|
||||
character_cast: str | dict[str, Any] | list[Any] | None = "",
|
||||
presence_mode: str = "visible",
|
||||
softcore_expression_intensity: float = -1.0,
|
||||
hardcore_expression_intensity: float = -1.0,
|
||||
softcore_outfit: str = "",
|
||||
hardcore_clothing: str = "",
|
||||
) -> dict[str, str]:
|
||||
existing_slots = _parse_character_cast(character_cast)
|
||||
slot = _normalize_character_slot(
|
||||
@@ -2173,8 +2204,12 @@ def build_character_slot_json(
|
||||
"eyes": eyes,
|
||||
"descriptor_detail": descriptor_detail,
|
||||
"presence_mode": presence_mode,
|
||||
"softcore_outfit": softcore_outfit,
|
||||
"hardcore_clothing": hardcore_clothing,
|
||||
"expression_enabled": expression_enabled,
|
||||
"expression_intensity": expression_intensity,
|
||||
"softcore_expression_intensity": softcore_expression_intensity,
|
||||
"hardcore_expression_intensity": hardcore_expression_intensity,
|
||||
}
|
||||
)
|
||||
slots = existing_slots + ([slot] if enabled else [])
|
||||
@@ -2271,6 +2306,58 @@ def _pov_composition_prompt(composition: Any, pov_labels: list[str]) -> str:
|
||||
return _clean_prompt_punctuation(text)
|
||||
|
||||
|
||||
def _slot_softcore_outfit(slot: dict[str, Any] | None) -> str:
|
||||
return _slot_value(slot.get("softcore_outfit")) if slot else ""
|
||||
|
||||
|
||||
def _slot_hardcore_clothing(slot: dict[str, Any] | None) -> str:
|
||||
return _slot_value(slot.get("hardcore_clothing")) if slot else ""
|
||||
|
||||
|
||||
def _softcore_outfit_sentence(label: str, outfit: str) -> str:
|
||||
outfit = str(outfit or "").strip()
|
||||
if not outfit:
|
||||
return ""
|
||||
lower = outfit.lower()
|
||||
if lower.startswith(("wears ", "wearing ", "in ")):
|
||||
return f"{label} {outfit}"
|
||||
return f"{label} wears {outfit}"
|
||||
|
||||
|
||||
def _hardcore_clothing_sentence(label: str, clothing: str) -> str:
|
||||
clothing = str(clothing or "").strip().rstrip(".")
|
||||
if not clothing:
|
||||
return ""
|
||||
lower = clothing.lower()
|
||||
if lower.startswith(("is ", "wears ", "wearing ", "keeps ", "has ", "with ")):
|
||||
return f"{label} {clothing}"
|
||||
if lower.startswith(("fully nude", "nude", "partly nude")):
|
||||
return f"{label} is {clothing}"
|
||||
return f"{label}'s clothing: {clothing}"
|
||||
|
||||
|
||||
def _character_hardcore_clothing_entries(
|
||||
label_map: dict[str, dict[str, Any]],
|
||||
women_count: int,
|
||||
men_count: int,
|
||||
pov_labels: list[str] | None = None,
|
||||
) -> list[str]:
|
||||
pov_set = set(pov_labels or [])
|
||||
labels = [
|
||||
*[f"Woman {chr(ord('A') + index)}" for index in range(max(0, women_count))],
|
||||
*[f"Man {chr(ord('A') + index)}" for index in range(max(0, men_count))],
|
||||
]
|
||||
entries: list[str] = []
|
||||
for label in labels:
|
||||
if label in pov_set:
|
||||
continue
|
||||
clothing = _slot_hardcore_clothing(label_map.get(label))
|
||||
sentence = _hardcore_clothing_sentence(label, clothing)
|
||||
if sentence:
|
||||
entries.append(sentence)
|
||||
return entries
|
||||
|
||||
|
||||
def _context_from_character_slot(
|
||||
rng: random.Random,
|
||||
slot: dict[str, Any],
|
||||
@@ -3303,6 +3390,7 @@ def _build_custom_row(
|
||||
expression_intensity: float,
|
||||
character_profile: str | dict[str, Any] | None = None,
|
||||
character_cast: str | dict[str, Any] | list[Any] | None = None,
|
||||
expression_phase: str = "",
|
||||
) -> dict[str, Any]:
|
||||
categories = load_category_library()
|
||||
category_rng = _axis_rng(seed_config, "category", seed, row_number)
|
||||
@@ -3382,7 +3470,7 @@ def _build_custom_row(
|
||||
expression_disabled = True
|
||||
expression_intensity_source = f"character_slot:{slot_label}:disabled"
|
||||
else:
|
||||
slot_expression_intensity = _slot_expression_intensity(applied_slot)
|
||||
slot_expression_intensity = _slot_expression_intensity_for_phase(applied_slot, expression_phase)
|
||||
if slot_expression_intensity is not None:
|
||||
expression_intensity = slot_expression_intensity
|
||||
expression_intensity_source = f"character_slot:{slot_label}"
|
||||
@@ -3392,6 +3480,7 @@ def _build_custom_row(
|
||||
character_slot_map,
|
||||
women_count,
|
||||
men_count,
|
||||
expression_phase,
|
||||
)
|
||||
if expression_intensity is None:
|
||||
expression_disabled = True
|
||||
@@ -3439,6 +3528,7 @@ def _build_custom_row(
|
||||
character_slot_map,
|
||||
women_count,
|
||||
men_count,
|
||||
expression_phase,
|
||||
)
|
||||
character_expression_text = "; ".join(character_expressions)
|
||||
if character_expression_text:
|
||||
@@ -3616,6 +3706,7 @@ def build_prompt(
|
||||
character_profile: str | dict[str, Any] | None = None,
|
||||
character_cast: str | dict[str, Any] | list[Any] | None = None,
|
||||
expression_enabled: bool = True,
|
||||
expression_phase: str = "",
|
||||
) -> dict[str, Any]:
|
||||
apply_pool_extensions()
|
||||
row_number = max(1, int(row_number))
|
||||
@@ -3683,6 +3774,7 @@ def build_prompt(
|
||||
expression_intensity,
|
||||
character_profile,
|
||||
character_cast,
|
||||
expression_phase,
|
||||
)
|
||||
|
||||
if not expression_enabled:
|
||||
@@ -4155,6 +4247,7 @@ def _insta_of_partner_styling(
|
||||
women_count: int,
|
||||
men_count: int,
|
||||
pov_labels: list[str] | None = None,
|
||||
label_map: dict[str, dict[str, Any]] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
content_rng = _axis_rng(seed_config, "content", seed, row_number + 421)
|
||||
pose_rng = _axis_rng(seed_config, "pose", seed, row_number + 421)
|
||||
@@ -4162,12 +4255,20 @@ def _insta_of_partner_styling(
|
||||
outfits: list[str] = []
|
||||
for index in range(max(0, women_count - 1)):
|
||||
label = chr(ord("B") + index)
|
||||
outfits.append(f"Woman {label} wears {g.choose(content_rng, INSTA_OF_SOFTCORE_PARTNER_WOMEN_OUTFITS)}")
|
||||
full_label = f"Woman {label}"
|
||||
outfit = _slot_softcore_outfit((label_map or {}).get(full_label)) or g.choose(content_rng, INSTA_OF_SOFTCORE_PARTNER_WOMEN_OUTFITS)
|
||||
sentence = _softcore_outfit_sentence(full_label, outfit)
|
||||
if sentence:
|
||||
outfits.append(sentence)
|
||||
for index in range(max(0, men_count)):
|
||||
label = chr(ord("A") + index)
|
||||
if f"Man {label}" in pov_set:
|
||||
full_label = f"Man {label}"
|
||||
if full_label in pov_set:
|
||||
continue
|
||||
outfits.append(f"Man {label} wears {g.choose(content_rng, INSTA_OF_SOFTCORE_PARTNER_MEN_OUTFITS)}")
|
||||
outfit = _slot_softcore_outfit((label_map or {}).get(full_label)) or g.choose(content_rng, INSTA_OF_SOFTCORE_PARTNER_MEN_OUTFITS)
|
||||
sentence = _softcore_outfit_sentence(full_label, outfit)
|
||||
if sentence:
|
||||
outfits.append(sentence)
|
||||
return {
|
||||
"outfits": outfits,
|
||||
"pose": g.choose(pose_rng, SOFTCORE_CAST_POSES),
|
||||
@@ -4225,6 +4326,7 @@ def build_insta_of_pair(
|
||||
character_slot_map,
|
||||
soft_expression_women_count,
|
||||
soft_expression_men_count,
|
||||
"softcore",
|
||||
)
|
||||
if soft_expression_intensity is None:
|
||||
soft_expression_enabled = False
|
||||
@@ -4277,11 +4379,12 @@ def build_insta_of_pair(
|
||||
soft_row["character_slot_status"] = "applied:Woman A"
|
||||
if not soft_expression_enabled:
|
||||
soft_row = _disable_row_expression(soft_row, soft_expression_intensity_source)
|
||||
soft_row["item"] = _insta_of_softcore_outfit(soft_content_rng, softcore_level_key)
|
||||
primary_softcore_outfit = _slot_softcore_outfit(primary_slot)
|
||||
soft_row["item"] = primary_softcore_outfit or _insta_of_softcore_outfit(soft_content_rng, softcore_level_key)
|
||||
soft_row["pose"] = _insta_of_softcore_pose(soft_content_rng, softcore_level_key)
|
||||
soft_row["item_label"] = "Insta/OF softcore outfit"
|
||||
soft_row["custom_item"] = "insta_of_softcore_outfit"
|
||||
soft_row["softcore_outfit_policy"] = "insta_of_safe_softcore"
|
||||
soft_row["softcore_outfit_policy"] = "character_slot:Woman A" if primary_softcore_outfit else "insta_of_safe_softcore"
|
||||
soft_row["pov_character_labels"] = (
|
||||
pov_character_labels
|
||||
if options["softcore_cast"] == "same_as_hardcore"
|
||||
@@ -4319,6 +4422,7 @@ def build_insta_of_pair(
|
||||
expression_enabled=options["hardcore_expression_enabled"],
|
||||
expression_intensity=options["hardcore_expression_intensity"],
|
||||
character_cast=character_cast or "",
|
||||
expression_phase="hardcore",
|
||||
)
|
||||
hard_row["hardcore_detail_density"] = options["hardcore_detail_density"]
|
||||
hard_row["pov_character_labels"] = pov_character_labels
|
||||
@@ -4351,6 +4455,7 @@ def build_insta_of_pair(
|
||||
hard_women_count if options["softcore_cast"] == "same_as_hardcore" else 1,
|
||||
hard_men_count if options["softcore_cast"] == "same_as_hardcore" else 0,
|
||||
pov_character_labels if options["softcore_cast"] == "same_as_hardcore" else [],
|
||||
character_slot_map,
|
||||
)
|
||||
if options["softcore_cast"] != "same_as_hardcore":
|
||||
soft_partner_styling = {"outfits": [], "pose": ""}
|
||||
@@ -4394,10 +4499,19 @@ def build_insta_of_pair(
|
||||
else ""
|
||||
)
|
||||
hard_cast = _insta_of_cast_phrase(hard_women_count, hard_men_count)
|
||||
hard_clothing_state = _insta_of_hardcore_clothing_state(
|
||||
character_hardcore_clothing_entries = _character_hardcore_clothing_entries(
|
||||
character_slot_map,
|
||||
hard_women_count,
|
||||
hard_men_count,
|
||||
pov_character_labels,
|
||||
)
|
||||
has_primary_hardcore_clothing = any(entry.startswith("Woman A") for entry in character_hardcore_clothing_entries)
|
||||
fallback_hard_clothing_state = "" if has_primary_hardcore_clothing else _insta_of_hardcore_clothing_state(
|
||||
options["hardcore_clothing_continuity"],
|
||||
soft_row["item"],
|
||||
)
|
||||
hard_clothing_parts = [part for part in (fallback_hard_clothing_state, *character_hardcore_clothing_entries) if part]
|
||||
hard_clothing_state = " ".join(hard_clothing_parts)
|
||||
hard_detail_density = options["hardcore_detail_density"]
|
||||
hard_detail_directive = {
|
||||
"compact": "Use one compact position-first sexual action sentence; avoid repeated aftermath wording. ",
|
||||
@@ -4482,6 +4596,7 @@ def build_insta_of_pair(
|
||||
"pov_character_labels": pov_character_labels,
|
||||
"pov_prompt_directive": pov_directive,
|
||||
"softcore_partner_styling": soft_partner_styling,
|
||||
"character_hardcore_clothing": character_hardcore_clothing_entries,
|
||||
"hardcore_clothing_state": hard_clothing_state,
|
||||
"hardcore_detail_density": hard_detail_density,
|
||||
"softcore_prompt": soft_prompt,
|
||||
|
||||
Reference in New Issue
Block a user