Add Park Minseo gym date CG progression
This commit is contained in:
@@ -32,6 +32,14 @@ Base standing images should feel like high-impact adult romance game character a
|
||||
- `song_arin_glossy_v1.png`: accepted glossy anime base asset.
|
||||
- `style_references/glossy_anime_fallback_reference.png`: user-approved reference for the current target look.
|
||||
|
||||
## Date CG Files
|
||||
|
||||
- `date_cg/park_minseo_fitness_affection_40_v1.png`: accepted Park Minseo gym date CG, friendly treadmill stage.
|
||||
- `date_cg/park_minseo_fitness_affection_60_v1.png`: accepted Park Minseo gym date CG, affectionate seated cable-row stage.
|
||||
- `date_cg/park_minseo_fitness_affection_80_v1.png`: accepted Park Minseo gym date CG, intense lying leg-curl stage.
|
||||
- `date_cg/park_minseo_fitness_affection_100_v1.png`: accepted Park Minseo gym date CG, high-tension cable-station stage.
|
||||
- Park Minseo affection `>100` remains `needs_revision`; reject candidates with ambiguous leg anatomy or insufficient final-tier intensity.
|
||||
|
||||
## Legacy Comparison Files
|
||||
|
||||
- `han_seoyun_v1.png`: earlier semi-realistic style/composition candidate; same-face risk remains.
|
||||
@@ -52,6 +60,7 @@ Base standing images should feel like high-impact adult romance game character a
|
||||
|
||||
- Use `<character_id>_glossy_v<number>.png` for current glossy anime base candidates.
|
||||
- Use `<character_id>_v<number>.png` for legacy semi-realistic candidates.
|
||||
- Use `date_cg/<character_id>_<location>_affection_<max_affection>_v<number>.png` for accepted date CG progression assets.
|
||||
- Use `style_tests/<character_id>_<style>_test.png` for experiments.
|
||||
- Keep generated originals in their default generator folder; copy selected candidates into this project.
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.3 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.1 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.1 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.2 MiB |
@@ -149,5 +149,59 @@
|
||||
"path": "assets/characters/song_arin_glossy_v1.png",
|
||||
"notes": "Glossy anime replacement accepted. Second-chance route uses short wavy bob, careful smile, knit dress, and quiet interior-shop warmth."
|
||||
}
|
||||
],
|
||||
"date_cgs": [
|
||||
{
|
||||
"character_id": "park_minseo",
|
||||
"location_id": "fitness_center",
|
||||
"set_id": "park_minseo_fitness_affection",
|
||||
"style": "glossy_anime_romance",
|
||||
"status": "partial",
|
||||
"notes": "Gym date CG progression for Park Minseo. The >100 tier needs a stronger replacement; the first generated candidate was rejected for ambiguous leg anatomy and insufficient final-tier intensity.",
|
||||
"tiers": [
|
||||
{
|
||||
"affection_min": 0,
|
||||
"affection_max": 20,
|
||||
"status": "accepted",
|
||||
"path": "assets/characters/park_minseo_glossy_v1.png",
|
||||
"notes": "Base image reused for early affection."
|
||||
},
|
||||
{
|
||||
"affection_min": 21,
|
||||
"affection_max": 40,
|
||||
"status": "accepted",
|
||||
"path": "assets/characters/date_cg/park_minseo_fitness_affection_40_v1.png",
|
||||
"notes": "Treadmill run, friendly smile, workout sweat, no romantic arousal."
|
||||
},
|
||||
{
|
||||
"affection_min": 41,
|
||||
"affection_max": 60,
|
||||
"status": "accepted",
|
||||
"path": "assets/characters/date_cg/park_minseo_fitness_affection_60_v1.png",
|
||||
"notes": "Seated cable-row pose with affectionate eye contact and light blush."
|
||||
},
|
||||
{
|
||||
"affection_min": 61,
|
||||
"affection_max": 80,
|
||||
"status": "accepted",
|
||||
"path": "assets/characters/date_cg/park_minseo_fitness_affection_80_v1.png",
|
||||
"notes": "Lying leg-curl pose with intense blush, sweat, and workout tension."
|
||||
},
|
||||
{
|
||||
"affection_min": 81,
|
||||
"affection_max": 100,
|
||||
"status": "accepted",
|
||||
"path": "assets/characters/date_cg/park_minseo_fitness_affection_100_v1.png",
|
||||
"notes": "Late-gym cable-station pose with loose resistance band, torn sleeve detail, and high romantic tension."
|
||||
},
|
||||
{
|
||||
"affection_min": 101,
|
||||
"affection_max": null,
|
||||
"status": "needs_revision",
|
||||
"path": null,
|
||||
"notes": "Regenerate with a clearer two-leg composition and more adult final-tier visual intensity while staying non-explicit."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -43,6 +43,18 @@ def png_dimensions(path: Path) -> tuple[int, int]:
|
||||
return width, height
|
||||
|
||||
|
||||
def validate_asset_image(path: Path, min_width: int, min_height: int, label: str) -> None:
|
||||
if not path.exists():
|
||||
fail(f"{label} asset is missing: {path}")
|
||||
|
||||
width, height = png_dimensions(path)
|
||||
if width < min_width or height < min_height:
|
||||
fail(
|
||||
f"{label} asset is too small: "
|
||||
f"{width}x{height}, minimum {min_width}x{min_height}"
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
characters_doc = load_json(DATA / "characters.json")
|
||||
assets_doc = load_json(DATA / "character_assets.json")
|
||||
@@ -90,24 +102,68 @@ def main() -> None:
|
||||
if "path" not in entry:
|
||||
fail(f"{entry['character_id']} has status {status} but no path")
|
||||
|
||||
path = ROOT / entry["path"]
|
||||
if not path.exists():
|
||||
fail(f"{entry['character_id']} asset is missing: {path}")
|
||||
|
||||
width, height = png_dimensions(path)
|
||||
if width < min_width or height < min_height:
|
||||
fail(
|
||||
f"{entry['character_id']} asset is too small: "
|
||||
f"{width}x{height}, minimum {min_width}x{min_height}"
|
||||
)
|
||||
validate_asset_image(
|
||||
ROOT / entry["path"],
|
||||
min_width,
|
||||
min_height,
|
||||
entry["character_id"],
|
||||
)
|
||||
|
||||
revision_count = sum(
|
||||
1 for entry in asset_entries if entry["status"] == "needs_revision"
|
||||
)
|
||||
|
||||
date_cg_accepted = 0
|
||||
date_cg_revision_count = 0
|
||||
for cg_set in assets_doc.get("date_cgs", []):
|
||||
character_id = cg_set["character_id"]
|
||||
if character_id not in character_id_set:
|
||||
fail(f"date CG set references missing character id: {character_id}")
|
||||
|
||||
if cg_set["status"] not in {"accepted", "partial", "needs_revision", "pending", "rejected"}:
|
||||
fail(f"{cg_set['set_id']} has invalid status {cg_set['status']}")
|
||||
|
||||
previous_max = -1
|
||||
for tier in cg_set["tiers"]:
|
||||
status = tier["status"]
|
||||
if status not in {"accepted", "needs_revision", "pending", "rejected"}:
|
||||
fail(f"{cg_set['set_id']} tier has invalid status {status}")
|
||||
|
||||
affection_min = tier["affection_min"]
|
||||
affection_max = tier["affection_max"]
|
||||
if affection_min <= previous_max:
|
||||
fail(f"{cg_set['set_id']} has overlapping affection tiers")
|
||||
if affection_max is not None and affection_max < affection_min:
|
||||
fail(f"{cg_set['set_id']} has an invalid affection tier range")
|
||||
previous_max = affection_max if affection_max is not None else affection_min
|
||||
|
||||
path_value = tier.get("path")
|
||||
if status == "accepted":
|
||||
date_cg_accepted += 1
|
||||
if not path_value:
|
||||
fail(f"{cg_set['set_id']} accepted tier lacks a path")
|
||||
validate_asset_image(
|
||||
ROOT / path_value,
|
||||
min_width,
|
||||
min_height,
|
||||
f"{cg_set['set_id']} affection {affection_min}",
|
||||
)
|
||||
elif status == "needs_revision":
|
||||
date_cg_revision_count += 1
|
||||
if path_value:
|
||||
validate_asset_image(
|
||||
ROOT / path_value,
|
||||
min_width,
|
||||
min_height,
|
||||
f"{cg_set['set_id']} revision candidate {affection_min}",
|
||||
)
|
||||
|
||||
print(
|
||||
"Validated character asset manifest: "
|
||||
f"{accepted}/{len(asset_entries)} accepted, "
|
||||
f"{revision_count} needs revision."
|
||||
f"{revision_count} needs revision, "
|
||||
f"{date_cg_accepted} accepted date CG tiers, "
|
||||
f"{date_cg_revision_count} date CG tiers need revision."
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user