Strengthen camera and pose diversity constraints in card prompts
This commit is contained in:
@@ -350,44 +350,137 @@ const CAMERA_ORIENTATION_RULES = {
|
||||
"Prioritize side/rear/back/low/high framing for collapse and pain continuity; avoid frontal composition even when convenient.",
|
||||
};
|
||||
|
||||
const CAMERA_VIEW_ANGLE_HINTS = {
|
||||
side: [
|
||||
"left-profile three-quarter side view, shoulders pushed slightly toward the left lens edge",
|
||||
"right-profile combat side frame with hips opening into frame and face angled away from camera",
|
||||
"full 3/4 side lock, from left shoulder toward camera so the chest reads diagonally",
|
||||
"full 3/4 side lock, from right shoulder toward camera so the hips read off-axis",
|
||||
],
|
||||
rear: [
|
||||
"rear three-quarter back-side shot with hips and shoulder twist, no front-facing head-on look",
|
||||
"deep shoulder-behind framing, character turning away from lens into back-left arc",
|
||||
"backward quarter-angle, body rotating through rear line while weapon/arms remain readable",
|
||||
"side-rear composite frame from over-the-left shoulder looking toward character's right side",
|
||||
],
|
||||
low: [
|
||||
"ultra-low perspective from knee height with body pitched forward and one leg planted",
|
||||
"low camera sweep from left ground line, knees and lower torso clearly lower in frame",
|
||||
"low side crawl-height angle with hips lower than lens and forceful body tilt",
|
||||
"ground-lean low-angle setup from right side with damaged armor edge near bottom foreground",
|
||||
],
|
||||
high: [
|
||||
"high-angle top-down read with character's shoulders and abdomen visible under diagonal light",
|
||||
"overhead shot from right high point, body read in strong 45-degree tilt",
|
||||
"high vertical cut looking down into lowered posture and exposed torso line",
|
||||
"steep upper shot from above with lower body leading into frame to keep full silhouette",
|
||||
],
|
||||
};
|
||||
|
||||
const CAMERA_STAGE_REQUIREMENTS = {
|
||||
normal: {
|
||||
0: "side",
|
||||
1: "high",
|
||||
2: "low",
|
||||
3: "rear",
|
||||
4: "side",
|
||||
0: ["side", "low"],
|
||||
1: ["high", "side", "rear"],
|
||||
2: ["low", "high"],
|
||||
3: ["rear", "low", "high"],
|
||||
4: ["side", "high", "rear"],
|
||||
},
|
||||
"light-damage": {
|
||||
0: "low",
|
||||
1: "high",
|
||||
2: "side",
|
||||
3: "rear",
|
||||
4: "high",
|
||||
0: ["low", "high"],
|
||||
1: ["side", "rear"],
|
||||
2: ["low", "side", "high"],
|
||||
3: ["rear", "low"],
|
||||
4: ["high", "rear", "side"],
|
||||
},
|
||||
"medium-damage": {
|
||||
0: "side",
|
||||
1: "low",
|
||||
2: "high",
|
||||
3: "rear",
|
||||
4: "side",
|
||||
0: ["side", "rear", "low"],
|
||||
1: ["low", "high"],
|
||||
2: ["high", "side", "rear"],
|
||||
3: ["rear", "low", "high"],
|
||||
4: ["side", "high", "low"],
|
||||
},
|
||||
"heavy-damage": {
|
||||
0: "low",
|
||||
1: "high",
|
||||
2: "rear",
|
||||
3: "side",
|
||||
4: "high",
|
||||
0: ["low", "high"],
|
||||
1: ["high", "low", "rear"],
|
||||
2: ["rear", "low", "high"],
|
||||
3: ["rear", "low"],
|
||||
4: ["high", "rear", "side"],
|
||||
},
|
||||
defeated: {
|
||||
0: "rear",
|
||||
1: "low",
|
||||
2: "high",
|
||||
3: "side",
|
||||
4: "rear",
|
||||
0: ["rear", "low", "high"],
|
||||
1: ["low", "side"],
|
||||
2: ["high", "rear", "side"],
|
||||
3: ["side", "rear", "low"],
|
||||
4: ["side", "high", "low"],
|
||||
},
|
||||
};
|
||||
|
||||
const POSE_STAGE_LOCK_HINT = {
|
||||
normal: {
|
||||
0: "pose lock A: defensive or impact",
|
||||
1: "pose lock B: defensive + movement",
|
||||
2: "pose lock C: impact + defensive",
|
||||
3: "pose lock D: ground-recovery + impact",
|
||||
4: "pose lock E: airborne + impact",
|
||||
},
|
||||
"light-damage": {
|
||||
0: "pose lock A: defensive",
|
||||
1: "pose lock B: impact + defensive",
|
||||
2: "pose lock C: airborne + impact",
|
||||
3: "pose lock D: ground-recovery + forward motion",
|
||||
4: "pose lock E: defensive + forward motion",
|
||||
},
|
||||
"medium-damage": {
|
||||
0: "pose lock A: defensive + recovery",
|
||||
1: "pose lock B: impact + airborne",
|
||||
2: "pose lock C: impact + recovery",
|
||||
3: "pose lock D: ground-recovery",
|
||||
4: "pose lock E: airborne + impact",
|
||||
},
|
||||
"heavy-damage": {
|
||||
0: "pose lock A: defensive + recovery",
|
||||
1: "pose lock B: impact + recovery",
|
||||
2: "pose lock C: recovery + airborne",
|
||||
3: "pose lock D: defensive + forward",
|
||||
4: "pose lock E: airborne + defensive",
|
||||
},
|
||||
defeated: {
|
||||
0: "pose lock A: ground-recovery",
|
||||
1: "pose lock B: defensive",
|
||||
2: "pose lock C: recovery + impact",
|
||||
3: "pose lock D: forward + ground-recovery",
|
||||
4: "pose lock E: collapsed + defensive",
|
||||
},
|
||||
};
|
||||
|
||||
function orientationCandidates(spec, stage) {
|
||||
const required = CAMERA_STAGE_REQUIREMENTS[spec.label]?.[stage];
|
||||
return required
|
||||
? Array.isArray(required)
|
||||
? required
|
||||
: [required]
|
||||
: [];
|
||||
}
|
||||
|
||||
function poseCandidates(spec, stage) {
|
||||
const required = STAGE_POSE_FAMILY_REQUIREMENTS[spec.label]?.[stage];
|
||||
return required
|
||||
? Array.isArray(required)
|
||||
? required
|
||||
: [required]
|
||||
: [];
|
||||
}
|
||||
|
||||
function cameraAngleHintForStage(orientation, character, stage) {
|
||||
const hintPool = CAMERA_VIEW_ANGLE_HINTS[orientation] ?? CAMERA_VIEW_ANGLE_HINTS.side;
|
||||
return pickDiverseOption(
|
||||
hintPool,
|
||||
charSeed(`${character.id}-${orientation}-${character.weapon}`),
|
||||
stage,
|
||||
23,
|
||||
);
|
||||
}
|
||||
|
||||
function templateOrientation(spec, character, stage) {
|
||||
const templates = CAMERA_ORIENTATION_TEMPLATES[spec.label] ?? CAMERA_ORIENTATION_TEMPLATES.normal;
|
||||
const templateSeed = charSeed(`${character.id}-${spec.label}-template`);
|
||||
@@ -404,161 +497,221 @@ const STAGE_POSE_FAMILY_PREFERENCE = {
|
||||
};
|
||||
|
||||
const STAGE_POSE_FAMILY_REQUIREMENTS = {
|
||||
normal: ["defensive", "impact", "forward-motion", "ground-recovery", "airborne"],
|
||||
"light-damage": ["defensive", "impact", "airborne", "ground-recovery", "forward-motion"],
|
||||
"medium-damage": ["ground-recovery", "defensive", "impact", "airborne", "forward-motion"],
|
||||
"heavy-damage": ["ground-recovery", "defensive", "impact", "forward-motion", "airborne"],
|
||||
defeated: ["ground-recovery", "defensive", "impact", "forward-motion", "airborne"],
|
||||
normal: [
|
||||
["defensive", "impact"],
|
||||
["ground-recovery", "defensive"],
|
||||
["forward-motion", "impact", "ground-recovery"],
|
||||
["ground-recovery", "impact", "airborne"],
|
||||
["airborne", "defensive", "forward-motion"],
|
||||
],
|
||||
"light-damage": [
|
||||
["defensive", "ground-recovery"],
|
||||
["impact", "defensive"],
|
||||
["airborne", "impact"],
|
||||
["ground-recovery", "forward-motion", "airborne"],
|
||||
["defensive", "forward-motion", "impact"],
|
||||
],
|
||||
"medium-damage": [
|
||||
["ground-recovery", "defensive"],
|
||||
["defensive", "impact", "airborne"],
|
||||
["impact", "ground-recovery"],
|
||||
["airborne", "defensive", "ground-recovery"],
|
||||
["ground-recovery", "forward-motion", "airborne"],
|
||||
],
|
||||
"heavy-damage": [
|
||||
["ground-recovery", "defensive"],
|
||||
["defensive", "impact"],
|
||||
["impact", "ground-recovery", "airborne"],
|
||||
["ground-recovery", "defensive", "forward-motion"],
|
||||
["airborne", "ground-recovery", "defensive"],
|
||||
],
|
||||
defeated: [
|
||||
["ground-recovery", "defensive"],
|
||||
["defensive", "ground-recovery", "impact"],
|
||||
["ground-recovery", "impact", "airborne"],
|
||||
["defensive", "forward-motion", "ground-recovery"],
|
||||
["ground-recovery", "defensive", "airborne"],
|
||||
],
|
||||
};
|
||||
|
||||
const POSE_ORIENTATION_VARIANTS = {
|
||||
normal: {
|
||||
front: [
|
||||
"forward slash-dodge blend with hips pivoting and back foot digging for balance",
|
||||
"low split-step into guarded lunge while shoulders drift around the impact angle",
|
||||
"angled stride with upper body opening and hips torqued toward counter line",
|
||||
],
|
||||
side: [
|
||||
"side-reverse guard stance with hips and elbow dropping into a counter line",
|
||||
"three-quarter sidestep guard with shoulder cut and heel shift",
|
||||
"profile side-lean into a counter setup with hip twist",
|
||||
"sideward launch stance while back arm sweeps for balance",
|
||||
"left-to-right sidestep pivot with shoulder hook and reverse hip flick",
|
||||
"cross-shoulder side dodge then immediate counter reset",
|
||||
"wide side-arc spin and low sweep to avoid a finishing strike",
|
||||
],
|
||||
rear: [
|
||||
"rear-open dodge with shoulder twist and hand reposition",
|
||||
"half-turn recovery where the torso shifts into back-side guard",
|
||||
"body twisted into reverse profile as the support leg checks inward",
|
||||
"single-foot reverse step with shoulder cut and hand recoil",
|
||||
"backward quarter-roll into a side-back recoil with weapon retention",
|
||||
"rearward shoulder dump then off-axis recovery step",
|
||||
"hip-shifted back-step into counter-stance from behind the target arc",
|
||||
],
|
||||
low: [
|
||||
"low crouch into short strike setup with lead knee weighting",
|
||||
"deep squat guard with chest thrust and shoulder roll",
|
||||
"dropping squat recovery with torso forward and hips stabilized",
|
||||
"knee-planted drop-step into low profile defense",
|
||||
"combat drop into low crawl-and-rise with one arm dragging the ground",
|
||||
"knee-slide recovery while hands brace and weapon arm cuts backward",
|
||||
"back-arch low dodge that converts into short recoil lunge",
|
||||
],
|
||||
high: [
|
||||
"vertical pull-up arc with chest open and one knee lifted",
|
||||
"high-angle recovery stance while the weapon arcs above eye level",
|
||||
"raised-angle block with chin tucked and shoulder pivot",
|
||||
"high-lift defensive reset with twisted hips and backward shoulder pull",
|
||||
"high spring recovery with upper body lift and one-leg pivot",
|
||||
"upward rebound frame with shoulder open and torso tilting away",
|
||||
"air-assisted reset where knees lift and weapon tracks above the head",
|
||||
],
|
||||
},
|
||||
"light-damage": {
|
||||
front: [
|
||||
"quick forward guard split with one shoulder collapsing and re-centering",
|
||||
"controlled recoil then short dash that reclaims forward ground",
|
||||
"recovery stride where arms and hips overcorrect while turning away from impact",
|
||||
],
|
||||
side: [
|
||||
"side recoil plus hip turn into defensive counter line",
|
||||
"angled cross step with hand crossing in pain-block recovery",
|
||||
"defensive sidestep with torso twisted to absorb impact",
|
||||
"off-angle side recoil with knee bend and heel snap backward",
|
||||
"side-step spin recovery with split shoulder pressure",
|
||||
"angled sidethrow parry with heel whip and hip rollback",
|
||||
"fast side evade and cross-body kick to open escape angle",
|
||||
],
|
||||
rear: [
|
||||
"rear guard with shoulders turned while stepping backward",
|
||||
"half-turn backward kick and shoulder brace after impact",
|
||||
"backward diagonal lurch into off-center survival stance",
|
||||
"rear half-spin with torso pulling away while hands guard the core",
|
||||
"backward cross-step into side-back guard with split-arm reset",
|
||||
"corkscrew retreat into defensive arc before recoiling again",
|
||||
],
|
||||
low: [
|
||||
"single-knee drop into defensive crouch after recoil",
|
||||
"low-angle recoil where knee bends to absorb a glancing blow",
|
||||
"weight drops to lower center with foot drag recovery",
|
||||
"grounding low guard with bent knees and shoulder drop",
|
||||
"fall-back one-knee slide and low counter reset",
|
||||
"low crouch-spin with rear arm wrapping while legs re-center",
|
||||
"kneeling recovery with feet whipping forward into side brace",
|
||||
],
|
||||
high: [
|
||||
"leaning recovery shot with head lowered and shoulders lifting",
|
||||
"upper-body pull-up after impact while one leg pushes backward",
|
||||
"rear shoulder guard raised from above and angled backward",
|
||||
"high-arc recoil with hip pivot and weapon reset",
|
||||
"spring kick recovery with knee lift and long reach from above",
|
||||
"jump-recoil from low footing into angled high guard",
|
||||
"jump-stop reset with upper body angled under incoming strike",
|
||||
],
|
||||
},
|
||||
"medium-damage": {
|
||||
front: [
|
||||
"recoil burst then forced counter as hip and rib line rotate",
|
||||
"forward drive with one arm absorbing shock and the other checking distance while hips pivot",
|
||||
"angry step-in with chest thrust while lower leg braces through an angled stance",
|
||||
],
|
||||
side: [
|
||||
"twisted side-collapse into half-dodge and weapon re-latch",
|
||||
"violent side pivot with hips dropping for recovery",
|
||||
"broadside recoil with shoulders and waist taking force",
|
||||
"explosive side dodge into a broken counter-stance",
|
||||
"side-cross slash avoidance with heel drag and shoulder roll",
|
||||
"shoulder-first spin-drift into counter-guard",
|
||||
"wide sidestep lunge into broken posture and instant reset",
|
||||
],
|
||||
rear: [
|
||||
"rear-lean dodge, one shoulder clipped and weapon hand recovering",
|
||||
"backward rotation with torso and hips pulled off rhythm",
|
||||
"cross-body drag pose after turning into the blow",
|
||||
"rearward half spin where one knee bends into short recovery",
|
||||
"backward evasive spin with torso folding then extending forward",
|
||||
"reverse-arc step while arms protect broken flank",
|
||||
],
|
||||
low: [
|
||||
"deep low crouch with one knee buckling and one arm reaching upward",
|
||||
"forced fall-to-knee pose with shoulder and thigh locked in strain",
|
||||
"full-bent-leg recovery where hips torque to stop momentum",
|
||||
"low side-crawl recovery with one knee dragging to regain control",
|
||||
"rolling knee recovery into defensive low stance",
|
||||
"drop-and-drift recovery with one arm skimming floor for push-off",
|
||||
"low split step then knee-wheel turn into counter line",
|
||||
],
|
||||
high: [
|
||||
"high-arc dodge with upper body flaring open from pain",
|
||||
"upward chest-raise recovery where weapon follows steep line",
|
||||
"torso recovery angle showing damage across chest and abdomen",
|
||||
"high-side rebound where chest turns toward the damage vector",
|
||||
"ceiling-feel upper arc as knees rise and chest leads",
|
||||
"side-spin rebound with torso leaning high and hips spiraling",
|
||||
"jump reset over impact vector with one heel leading",
|
||||
],
|
||||
},
|
||||
"heavy-damage": {
|
||||
front: [
|
||||
"desperate front counter while dropping into fractured posture",
|
||||
"broken-forward guard, hands and feet split to catch falling weight",
|
||||
"aggressive rebound with shoulder collapsing inward and hips dragging back",
|
||||
],
|
||||
side: [
|
||||
"violent side-fall pivot with one leg giving and one hand bracing",
|
||||
"off-axis side collision posture, knees flexed and spine twisted",
|
||||
"angry side-block stance with torso collapsing then pushing up",
|
||||
"twisted side-lunge collapse with weapon trailing and heel scrape",
|
||||
"side-impact crash with shoulder throw into recoil spin",
|
||||
"stumbling side step with twisted hips and forward arm wrap",
|
||||
"angry side rebound where knees fold and body twists through",
|
||||
],
|
||||
rear: [
|
||||
"rear collapse step while torso twists to keep balance",
|
||||
"backwards stagger with arms crossing over torn shoulders",
|
||||
"fallen side-back push-off with boots dragging backward",
|
||||
"rear pivot collapse into one-leg recovery with limp shoulder",
|
||||
"back-side tumble into bent-knee recovery stance",
|
||||
"counter-rotation recoil while turning from back to side profile",
|
||||
],
|
||||
low: [
|
||||
"floor-level collapse with one hand clawing forward and one foot extended",
|
||||
"kneeling recoil pose where upper body drops first",
|
||||
"low recovery attempt with bent knees and hips dragged sideways",
|
||||
"knee-drop recovery with one leg sliding backward under pain",
|
||||
"floor-roll through broken stance while still holding weapon guard",
|
||||
"low desperate drag-kick and elbow recovery",
|
||||
"kneeling spin-drop recovery with shoulders buckling under force",
|
||||
],
|
||||
high: [
|
||||
"upward shake-out motion while keeping hips off-line from injury",
|
||||
"high-angle pain-shift where torso rises briefly then sinks",
|
||||
"upper-body lift against collapsed armor and ground pull",
|
||||
"high recoil recovery while shoulder and hip line are clearly broken open",
|
||||
"high jump-reset with unstable landing and knee fold",
|
||||
"high pivot strike recovery with weapon pulled behind shoulder",
|
||||
"overhead recovery rebound that drops into broken side guard",
|
||||
],
|
||||
},
|
||||
defeated: {
|
||||
front: [
|
||||
"collapsed side-heavy breath pose with one knee low and body folded into recoil",
|
||||
"kneeling sideward collapse while torso twists and shoulders drop",
|
||||
"final effort reach-forward pose with one hand searching for footing while upper body falls",
|
||||
"side-forward defeated hold while the lower body cannot stabilize",
|
||||
],
|
||||
side: [
|
||||
"sideways collapse with legs apart and shoulders dropping across chest line",
|
||||
"swayed side collapse showing hips opening for impact visibility",
|
||||
"falling side-lean against broken footing with one arm stretched",
|
||||
"side collapse into one-breath break pose with spine folding",
|
||||
"collapsed side-lean with one knee folding and torso curling",
|
||||
],
|
||||
rear: [
|
||||
"rear slump with shoulder collapse and waist open to the shot",
|
||||
"turning-over collapse while body folds toward the damage side",
|
||||
"back-lean seated defeat where torso twists and legs separate",
|
||||
"rear kneel collapse with shoulder sinking and hips spinning wide",
|
||||
"defeat-side crawl into seated fold over broken armour",
|
||||
],
|
||||
low: [
|
||||
"ground-level collapse where back leg is dragged forward and torso sinks",
|
||||
"deep knee-drop surrender posture with shoulders gone inward",
|
||||
"knees-first give-up frame with one hand on fractured hip area",
|
||||
"kneeling collapse with staggered arms and half-turn to the side",
|
||||
"full fold to both knees with heel dragging through debris",
|
||||
],
|
||||
high: [
|
||||
"high-angle pinned defeat, body folded and centerline visible",
|
||||
"raised camera angle collapse showing hips and abdomen exposed",
|
||||
"full-body give-way with shoulders dropping and torso leaning down",
|
||||
"high collapse pivot where spine twists before sheeting downward",
|
||||
"raised-side-high fall with torso dropping into surrender angle",
|
||||
],
|
||||
},
|
||||
};
|
||||
@@ -573,6 +726,9 @@ const POSE_VARIANTS = {
|
||||
"guarding pose while shifting weight through the back foot",
|
||||
"dynamic sidestep lean with weapon hand slicing outward",
|
||||
"single-step dodge into counter position with raised elbow and twisted waist",
|
||||
"quarter turn into a backhand guard, hips driving sideways",
|
||||
"quick spin reset while keeping one knee bent for recoil control",
|
||||
"side vault dodge from neutral guard into counter position",
|
||||
],
|
||||
light: [
|
||||
"reaction pose after first impact, one shoulder dropping and feet rebalancing",
|
||||
@@ -583,6 +739,9 @@ const POSE_VARIANTS = {
|
||||
"one-hand brace while weapon hand reaches for a quick reset",
|
||||
"half-step backward check with shoulder dip and waist coil",
|
||||
"lateral recoil pose, foot split and hand crossing low guard",
|
||||
"short-range pivot evade then sudden return to offensive stance",
|
||||
"heel pivot with side-back recovery and weapon lock reset",
|
||||
"staggered jump-and-land recoil with knees and hips absorbing impact",
|
||||
],
|
||||
medium: [
|
||||
"strained mid-combat pose with bent knee, waist coil, and heavy shoulder impact",
|
||||
@@ -593,6 +752,9 @@ const POSE_VARIANTS = {
|
||||
"mid-air dodge frame followed by sharp landing, damaged sleeve catching air",
|
||||
"elbow-heavy crash block with one shoulder dropped and one arm sweeping back",
|
||||
"reverse pivot dodge with back heel drag and hip torque",
|
||||
"acrobatic roll through impact, rising to unstable counter-stance",
|
||||
"overhead lunge and drop-knee recovery into backward guard",
|
||||
"double pivot and kick with one knee planted for recoil",
|
||||
],
|
||||
heavy: [
|
||||
"collapsed combat pose, torso over one leg and shoulders catching recoil",
|
||||
@@ -603,6 +765,9 @@ const POSE_VARIANTS = {
|
||||
"falling stagger pose, back arched while hands scramble to hold broken gear",
|
||||
"violent side-fall recovery with broken arm brace and one leg dragging backward",
|
||||
"desperation block posture with knees bent, torso bent inward, and wide arm swing",
|
||||
"half-spin kick then one-knee recovery with broken grip",
|
||||
"collapsed jump-stop counter-guard with unstable hip drive",
|
||||
"rolling shoulder impact recovery followed by low collapse",
|
||||
],
|
||||
defeated: [
|
||||
"kneeling collapse with one hand low and one hand gripping broken gear",
|
||||
@@ -613,6 +778,9 @@ const POSE_VARIANTS = {
|
||||
"sideways seated defeat pose, legs apart, torso twisted toward the camera",
|
||||
"swaying seated collapse on bent knees, one hand clutching torn belt",
|
||||
"full knee-drop giving-up frame with limp forearm and heavy torso slump",
|
||||
"kneeling seated surrender with shoulder folding and head bowed",
|
||||
"low seated collapse where legs separate and one arm reaches for footing",
|
||||
"fallen seated recoil with torso twisted and knees caving inward",
|
||||
],
|
||||
};
|
||||
|
||||
@@ -683,7 +851,7 @@ function poseFamily(phrase) {
|
||||
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)) {
|
||||
if (/(jump|vault|leap|swing|spin|air|mid-air|rebound|flip|somersault|dive|tumble|roll)/.test(key)) {
|
||||
return "airborne";
|
||||
}
|
||||
if (/(counter|parry|brace|rebound|recover|drag|pull|guard)/.test(key)) {
|
||||
@@ -697,7 +865,7 @@ function poseFamily(phrase) {
|
||||
|
||||
function pickCameraForStage(spec, character, stage) {
|
||||
const orientationPlan = CAMERA_ORIENTATION_PLAN[spec.label] ?? CAMERA_ORIENTATION_PLAN.normal;
|
||||
const requiredOrientation = CAMERA_STAGE_REQUIREMENTS[spec.label]?.[stage];
|
||||
const requiredOrientationSpec = CAMERA_STAGE_REQUIREMENTS[spec.label]?.[stage];
|
||||
const requiredTemplateOrientation = templateOrientation(spec, character, stage);
|
||||
const historyKey = character.id;
|
||||
const history = cameraHistoryByCharacter.get(historyKey) ?? [];
|
||||
@@ -710,14 +878,37 @@ function pickCameraForStage(spec, character, stage) {
|
||||
resolvedIndex = stage % orientationPlan.length;
|
||||
}
|
||||
|
||||
const requiredIndex = requiredOrientation
|
||||
? firstIndexForOrientation(orientationPlan, requiredOrientation)
|
||||
const requiredPool = Array.isArray(requiredOrientationSpec)
|
||||
? requiredOrientationSpec
|
||||
: requiredOrientationSpec
|
||||
? [requiredOrientationSpec]
|
||||
: null;
|
||||
const requiredOrientation = requiredPool
|
||||
? requiredPool[charSeed(`${character.id}-${spec.label}-${stage}`) % requiredPool.length]
|
||||
: null;
|
||||
const normalizedRequiredOrientation = normalizeOrientation(requiredOrientation);
|
||||
const requiredIndex = normalizedRequiredOrientation
|
||||
? firstIndexForOrientation(orientationPlan, normalizedRequiredOrientation)
|
||||
: -1;
|
||||
if (requiredIndex !== -1) {
|
||||
resolvedIndex = requiredIndex;
|
||||
}
|
||||
|
||||
if (history.length > 0 && orientationPlan[resolvedIndex] === history[history.length - 1]) {
|
||||
if (
|
||||
requiredPool &&
|
||||
requiredPool.length === 1 &&
|
||||
history.length > 0 &&
|
||||
orientationPlan[requiredIndex] === history[history.length - 1]
|
||||
) {
|
||||
resolvedIndex = nextDifferentIndex(
|
||||
orientationPlan,
|
||||
requiredIndex,
|
||||
history[history.length - 1],
|
||||
);
|
||||
} else {
|
||||
resolvedIndex = requiredIndex;
|
||||
}
|
||||
} else if (
|
||||
history.length > 0 &&
|
||||
orientationPlan[resolvedIndex] === history[history.length - 1]
|
||||
) {
|
||||
resolvedIndex = nextDifferentIndex(
|
||||
orientationPlan,
|
||||
resolvedIndex,
|
||||
@@ -770,11 +961,24 @@ function pickPoseModifier(spec, character, stage, orientation) {
|
||||
STAGE_POSE_FAMILY_PREFERENCE.normal;
|
||||
const familyHistory = poseFamilyHistoryByCharacter.get(character.id) ?? [];
|
||||
const avoidedFamilies = new Set(familyHistory.slice(-3));
|
||||
const requiredFamily = STAGE_POSE_FAMILY_REQUIREMENTS[spec.label]?.[stage] ??
|
||||
const requiredFamilySpec = STAGE_POSE_FAMILY_REQUIREMENTS[spec.label]?.[stage] ??
|
||||
STAGE_POSE_FAMILY_REQUIREMENTS.normal[stage] ??
|
||||
null;
|
||||
const requiredFamilyPool = requiredFamily
|
||||
? posePool.filter((pose) => poseFamily(pose) === requiredFamily && !avoidedFamilies.has(requiredFamily))
|
||||
const requiredFamilyPool = Array.isArray(requiredFamilySpec)
|
||||
? requiredFamilySpec
|
||||
: requiredFamilySpec
|
||||
? [requiredFamilySpec]
|
||||
: [];
|
||||
const requiredFamily = requiredFamilyPool.length > 0
|
||||
? requiredFamilyPool[
|
||||
charSeed(`${character.id}-${character.weapon}-${spec.label}-${stage}`) % requiredFamilyPool.length
|
||||
]
|
||||
: null;
|
||||
const filteredRequiredFamily = requiredFamily && avoidedFamilies.has(requiredFamily)
|
||||
? (requiredFamilyPool.find((candidate) => !avoidedFamilies.has(candidate)) || null)
|
||||
: requiredFamily;
|
||||
const matchingRequiredFamilyPool = filteredRequiredFamily
|
||||
? posePool.filter((pose) => poseFamily(pose) === filteredRequiredFamily && !avoidedFamilies.has(filteredRequiredFamily))
|
||||
: [];
|
||||
const preferredPool = posePool.filter(
|
||||
(pose) =>
|
||||
@@ -783,8 +987,8 @@ function pickPoseModifier(spec, character, stage, orientation) {
|
||||
);
|
||||
const familyFilteredPool = posePool.filter((pose) => !avoidedFamilies.has(poseFamily(pose)));
|
||||
const finalPool =
|
||||
requiredFamilyPool.length > 0
|
||||
? requiredFamilyPool
|
||||
matchingRequiredFamilyPool.length > 0
|
||||
? matchingRequiredFamilyPool
|
||||
: preferredPool.length > 0
|
||||
? preferredPool
|
||||
: familyFilteredPool.length > 0
|
||||
@@ -841,6 +1045,19 @@ function promptFor(character, stage) {
|
||||
const broadDamageGate =
|
||||
"broad-damage gate: if the damaged outfit mostly reads as many fine cracks, tiny scratches, lace frays, thorny threads, or vine-like tendrils, the card fails even when the character is exposed. Each major damaged zone should include at least one large missing panel, wide split seam, snapped fastener, sheared plate, or heavy hanging remnant that can be understood at card size.";
|
||||
const camera = pickCameraForStage(spec, character, stage);
|
||||
const requiredCamera = camera.orientation;
|
||||
const poseOptions = poseCandidates(spec, stage);
|
||||
const cameraOptions = orientationCandidates(spec, stage);
|
||||
const shotLock =
|
||||
cameraOptions.length > 1
|
||||
? `shot ${stage + 1}: one of [${cameraOptions.join(", ")}] camera families`
|
||||
: `shot ${stage + 1}: ${requiredCamera}-axis`;
|
||||
const requiredCameraLock = `Hard camera lock: ${shotLock}; use side/rear/low/high only and never frontal or head-on.`;
|
||||
const cameraNoFront = [
|
||||
"Do not output full-frontal head-on, frontally symmetrical, or direct eye-level framing.",
|
||||
"Do not include profile-to-viewer camera phrases like 'facing the camera' or 'staring straight-on'.",
|
||||
"If the image looks symmetric, push the composition farther to side, rear, low, or high first.",
|
||||
].join(" ");
|
||||
const angleControl =
|
||||
"Angle hard lock: render only side/rear/low/high compositions. Do not allow a full frontal/head-on framing under any circumstance.";
|
||||
const angleConstraint =
|
||||
@@ -852,6 +1069,20 @@ function promptFor(character, stage) {
|
||||
const poseHardStop =
|
||||
"Hard stop: do not keep the exact same pose family or core action type in repeated stages; distribute at least three different families over five stages, with low-angle/high-angle poses represented in light or higher damage stages.";
|
||||
const poseModifier = pickPoseModifier(spec, character, stage, camera.orientation);
|
||||
const poseAngleHint = cameraAngleHintForStage(camera.orientation, character, stage);
|
||||
const selectedCameraCandidate = cameraOptions.length > 0
|
||||
? cameraOptions[charSeed(`${character.id}-${spec.label}-${stage}`) % cameraOptions.length]
|
||||
: camera.orientation;
|
||||
const selectedPoseFamily = poseOptions.length > 0
|
||||
? poseOptions[charSeed(`${character.id}-${character.weapon}-${spec.label}-${stage}`) % poseOptions.length]
|
||||
: null;
|
||||
const poseFamilyHint = selectedPoseFamily
|
||||
? `Stage ${stage + 1} pose-family priority: ${selectedPoseFamily}.`
|
||||
: "Stage pose-family priority: any non-frontal combat action family that keeps pose diversity.";
|
||||
const orientationPriority = cameraOptions.length > 1
|
||||
? `Stage ${stage + 1} allowed camera families: ${cameraOptions.join(", ")}.`
|
||||
: `Stage ${stage + 1} camera family lock: ${selectedCameraCandidate}.`;
|
||||
const poseLockHint = POSE_STAGE_LOCK_HINT[spec.label]?.[stage] ?? `pose lock ${stage + 1}`;
|
||||
return [
|
||||
"Use case: stylized-concept",
|
||||
"Asset type: high-resolution individual trading card illustration",
|
||||
@@ -864,7 +1095,11 @@ function promptFor(character, stage) {
|
||||
`Identity lock across stages: preserve the exact same person in every damage stage--same race/species, ear shape, face structure, eye shape, nose, lips, skin tone, hair color, hair length, hairstyle, body proportions, bust shape, and mature presence. Damage may change clothing, armor, pose, expression, dirt, sweat, and injury reaction, but must never change a human into an elf, change ear shape, change hair color, change age, change ethnicity, or replace the character with a different-looking person.`,
|
||||
`Personality and pose: ${character.personality}; ${character.pose}; ${spec.poseNote}; ${poseModifier}.`,
|
||||
`Facial expression: ${spec.expression}.`,
|
||||
`Camera/view variation: ${camera.orientation} angle. Composition directive: ${camera.phrase}.`,
|
||||
`Camera/view variation: ${camera.orientation} angle. ${poseAngleHint}. Composition directive: ${camera.phrase}.`,
|
||||
requiredCameraLock,
|
||||
orientationPriority,
|
||||
`Shot constraint: ${poseLockHint}; ${poseFamilyHint}`,
|
||||
cameraNoFront,
|
||||
angleControl,
|
||||
angleConstraint,
|
||||
angleHardStop,
|
||||
|
||||
Reference in New Issue
Block a user