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
|
per-character expression text such as `Woman A has ...; Man A has ...`; Krea
|
||||||
formatting naturalizes those labels in pair prompts.
|
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
|
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,
|
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:
|
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
|
microwear, or shirtless partner styling. `explicit_nude` is available when
|
||||||
you want visible nude creator-shot framing without a sex act.
|
you want visible nude creator-shot framing without a sex act.
|
||||||
- `hardcore_level`: `explicit` or `hardcore`.
|
- `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.
|
`1.0` strongly favors more heated softcore faces.
|
||||||
- `hardcore_expression_intensity`: `0.0` is controlled, `0.5` is balanced
|
- `hardcore_expression_intensity`: fallback when no connected character slot
|
||||||
hardcore, `1.0` strongly favors ahegao-style, drooling, fucked-out, climax,
|
sets `hardcore_expression_intensity`. `0.0` is controlled, `0.5` is balanced
|
||||||
and messy orgasm expressions.
|
hardcore, `1.0` strongly favors stronger hardcore expressions.
|
||||||
- `softcore_expression_enabled` and `hardcore_expression_enabled`: disable the
|
- `softcore_expression_enabled` and `hardcore_expression_enabled`: disable the
|
||||||
expression sentence for that half of the Insta/OF pair. The intensity values
|
expression sentence for that half of the Insta/OF pair. The intensity values
|
||||||
are fallbacks; `SxCP Woman Slot` / `SxCP Man Slot` `expression_intensity`
|
are fallbacks; `SxCP Woman Slot` / `SxCP Man Slot` `expression_intensity`
|
||||||
@@ -328,7 +340,8 @@ Options:
|
|||||||
- `hardcore_clothing_continuity`: `none`, `same_outfit`, `partially_removed`,
|
- `hardcore_clothing_continuity`: `none`, `same_outfit`, `partially_removed`,
|
||||||
`implied_nude`, or `explicit_nude`. This controls whether the hardcore prompt
|
`implied_nude`, or `explicit_nude`. This controls whether the hardcore prompt
|
||||||
references the softcore outfit, uses it displaced/removed, or makes Woman A
|
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.
|
- `softcore_camera_mode`: base camera mode for the softcore output.
|
||||||
- `hardcore_camera_mode`: `from_camera_config`, `same_as_softcore`, or a
|
- `hardcore_camera_mode`: `from_camera_config`, `same_as_softcore`, or a
|
||||||
separate base camera mode for the hardcore output. `from_camera_config` is
|
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_enabled": ("BOOLEAN", {"default": True}),
|
||||||
"expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
"expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||||
"presence_mode": (character_presence_choices(), {"default": "visible"}),
|
"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": {
|
"optional": {
|
||||||
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
||||||
@@ -644,6 +648,10 @@ class SxCPCharacterSlot:
|
|||||||
expression_enabled=True,
|
expression_enabled=True,
|
||||||
expression_intensity=-1.0,
|
expression_intensity=-1.0,
|
||||||
presence_mode="visible",
|
presence_mode="visible",
|
||||||
|
softcore_expression_intensity=-1.0,
|
||||||
|
hardcore_expression_intensity=-1.0,
|
||||||
|
softcore_outfit="",
|
||||||
|
hardcore_clothing="",
|
||||||
character_cast="",
|
character_cast="",
|
||||||
):
|
):
|
||||||
result = build_character_slot_json(
|
result = build_character_slot_json(
|
||||||
@@ -663,6 +671,10 @@ class SxCPCharacterSlot:
|
|||||||
expression_enabled=expression_enabled,
|
expression_enabled=expression_enabled,
|
||||||
expression_intensity=expression_intensity,
|
expression_intensity=expression_intensity,
|
||||||
presence_mode=presence_mode,
|
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,
|
enabled=enabled,
|
||||||
character_cast=character_cast or "",
|
character_cast=character_cast or "",
|
||||||
)
|
)
|
||||||
@@ -689,6 +701,10 @@ class SxCPWomanSlot:
|
|||||||
"descriptor_detail": (character_descriptor_detail_choices(), {"default": "auto"}),
|
"descriptor_detail": (character_descriptor_detail_choices(), {"default": "auto"}),
|
||||||
"expression_enabled": ("BOOLEAN", {"default": True}),
|
"expression_enabled": ("BOOLEAN", {"default": True}),
|
||||||
"expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
"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": {
|
"optional": {
|
||||||
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
||||||
@@ -717,6 +733,10 @@ class SxCPWomanSlot:
|
|||||||
descriptor_detail="auto",
|
descriptor_detail="auto",
|
||||||
expression_enabled=True,
|
expression_enabled=True,
|
||||||
expression_intensity=-1.0,
|
expression_intensity=-1.0,
|
||||||
|
softcore_expression_intensity=-1.0,
|
||||||
|
hardcore_expression_intensity=-1.0,
|
||||||
|
softcore_outfit="",
|
||||||
|
hardcore_clothing="",
|
||||||
character_cast="",
|
character_cast="",
|
||||||
):
|
):
|
||||||
result = build_character_slot_json(
|
result = build_character_slot_json(
|
||||||
@@ -735,6 +755,10 @@ class SxCPWomanSlot:
|
|||||||
descriptor_detail=descriptor_detail,
|
descriptor_detail=descriptor_detail,
|
||||||
expression_enabled=expression_enabled,
|
expression_enabled=expression_enabled,
|
||||||
expression_intensity=expression_intensity,
|
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,
|
enabled=enabled,
|
||||||
character_cast=character_cast or "",
|
character_cast=character_cast or "",
|
||||||
)
|
)
|
||||||
@@ -761,6 +785,10 @@ class SxCPManSlot:
|
|||||||
"expression_enabled": ("BOOLEAN", {"default": True}),
|
"expression_enabled": ("BOOLEAN", {"default": True}),
|
||||||
"expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
"expression_intensity": ("FLOAT", {"default": -1.0, "min": -1.0, "max": 1.0, "step": 0.01}),
|
||||||
"presence_mode": (character_presence_choices(), {"default": "visible"}),
|
"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": {
|
"optional": {
|
||||||
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
"character_cast": ("STRING", {"default": "", "multiline": True}),
|
||||||
@@ -789,6 +817,10 @@ class SxCPManSlot:
|
|||||||
expression_enabled=True,
|
expression_enabled=True,
|
||||||
expression_intensity=-1.0,
|
expression_intensity=-1.0,
|
||||||
presence_mode="visible",
|
presence_mode="visible",
|
||||||
|
softcore_expression_intensity=-1.0,
|
||||||
|
hardcore_expression_intensity=-1.0,
|
||||||
|
softcore_outfit="",
|
||||||
|
hardcore_clothing="",
|
||||||
character_cast="",
|
character_cast="",
|
||||||
):
|
):
|
||||||
result = build_character_slot_json(
|
result = build_character_slot_json(
|
||||||
@@ -808,6 +840,10 @@ class SxCPManSlot:
|
|||||||
expression_enabled=expression_enabled,
|
expression_enabled=expression_enabled,
|
||||||
expression_intensity=expression_intensity,
|
expression_intensity=expression_intensity,
|
||||||
presence_mode=presence_mode,
|
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,
|
enabled=enabled,
|
||||||
character_cast=character_cast or "",
|
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"\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"\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"\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)
|
rendered = re.sub(r"\bthe POV viewer is positioned\b", "the POV camera is positioned", rendered, flags=re.IGNORECASE)
|
||||||
return rendered
|
return rendered
|
||||||
|
|
||||||
@@ -1595,7 +1607,10 @@ def _insta_pair_to_krea(row: dict[str, Any], detail_level: str, style_mode: str)
|
|||||||
hard_parts = [
|
hard_parts = [
|
||||||
hard_action,
|
hard_action,
|
||||||
_pov_camera_phrase(pov_labels),
|
_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,
|
hard_cast_prose,
|
||||||
f"set in {hard_scene}" if hard_scene else "",
|
f"set in {hard_scene}" if hard_scene else "",
|
||||||
_expression_phrase(hard_expression),
|
_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
|
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:
|
def _mean(values: list[float]) -> float:
|
||||||
return sum(values) / len(values)
|
return sum(values) / len(values)
|
||||||
|
|
||||||
@@ -1915,6 +1926,7 @@ def _cast_expression_intensity_override(
|
|||||||
label_map: dict[str, dict[str, Any]],
|
label_map: dict[str, dict[str, Any]],
|
||||||
women_count: int,
|
women_count: int,
|
||||||
men_count: int,
|
men_count: int,
|
||||||
|
expression_phase: str = "",
|
||||||
) -> tuple[float | None, str]:
|
) -> tuple[float | None, str]:
|
||||||
groups: list[tuple[str, list[str]]] = [
|
groups: list[tuple[str, list[str]]] = [
|
||||||
("women", [f"Woman {chr(ord('A') + index)}" for index in range(max(0, women_count))]),
|
("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
|
continue
|
||||||
if slot:
|
if slot:
|
||||||
matching_slots.append(slot)
|
matching_slots.append(slot)
|
||||||
value = _slot_expression_intensity(slot)
|
value = _slot_expression_intensity_for_phase(slot, expression_phase)
|
||||||
if value is not None:
|
if value is not None:
|
||||||
values.append(value)
|
values.append(value)
|
||||||
value_labels.append(label)
|
value_labels.append(label)
|
||||||
@@ -1954,6 +1966,7 @@ def _character_expression_entries(
|
|||||||
label_map: dict[str, dict[str, Any]],
|
label_map: dict[str, dict[str, Any]],
|
||||||
women_count: int,
|
women_count: int,
|
||||||
men_count: int,
|
men_count: int,
|
||||||
|
expression_phase: str = "",
|
||||||
) -> list[str]:
|
) -> list[str]:
|
||||||
labels = [
|
labels = [
|
||||||
*[f"Woman {chr(ord('A') + index)}" for index in range(max(0, women_count))],
|
*[f"Woman {chr(ord('A') + index)}" for index in range(max(0, women_count))],
|
||||||
@@ -1969,7 +1982,7 @@ def _character_expression_entries(
|
|||||||
continue
|
continue
|
||||||
if not _slot_expression_enabled(slot):
|
if not _slot_expression_enabled(slot):
|
||||||
continue
|
continue
|
||||||
intensity = _slot_expression_intensity(slot)
|
intensity = _slot_expression_intensity_for_phase(slot, expression_phase)
|
||||||
if intensity is None:
|
if intensity is None:
|
||||||
intensity = fallback_intensity
|
intensity = fallback_intensity
|
||||||
entries = _compatible_entries(
|
entries = _compatible_entries(
|
||||||
@@ -2075,8 +2088,12 @@ def _normalize_character_slot(slot: dict[str, Any]) -> dict[str, Any]:
|
|||||||
"eyes": _slot_value(slot.get("eyes")),
|
"eyes": _slot_value(slot.get("eyes")),
|
||||||
"descriptor_detail": _normalize_descriptor_detail(slot.get("descriptor_detail")),
|
"descriptor_detail": _normalize_descriptor_detail(slot.get("descriptor_detail")),
|
||||||
"presence_mode": _normalize_presence_mode(slot.get("presence_mode"), subject_type),
|
"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_enabled": not _is_false(slot.get("expression_enabled", True)),
|
||||||
"expression_intensity": _normalize_slot_expression_intensity(slot.get("expression_intensity")),
|
"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)
|
normalized["summary"] = _character_slot_summary(normalized)
|
||||||
return normalized
|
return normalized
|
||||||
@@ -2129,6 +2146,16 @@ def _character_slot_summary(slot: dict[str, Any]) -> str:
|
|||||||
expression_intensity = _slot_expression_intensity(slot)
|
expression_intensity = _slot_expression_intensity(slot)
|
||||||
if expression_intensity is not None:
|
if expression_intensity is not None:
|
||||||
parts.append(f"expression={expression_intensity:.2f}")
|
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"):
|
for key in ("body_phrase", "skin", "hair", "eyes"):
|
||||||
value = slot.get(key)
|
value = slot.get(key)
|
||||||
if value:
|
if value:
|
||||||
@@ -2155,6 +2182,10 @@ def build_character_slot_json(
|
|||||||
enabled: bool = True,
|
enabled: bool = True,
|
||||||
character_cast: str | dict[str, Any] | list[Any] | None = "",
|
character_cast: str | dict[str, Any] | list[Any] | None = "",
|
||||||
presence_mode: str = "visible",
|
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]:
|
) -> dict[str, str]:
|
||||||
existing_slots = _parse_character_cast(character_cast)
|
existing_slots = _parse_character_cast(character_cast)
|
||||||
slot = _normalize_character_slot(
|
slot = _normalize_character_slot(
|
||||||
@@ -2173,8 +2204,12 @@ def build_character_slot_json(
|
|||||||
"eyes": eyes,
|
"eyes": eyes,
|
||||||
"descriptor_detail": descriptor_detail,
|
"descriptor_detail": descriptor_detail,
|
||||||
"presence_mode": presence_mode,
|
"presence_mode": presence_mode,
|
||||||
|
"softcore_outfit": softcore_outfit,
|
||||||
|
"hardcore_clothing": hardcore_clothing,
|
||||||
"expression_enabled": expression_enabled,
|
"expression_enabled": expression_enabled,
|
||||||
"expression_intensity": expression_intensity,
|
"expression_intensity": expression_intensity,
|
||||||
|
"softcore_expression_intensity": softcore_expression_intensity,
|
||||||
|
"hardcore_expression_intensity": hardcore_expression_intensity,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
slots = existing_slots + ([slot] if enabled else [])
|
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)
|
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(
|
def _context_from_character_slot(
|
||||||
rng: random.Random,
|
rng: random.Random,
|
||||||
slot: dict[str, Any],
|
slot: dict[str, Any],
|
||||||
@@ -3303,6 +3390,7 @@ def _build_custom_row(
|
|||||||
expression_intensity: float,
|
expression_intensity: float,
|
||||||
character_profile: str | dict[str, Any] | None = None,
|
character_profile: str | dict[str, Any] | None = None,
|
||||||
character_cast: str | dict[str, Any] | list[Any] | None = None,
|
character_cast: str | dict[str, Any] | list[Any] | None = None,
|
||||||
|
expression_phase: str = "",
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
categories = load_category_library()
|
categories = load_category_library()
|
||||||
category_rng = _axis_rng(seed_config, "category", seed, row_number)
|
category_rng = _axis_rng(seed_config, "category", seed, row_number)
|
||||||
@@ -3382,7 +3470,7 @@ def _build_custom_row(
|
|||||||
expression_disabled = True
|
expression_disabled = True
|
||||||
expression_intensity_source = f"character_slot:{slot_label}:disabled"
|
expression_intensity_source = f"character_slot:{slot_label}:disabled"
|
||||||
else:
|
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:
|
if slot_expression_intensity is not None:
|
||||||
expression_intensity = slot_expression_intensity
|
expression_intensity = slot_expression_intensity
|
||||||
expression_intensity_source = f"character_slot:{slot_label}"
|
expression_intensity_source = f"character_slot:{slot_label}"
|
||||||
@@ -3392,6 +3480,7 @@ def _build_custom_row(
|
|||||||
character_slot_map,
|
character_slot_map,
|
||||||
women_count,
|
women_count,
|
||||||
men_count,
|
men_count,
|
||||||
|
expression_phase,
|
||||||
)
|
)
|
||||||
if expression_intensity is None:
|
if expression_intensity is None:
|
||||||
expression_disabled = True
|
expression_disabled = True
|
||||||
@@ -3439,6 +3528,7 @@ def _build_custom_row(
|
|||||||
character_slot_map,
|
character_slot_map,
|
||||||
women_count,
|
women_count,
|
||||||
men_count,
|
men_count,
|
||||||
|
expression_phase,
|
||||||
)
|
)
|
||||||
character_expression_text = "; ".join(character_expressions)
|
character_expression_text = "; ".join(character_expressions)
|
||||||
if character_expression_text:
|
if character_expression_text:
|
||||||
@@ -3616,6 +3706,7 @@ def build_prompt(
|
|||||||
character_profile: str | dict[str, Any] | None = None,
|
character_profile: str | dict[str, Any] | None = None,
|
||||||
character_cast: str | dict[str, Any] | list[Any] | None = None,
|
character_cast: str | dict[str, Any] | list[Any] | None = None,
|
||||||
expression_enabled: bool = True,
|
expression_enabled: bool = True,
|
||||||
|
expression_phase: str = "",
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
apply_pool_extensions()
|
apply_pool_extensions()
|
||||||
row_number = max(1, int(row_number))
|
row_number = max(1, int(row_number))
|
||||||
@@ -3683,6 +3774,7 @@ def build_prompt(
|
|||||||
expression_intensity,
|
expression_intensity,
|
||||||
character_profile,
|
character_profile,
|
||||||
character_cast,
|
character_cast,
|
||||||
|
expression_phase,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not expression_enabled:
|
if not expression_enabled:
|
||||||
@@ -4155,6 +4247,7 @@ def _insta_of_partner_styling(
|
|||||||
women_count: int,
|
women_count: int,
|
||||||
men_count: int,
|
men_count: int,
|
||||||
pov_labels: list[str] | None = None,
|
pov_labels: list[str] | None = None,
|
||||||
|
label_map: dict[str, dict[str, Any]] | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
content_rng = _axis_rng(seed_config, "content", seed, row_number + 421)
|
content_rng = _axis_rng(seed_config, "content", seed, row_number + 421)
|
||||||
pose_rng = _axis_rng(seed_config, "pose", 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] = []
|
outfits: list[str] = []
|
||||||
for index in range(max(0, women_count - 1)):
|
for index in range(max(0, women_count - 1)):
|
||||||
label = chr(ord("B") + index)
|
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)):
|
for index in range(max(0, men_count)):
|
||||||
label = chr(ord("A") + index)
|
label = chr(ord("A") + index)
|
||||||
if f"Man {label}" in pov_set:
|
full_label = f"Man {label}"
|
||||||
|
if full_label in pov_set:
|
||||||
continue
|
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 {
|
return {
|
||||||
"outfits": outfits,
|
"outfits": outfits,
|
||||||
"pose": g.choose(pose_rng, SOFTCORE_CAST_POSES),
|
"pose": g.choose(pose_rng, SOFTCORE_CAST_POSES),
|
||||||
@@ -4225,6 +4326,7 @@ def build_insta_of_pair(
|
|||||||
character_slot_map,
|
character_slot_map,
|
||||||
soft_expression_women_count,
|
soft_expression_women_count,
|
||||||
soft_expression_men_count,
|
soft_expression_men_count,
|
||||||
|
"softcore",
|
||||||
)
|
)
|
||||||
if soft_expression_intensity is None:
|
if soft_expression_intensity is None:
|
||||||
soft_expression_enabled = False
|
soft_expression_enabled = False
|
||||||
@@ -4277,11 +4379,12 @@ def build_insta_of_pair(
|
|||||||
soft_row["character_slot_status"] = "applied:Woman A"
|
soft_row["character_slot_status"] = "applied:Woman A"
|
||||||
if not soft_expression_enabled:
|
if not soft_expression_enabled:
|
||||||
soft_row = _disable_row_expression(soft_row, soft_expression_intensity_source)
|
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["pose"] = _insta_of_softcore_pose(soft_content_rng, softcore_level_key)
|
||||||
soft_row["item_label"] = "Insta/OF softcore outfit"
|
soft_row["item_label"] = "Insta/OF softcore outfit"
|
||||||
soft_row["custom_item"] = "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"] = (
|
soft_row["pov_character_labels"] = (
|
||||||
pov_character_labels
|
pov_character_labels
|
||||||
if options["softcore_cast"] == "same_as_hardcore"
|
if options["softcore_cast"] == "same_as_hardcore"
|
||||||
@@ -4319,6 +4422,7 @@ def build_insta_of_pair(
|
|||||||
expression_enabled=options["hardcore_expression_enabled"],
|
expression_enabled=options["hardcore_expression_enabled"],
|
||||||
expression_intensity=options["hardcore_expression_intensity"],
|
expression_intensity=options["hardcore_expression_intensity"],
|
||||||
character_cast=character_cast or "",
|
character_cast=character_cast or "",
|
||||||
|
expression_phase="hardcore",
|
||||||
)
|
)
|
||||||
hard_row["hardcore_detail_density"] = options["hardcore_detail_density"]
|
hard_row["hardcore_detail_density"] = options["hardcore_detail_density"]
|
||||||
hard_row["pov_character_labels"] = pov_character_labels
|
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_women_count if options["softcore_cast"] == "same_as_hardcore" else 1,
|
||||||
hard_men_count if options["softcore_cast"] == "same_as_hardcore" else 0,
|
hard_men_count if options["softcore_cast"] == "same_as_hardcore" else 0,
|
||||||
pov_character_labels if options["softcore_cast"] == "same_as_hardcore" else [],
|
pov_character_labels if options["softcore_cast"] == "same_as_hardcore" else [],
|
||||||
|
character_slot_map,
|
||||||
)
|
)
|
||||||
if options["softcore_cast"] != "same_as_hardcore":
|
if options["softcore_cast"] != "same_as_hardcore":
|
||||||
soft_partner_styling = {"outfits": [], "pose": ""}
|
soft_partner_styling = {"outfits": [], "pose": ""}
|
||||||
@@ -4394,10 +4499,19 @@ def build_insta_of_pair(
|
|||||||
else ""
|
else ""
|
||||||
)
|
)
|
||||||
hard_cast = _insta_of_cast_phrase(hard_women_count, hard_men_count)
|
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"],
|
options["hardcore_clothing_continuity"],
|
||||||
soft_row["item"],
|
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_density = options["hardcore_detail_density"]
|
||||||
hard_detail_directive = {
|
hard_detail_directive = {
|
||||||
"compact": "Use one compact position-first sexual action sentence; avoid repeated aftermath wording. ",
|
"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_character_labels": pov_character_labels,
|
||||||
"pov_prompt_directive": pov_directive,
|
"pov_prompt_directive": pov_directive,
|
||||||
"softcore_partner_styling": soft_partner_styling,
|
"softcore_partner_styling": soft_partner_styling,
|
||||||
|
"character_hardcore_clothing": character_hardcore_clothing_entries,
|
||||||
"hardcore_clothing_state": hard_clothing_state,
|
"hardcore_clothing_state": hard_clothing_state,
|
||||||
"hardcore_detail_density": hard_detail_density,
|
"hardcore_detail_density": hard_detail_density,
|
||||||
"softcore_prompt": soft_prompt,
|
"softcore_prompt": soft_prompt,
|
||||||
|
|||||||
Reference in New Issue
Block a user