Tighten camera and pose diversity rules for card prompts

This commit is contained in:
2026-06-18 00:44:56 +09:00
parent d93c6b049b
commit 01f7a1bcf0
2 changed files with 49 additions and 40 deletions

View File

@@ -47,7 +47,7 @@ The game art direction is adult-oriented multi-world pinup:
- Medium through defeated stages may show clear combat-pain reactions from abdominal hits: gasping open mouth, hunched posture, labored breathing, tears, and small saliva/drool details. These must read as combat injury reactions, not sexual acts or fetish framing.
- High-damage stages should make the combat feel intense through missing or destroyed hand, foot, shoulder, abdomen, hip, and torso armor/clothing. Strong adult damaged-costume presentation should be used wherever possible, and the composition should be built to show it clearly.
- Adult pinup body language is strongly encouraged when it remains framed as battle damage, defeat, or damaged-costume card art. Use arched backs, twisted torsos, hip-forward stances, side-back views, exposed-leg kneeling, strained chest/shoulder posture, damaged-costume torso silhouettes, and defeated poses that emphasize mature curves. Do not depict intimate acts, penetration, minors, gore, or explicit closeups.
- Do not keep every card as a frontal shot. Use camera variety across stages: side and low/high combinations for baseline, twisted side or side-back for abdomen impacts, and rear/low/high angles for heavy damage and defeated art when it helps show shoulders, back, cloak, and armor destruction.
- Do not keep every card as a frontal shot. Use camera variety across stages: side and low/high combinations for baseline, twisted side or side-back for abdomen impacts, and rear/low/high angles for heavy damage and defeated art when it helps show shoulders, back, cloak, and armor destruction. Force at least one low-angle and one high-angle view in every 5-stage damage set, and avoid two consecutive front-facing reads by default.
- For each character, force low-angle or high-angle framing somewhere in every consecutive damage-set. Stages should not read as a mirrored front-facing sequence even when same movement language is used.
All damage-state art must use adult characters only. Intimate acts, penetration, childlike characters, and gore are out of scope; adult-only damaged-costume presentation, adult pinup posing, mature body language, and visible non-sensitive body areas are allowed and encouraged when framed as battle damage, defeat, or damaged-costume card art. Do not make the roster feel like a teen-safe or modest game; high-damage cards should read unmistakably as adult-only card art while remaining non-explicit.

View File

@@ -193,11 +193,6 @@ const CAMERA_ORIENTATION_PLAN = {
const CAMERA_VARIANTS = {
normal: {
front: [
"low side-three-quarter composition with a shoulder twist that keeps the body angled",
"three-quarter profile composition with one foot forward and hips leading the movement",
"slanted side-front composition where the face stays readable through a short turn",
],
side: [
"strong profile side shot with one step forward and hips leading the movement",
"45-degree profile turn with the leading shoulder pushed toward camera",
@@ -220,11 +215,6 @@ const CAMERA_VARIANTS = {
],
},
"light-damage": {
front: [
"front-canted three-quarter read with shoulders turned against impact and one side stepping in",
"low-front slant with torso dropping into counter step and side-shifted hips",
"eye-level angled guard frame that keeps the strike angle visible in the shoulder and waist",
],
side: [
"side pivot camera with shoulder turn and counter-step body shift",
"off-center side-frame with one foot forward and hips leading",
@@ -247,11 +237,6 @@ const CAMERA_VARIANTS = {
],
},
"medium-damage": {
front: [
"front-angled three-quarter combat lock with shoulder and hip crossing against a force",
"low-front recovery turn with one leg absorbing impact and waist twisted for balance",
"angled forward body-lock pose as the torso rotates into a side-facing response",
],
side: [
"strong side profile, body twisted from force so shoulder, torso, and abdomen damage are obvious",
"rearward three-quarter side-forward action frame while keeping enough face profile",
@@ -274,11 +259,6 @@ const CAMERA_VARIANTS = {
],
},
"heavy-damage": {
front: [
"low-front collapse angle where one shoulder drops while body rotates around a failing knee",
"aggressive forward-lean collapse with side weight shift and heavy gear drag",
"frontal-to-side rebound pose with fractured guard and broken upper-body support",
],
side: [
"dramatic side view from near ground, torn shoulder and torso damage clearly visible",
"wide extreme-side camera with body falling forward and equipment flying",
@@ -301,11 +281,6 @@ const CAMERA_VARIANTS = {
],
},
defeated: {
front: [
"front-canted defeated collapse with one knee down and torso dropping into recoil",
"kneeling recoil frame with hip-heavy fall and waist opening",
"collapsed chest-forward posture with profile drift and controlled face read",
],
side: [
"low angle side-back shot with fallen armor pieces and defeated body posture",
"rear three-quarter, side-back, or very low side view; show damaged back/shoulder/cloak with readable face profile",
@@ -331,7 +306,7 @@ const CAMERA_VARIANTS = {
const CAMERA_ORIENTATION_RULES = {
normal:
"Use only side/rear/low/high framing. Never use a pure front-on head-on lock. Across one character's 5 stages, make sure at least one low-angle and one high-angle shot appear.",
"Use only side/rear/low/high framing. Frontal-on faces are disallowed unless identity continuity absolutely requires it. Across one character's 5 stages, enforce at least one low-angle and one high-angle shot.",
"light-damage":
"Prioritize low-angle, rear, side, and high-angle framing. Frontal reads are disallowed unless strict identity continuity would otherwise fail.",
"medium-damage":
@@ -598,7 +573,7 @@ function nextDifferentIndex(plan, startIndex, bannedOrientation) {
for (let i = 1; i <= plan.length; i += 1) {
const index = (startIndex + i) % plan.length;
const candidate = normalizeOrientation(plan[index]);
if (candidate !== bannedOrientation && candidate !== "front") {
if (candidate !== bannedOrientation) {
return index;
}
}
@@ -606,11 +581,29 @@ function nextDifferentIndex(plan, startIndex, bannedOrientation) {
}
function normalizeOrientation(orientation) {
return orientation === "front" ? "side" : orientation;
return orientation;
}
const cameraHistoryByCharacter = new Map();
const poseHistoryByCharacter = new Map();
const poseFamilyHistoryByCharacter = new Map();
function poseFamily(phrase) {
const key = phrase.toLowerCase();
if (/(drop|kneel|crouch|crawl|collapse|sit|ground|fall)/.test(key)) {
return "ground-recovery";
}
if (/(jump|vault|leap|swing|spin|air|mid-air|rebound)/.test(key)) {
return "airborne";
}
if (/(counter|parry|brace|rebound|recover|drag|pull|guard)/.test(key)) {
return "defensive";
}
if (/(lunge|dash|step|stride|pivot|split|run|dodge|advance)/.test(key)) {
return "forward-motion";
}
return "impact";
}
function pickCameraForStage(spec, character, stage) {
const orientationPlan = CAMERA_ORIENTATION_PLAN[spec.label] ?? CAMERA_ORIENTATION_PLAN.normal;
@@ -686,23 +679,36 @@ function pickPoseModifier(spec, character, stage, orientation) {
POSE_ORIENTATION_VARIANTS.normal?.[normalizedOrientation] ??
[];
const posePool = orientationBonus.concat(list);
const pick = pickDiverseOption(
posePool,
charSeed(`${character.id}-${character.pose}-${character.weapon}-${orientation}`),
stage,
19,
);
const historyKey = character.id;
const history = poseHistoryByCharacter.get(historyKey) ?? [];
let selected = pick;
const familyHistory = poseFamilyHistoryByCharacter.get(character.id) ?? [];
const avoidedFamilies = new Set(familyHistory.slice(-2));
const familyFilteredPool = posePool.filter((pose) => !avoidedFamilies.has(poseFamily(pose)));
const finalPool = familyFilteredPool.length > 0 ? familyFilteredPool : posePool;
const poolSeed = charSeed(`${character.id}-${character.pose}-${character.weapon}-${normalizedOrientation}`);
const index = (poolSeed + stage * 19) % finalPool.length;
const selectedCandidate = finalPool[index];
const history = poseHistoryByCharacter.get(character.id) ?? [];
let selected = selectedCandidate;
if (history.length > 0 && selected === history[history.length - 1]) {
selected = posePool[(posePool.indexOf(pick) + 1) % posePool.length];
const fallbackIndex = (finalPool.indexOf(selected) + 1) % finalPool.length;
selected = finalPool[fallbackIndex];
}
const family = poseFamily(selected);
const familyHistoryKey = character.id;
const nextFamilyHistory = poseFamilyHistoryByCharacter.get(familyHistoryKey) ?? [];
nextFamilyHistory.push(family);
if (nextFamilyHistory.length > 8) {
nextFamilyHistory.shift();
}
poseFamilyHistoryByCharacter.set(familyHistoryKey, nextFamilyHistory);
history.push(selected);
if (history.length > 12) {
history.shift();
}
poseHistoryByCharacter.set(historyKey, history);
poseHistoryByCharacter.set(character.id, history);
return selected;
}
@@ -726,6 +732,8 @@ function promptFor(character, stage) {
"Angle strictness: this render must use side/rear/low/high framing first; direct frontal composition is disallowed unless the model absolutely cannot preserve face identity.";
const angleConstraint =
"Do not output front-facing framing for default action shots. Use side/rear/low/high angles as primary composition, and rotate the view family between shots so the same character does not repeat the exact same camera axis.";
const poseDiversityConstraint =
"Pose diversity is mandatory: rotate through guard, movement, recoil, evasive, grounded, and collapse actions across the five stages; do not repeat the same motion family in multiple consecutive shots.";
const poseModifier = pickPoseModifier(spec, character, stage, camera.orientation);
return [
"Use case: stylized-concept",
@@ -742,6 +750,7 @@ function promptFor(character, stage) {
`Camera/view variation: ${camera.orientation} angle. Composition directive: ${camera.phrase}.`,
angleControl,
angleConstraint,
poseDiversityConstraint,
"Across the character's five damage stages, rotate between side, rear, low-angle, and high-angle views as the primary framing, and only use frontal composition when the identity cannot be preserved.",
`Camera constraint: ${CAMERA_ORIENTATION_RULES[spec.label] ?? CAMERA_ORIENTATION_RULES.normal}`,
`Adult visual appeal: ${character.appealFocus}; ${adultAppeal}.`,