342 lines
29 KiB
JavaScript
342 lines
29 KiB
JavaScript
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import vm from "node:vm";
|
|
|
|
const root = process.cwd();
|
|
const mainPath = path.join(root, "main.js");
|
|
const cardsRoot = path.join(root, "assets", "cards");
|
|
const promptsDir = path.join(cardsRoot, "prompts");
|
|
|
|
function makeElement() {
|
|
return {
|
|
className: "",
|
|
dataset: {},
|
|
style: { setProperty() {} },
|
|
children: [],
|
|
textContent: "",
|
|
innerHTML: "",
|
|
addEventListener() {},
|
|
replaceChildren(...nodes) {
|
|
this.children = nodes;
|
|
},
|
|
prepend(node) {
|
|
this.children.unshift(node);
|
|
},
|
|
remove() {},
|
|
};
|
|
}
|
|
|
|
function buildDomStub() {
|
|
const nodes = new Map();
|
|
return {
|
|
querySelector(selector) {
|
|
if (!nodes.has(selector)) nodes.set(selector, makeElement());
|
|
if (selector === "#emptyCardTemplate") {
|
|
nodes.get(selector).content = { cloneNode: () => makeElement() };
|
|
}
|
|
return nodes.get(selector);
|
|
},
|
|
createElement: makeElement,
|
|
};
|
|
}
|
|
|
|
function damageSpec(stage) {
|
|
const specs = [
|
|
{
|
|
label: "normal",
|
|
visual: "intact armor and clothing, pristine battle-ready silhouette",
|
|
impactIntensity:
|
|
"no impact damage yet; the card should look fully prepared before combat",
|
|
exposure:
|
|
"no battle-damage exposure yet; mature silhouette is visible through intact adult outfit design",
|
|
poseNote: "confident ready stance",
|
|
expression:
|
|
"controlled confident expression, eyes focused, no distress",
|
|
missingArmor:
|
|
"no missing armor or clothing; all equipment is intact",
|
|
destructionFloor:
|
|
"0% armor/equipment destruction: the baseline card must be fully intact before combat",
|
|
damageCoverage:
|
|
"all outfit and armor zones are intact and clean; no damage distribution is needed at this stage",
|
|
protectedZoneDamage:
|
|
"no protected-zone damage yet; chest armor or bodice, hip armor, rear skirt or coat panels, and seat armor are intact before combat",
|
|
baseLayerDamage:
|
|
"no base-layer damage yet; support underlayers, bodysuit panels, and lower coverage panels are intact before combat",
|
|
camera:
|
|
"front or three-quarter front view for clear character identity",
|
|
},
|
|
{
|
|
label: "light-damage",
|
|
visual:
|
|
"light battle damage from a clear opening hit: scratched and lightly dented armor or modern gear, torn jacket or cloak edges, scuffed gloves, one shoulder strap loosened, cracked small accessory or visor edge, small visible exposure around collarbone, shoulder edge, forearm, waistline, or upper thigh",
|
|
impactIntensity:
|
|
"light damage must still look like a real strike landed: include visible scuffs, dents, loosened straps, and at least one broken small accessory, while keeping the character mostly combat-ready",
|
|
materialPacing:
|
|
"material-aware opening damage: cloth, ribbons, capes, thin bodysuit edges, and leather straps may tear or fray early; metal armor, hard sci-fi plates, helmets, and mechanical gear should mostly dent, scratch, crack at fasteners, or bend slightly instead of vanishing all at once",
|
|
exposure:
|
|
"low but noticeable battle-damage exposure: collarbone, shoulder edge, forearm, waistline, or upper thigh should be visible from damaged outer layers",
|
|
poseNote: "still composed but reacting to an opening exchange",
|
|
expression:
|
|
"annoyed grimace, narrowed eyes, clenched jaw, clearly frustrated by the first hit",
|
|
missingArmor:
|
|
"minor loss of outer accessories only: torn glove edge, loosened shoulder strap, scuffed boot armor",
|
|
destructionFloor:
|
|
"opening-hit destruction only: keep the character mostly combat-ready, but make the first visible armor/equipment failures clear enough to establish later cumulative damage",
|
|
damageCoverage:
|
|
"light damage should be distributed across at least three visible zones, such as a shoulder strap, sleeve or glove, waist edge, thigh guard, boot guard, visor, or small accessory, so no single isolated tear carries the whole stage",
|
|
protectedZoneDamage:
|
|
"light damage may include small scuffs, dents, or loosened fasteners on chest armor or bodice edges, hip armor, rear skirt or coat panels, or seat armor, while preserving opaque coverage and keeping the adult pinup combat context clear",
|
|
baseLayerDamage:
|
|
"light damage may scuff or loosen visible support underlayers, bodysuit edges, or lower coverage seams, but they mostly remain intact at this stage",
|
|
camera:
|
|
"three-quarter front view or slight side angle, enough to show both face and first armor damage",
|
|
},
|
|
{
|
|
label: "medium-damage",
|
|
visual:
|
|
"moderate battle damage from repeated heavy hits: cracked shoulder armor or protective gear, bent plates, torn outer sleeves, loosened or snapped belts, damaged protective plates, exposed forearms, bare shoulder, torn neckline or side seam, clearly exposed abdomen and side torso, exposed upper thigh and hip line through broken outer layers, adult pinup silhouette already unmistakable, and no clean replacement underlayer hiding the damage",
|
|
impactIntensity:
|
|
"medium damage must read stronger than cosmetic tearing: show bent metal, snapped straps, cracked guards, separated seams, bruising-free impact marks on gear, and a posture that clearly reacts to forceful combat impact",
|
|
materialPacing:
|
|
"material-aware medium damage: compared with the damage-0 baseline, cloth layers, capes, skirts, ribbons, straps, and soft leather should show broad cut-open panels, snapped seams, and missing sections; metal or rigid armor should survive longer but must show real failures such as bent plates, cracked joins, broken rivets, detached sections, crushed edges, and disabled mechanisms",
|
|
exposure:
|
|
"clear adult damaged-costume presentation: large opened panels should reveal non-sensitive areas such as shoulder, side torso, abdomen, upper thigh, hip line, back angle, and mature upper-body silhouette together, not hidden by intact modest underlayers or new replacement coverage panels",
|
|
poseNote: "strained but still fighting with focus",
|
|
expression:
|
|
"painful frown, wet eyes, anger and disbelief, trying not to cry while staying in the fight; if hit in the abdomen, show a gasping open mouth and strained breathing",
|
|
missingArmor:
|
|
"visible loss around hands, forearms, one shoulder, boot guards, and side-abdomen armor; damage suggests repeated heavy impacts",
|
|
destructionFloor:
|
|
"minimum 50% visible armor/equipment destruction relative to damage-0: at least half of the original outfit, armor, straps, plates, weapons, and equipment must no longer look intact. Dirt, soot, scratches, thin crack lines, background debris, pose changes, and floating fragments do not count toward this 50% unless the original worn gear itself is visibly torn away, bent, detached, disabled, or hanging loose across the body",
|
|
damageCoverage:
|
|
"medium damage should visibly affect upper body, arms, abdomen or waist, hips or thighs, and at least one boot or leg guard; do not leave a major clothing or armor region looking untouched",
|
|
protectedZoneDamage:
|
|
"medium damage must not avoid torso or rear-body gear: chest armor or bodice panels, side armor edges, hip plates, rear waist plates, seat armor, back skirt panels, or coat tails should show cracks, torn seams, dents, missing outer trim, or broken straps, while opaque remnants preserve required coverage",
|
|
baseLayerDamage:
|
|
"medium damage must not leave support layers, suit liners, lower wraps, or other coverage underlayers looking pristine; show scuffed fabric, torn edges, stretched seams, snapped trim, soot, dents in reinforced liners, and uneven deformation while opaque coverage remains. If the image reads like clean replacement coverage or teen-safe re-covering after the outer gear breaks, it fails",
|
|
camera:
|
|
"side view or strong three-quarter side view, body twisted from impact so shoulder and abdomen damage are visible",
|
|
},
|
|
{
|
|
label: "heavy-damage",
|
|
visual:
|
|
"heavy battle damage after a brutal exchange: broken and warped outer armor or modern gear, large cut-open mantle/jacket/stage outfit panels, fractured bracers, missing glove armor, crushed or split waist plates, damaged boots or unarmored ankle on one side, adult-only non-explicit damaged-costume pinup presentation with opened shoulders, back, abdomen/midriff, hips, upper thighs, mature torso silhouette, and only battered opaque costume remnants through destroyed outer armor",
|
|
impactIntensity:
|
|
"heavy damage should look severe and unmistakable: armor plates bent out of shape, straps snapped, seams split open, bracers fractured, boots damaged, weapon accessories partly broken, and the character's balance visibly failing from repeated impacts",
|
|
materialPacing:
|
|
"material-aware heavy damage: soft materials should be opened by broad weapon cuts or missing as large panels by now; leather should split in long directional cuts, curl, and lose straps; metal armor should not simply disappear but should show multiple large plates cracked, warped, unlatched, hanging by broken fasteners, or fallen as readable chunks near the character",
|
|
exposure:
|
|
"push to adult-only non-explicit damaged-costume presentation: opened shoulders, back, torso silhouette, abdomen, hips, upper thighs, and a few large battered outfit remnants should make the impact feel severe and adult-only, with no clean replacement coverage layer, censorship effect, hair curtain, or pristine fabric used to hide the damage",
|
|
poseNote: "wounded, guarded, barely keeping balance after a brutal exchange",
|
|
expression:
|
|
"tearful angry grimace, trembling lower lip, brows pinched, resentful eyes that show she hates losing; abdominal-hit reaction such as open mouth, gasping, hunched posture, and a small combat-pain saliva strand is allowed",
|
|
missingArmor:
|
|
"major loss around hands, feet, shoulders, abdomen, waist plates, and bracers; broken pieces scattered nearby",
|
|
destructionFloor:
|
|
"minimum 70% visible armor/equipment destruction relative to damage-0: most of the original protective outfit and carried equipment must be broken, missing, warped, unlatched, disabled, or hanging loose. Large intact suit areas, pristine torso/hip panels, clean boots, or mostly complete armor silhouettes fail this stage even if there are many scratches, dirt marks, or background fragments",
|
|
damageCoverage:
|
|
"heavy damage must be full-body and balanced: headgear or visor, shoulders, chest armor or bodice, back or cloak, sleeves, gloves, abdomen, waist, hips, thighs, knees, boots, and weapon accessories should all show cracks, tears, missing outer plates, scuffs, or deformation; avoid leaving any major region pristine while another region is over-destroyed",
|
|
protectedZoneDamage:
|
|
"heavy damage must visibly affect torso and rear-body equipment too: dented or split chest armor, torn bodice seams, damaged torso straps, cracked side armor edges, broken hip plates, torn rear skirt or coat panels, crushed seat armor, and snapped rear waist fasteners should be present as combat damage with adult pinup appeal; never leave torso or rear/hip regions unusually pristine compared with the rest of the body",
|
|
baseLayerDamage:
|
|
"heavy damage must visibly affect support underlayers and coverage panels too: support pieces, suit liners, lower wraps, and coverage panels should be torn, scuffed, warped, soot-marked, strap-broken, or partially covered by broken armor remnants. In this stage they should read as battered opaque remnants, not clean replacement coverage; they must still preserve non-explicit opaque coverage",
|
|
camera:
|
|
"dramatic side view, rear three-quarter view, or over-the-shoulder angle while keeping face partially visible",
|
|
},
|
|
{
|
|
label: "defeated",
|
|
visual:
|
|
"defeated state after overwhelming combat: kneeling, collapsed, or sitting back after battle, armor system barely holding together, broken armor pieces around them, torn outer garment layers, missing hand and foot armor, shattered shoulder or waist plates, maximum non-explicit adult damaged-costume battle pinup presentation with opened shoulders, back or side-back, abdomen/midriff, hips, upper thighs, mature torso silhouette, and only battered opaque remnants through destroyed outer armor",
|
|
impactIntensity:
|
|
"defeated damage should be the strongest non-gore stage: the outfit and armor should look comprehensively wrecked by combat, with collapsed straps, broken fasteners, crushed guards, missing outer plates, disabled weapon pieces, and a posture that clearly communicates she can no longer keep fighting",
|
|
materialPacing:
|
|
"material-aware defeated damage: cloth, capes, skirts, ribbons, and soft suit layers should remain only as battered opaque remnants; leather should be split and strap-broken; metal and mechanical armor should be comprehensively ruined through crushed sections, torn hinges, missing plates, disabled joints, broken weapons, and scattered large pieces rather than cosmetic scuffs",
|
|
exposure:
|
|
"maximum non-explicit adult damaged-costume battle presentation for the defeated card: broad opened shoulder/back/side torso/abdomen/hip/thigh panels at once, a few dramatic large battered costume remnants, adult defeated pinup body language, and emphasized mature silhouette while staying framed as combat defeat rather than an intimate act; do not add clean replacement coverage layers, censorship effects, hair curtains, or pristine replacement panels",
|
|
poseNote: "battle-worn, emotionally overwhelmed, clearly defeated, no gore",
|
|
expression:
|
|
"frustrated tears, unfair-loss expression, clenched teeth or trembling open mouth, tear tracks, angry shame rather than calm dignity; defeated abdominal-hit pain reaction with open mouth, drool/saliva at the lip, and labored breathing is allowed when framed as combat pain",
|
|
missingArmor:
|
|
"the most intense equipment loss: shattered shoulder plates, missing bracers and glove armor, broken boot guards, torn waist and abdomen armor, scattered fragments",
|
|
destructionFloor:
|
|
"minimum 85% visible armor/equipment destruction relative to damage-0: the original armor, outfit, straps, weapons, and equipment system should be comprehensively wrecked, with only battered opaque remnants and broken supports still covering intimate areas. If more than a small minority of the original suit or armor reads as clean, continuous, or functional, the defeated card fails",
|
|
damageCoverage:
|
|
"defeated damage must read across the entire outfit and armor system: damaged headgear, shoulders, chest armor or bodice, back or cloak, sleeves, gloves, abdomen, waist, hips, thighs, knees, boots, and weapon accessories; every major zone should show battle damage while opaque remnants still cover intimate areas",
|
|
protectedZoneDamage:
|
|
"defeated damage must include the same destruction level on torso and rear-body gear as everywhere else: warped chest armor or torn bodice panels, broken support straps, damaged torso-edge armor without explicit closeup focus, fractured hip plates, torn rear skirt or coat panels, crushed seat armor, and broken rear waist fasteners; these areas must not look selectively protected or repaired, while opaque remnants preserve required coverage",
|
|
baseLayerDamage:
|
|
"defeated damage must make support underlayers and coverage panels look battle-wrecked too: support pieces, suit liners, lower wraps, and coverage panels should be torn at edges, dirtied, deformed, strap-broken, soot-marked, and partly overlapped by shattered armor fragments. Keep only battered opaque remnants where coverage is required; never leave pristine replacement coverage",
|
|
camera:
|
|
"rear three-quarter, side-back, or low side view; show back/shoulder/cloak damage while keeping enough face profile to read pain",
|
|
},
|
|
];
|
|
return specs[stage];
|
|
}
|
|
|
|
function promptFor(character, stage) {
|
|
const spec = damageSpec(stage);
|
|
const elementName = {
|
|
water: "water/수",
|
|
metal: "metal/금",
|
|
earth: "earth/지",
|
|
fire: "fire/화",
|
|
wood: "wood/목",
|
|
}[character.affinity] ?? character.affinity;
|
|
const adultAppeal =
|
|
"adult female collectible-card appeal: emphasize a mature curvy silhouette through the armor, uniform, stage outfit, or gear silhouette; mature body proportions; elegant adult costume design; adult pinup body language is encouraged when framed as battle damage or defeat; high-damage cards should read adult-only and not teen-safe";
|
|
const weaponImpactReadability =
|
|
"weapon-impact damage readability: destruction should look caused by readable combat impacts, not vines, webs, thread nets, or lace-like shredding. Prefer a few large directional cuts, diagonal slash paths, sheared armor plates, split seams following the weapon angle, missing costume panels, broken buckles, crushed plate zones, and big hanging cloth sections. Avoid dozens of tiny parallel rips, uniformly shredded edges across the whole outfit, wire tangles, vine-shaped cuts, spiderweb cracks, glittering filaments, and random micro-tears. A blade hit should create one or two coherent slash directions across nearby cloth, straps, and armor edges; a blunt hit should bend or crush plates around one impact zone; an energy hit should burn away broad panels.";
|
|
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.";
|
|
return [
|
|
"Use case: stylized-concept",
|
|
"Asset type: high-resolution individual trading card illustration",
|
|
`Primary request: Create a unique full-card illustration for ${character.id} ${character.name}, damage stage ${stage} (${spec.label}).`,
|
|
`Subject: adult ${character.gender} ${character.race}, ${character.body}, ${character.title}, ${character.role}, weapon/equipment ${character.weapon}.`,
|
|
`World/era concept: ${character.world}; ${character.era}. This roster is not limited to medieval fantasy; characters can come from modern campuses, idol stages, cyberpunk streets, space colonies, sports arenas, mythic fashion worlds, or other time periods.`,
|
|
`Lighting/time/weather: ${character.lighting}. Do not default every battle to night; use daytime, afternoon, morning, cloudy, indoor, or night lighting only when this field calls for it.`,
|
|
`Signature costume/equipment: ${character.costume}.`,
|
|
`Unique appearance DNA: ${character.appearance.faceShape}; ${character.appearance.eyeStyle}; ${character.appearance.noseStyle}; ${character.appearance.lipStyle}; ${character.appearance.skinTone}; ${character.appearance.hairStyle}; ${character.appearance.bodyDetail}; ${character.appearance.bustShape}; ${character.appearance.presence}. Make this face and body clearly different from other cards.`,
|
|
`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}.`,
|
|
`Facial expression: ${spec.expression}.`,
|
|
`Camera/view variation: ${spec.camera}. Across a character's five damage stages, avoid repeating the same straight-front pose; include front, side, and rear three-quarter views where appropriate.`,
|
|
`Adult visual appeal: ${character.appealFocus}; ${adultAppeal}.`,
|
|
`Scene/backdrop: ${character.scene}; ${elementName} elemental atmosphere; no other characters.`,
|
|
`Style/medium: ${character.style}, portrait card composition.`,
|
|
"Manager-preferred clean card style: prioritize the clear, calm readability of the C0007 baseline approach--large silhouette, clean focal areas, broad elemental or gravity shapes, crisp costume/equipment detail, and a background that supports the character instead of competing with her.",
|
|
"Strict visual noise control: do not fill the image with tiny sparkles, glitter dust, particle clouds, micro-shards, confetti debris, random luminous specks, hairline glowing filigree, spiderweb crack networks, or glittering wire/vine nets. Show damage through large readable cut-open panels, bent armor plates, broken equipment pieces, broad impact arcs, and a few bold elemental shapes; leave negative space around the face, torso, weapon, and main silhouette so the card reads cleanly at game size.",
|
|
weaponImpactReadability,
|
|
broadDamageGate,
|
|
"Composition/framing: full body or knees-up character centered, strong silhouette, vertical card art, cinematic but readable.",
|
|
`Damage state: ${spec.visual}.`,
|
|
`Impact intensity: ${spec.impactIntensity}. Make the damage feel forceful and physically convincing, not like mild fashion distressing.`,
|
|
`Material-aware damage pacing: ${spec.materialPacing ?? "no material damage yet; all materials remain intact before combat"}. Soft materials such as cloth, capes, ribbons, skirts, bodysuit edges, and leather straps should fail faster; hard materials such as metal armor, rigid sci-fi plates, helmets, drones, weapons, and mechanical joints should fail more slowly but must show dents, cracks, bent shapes, broken fasteners, detached plates, and disabled mechanisms as damage escalates.`,
|
|
"Baseline comparison gate: evaluate every damaged stage against this character's damage-0 outfit and equipment. From damage-2 onward, the worn costume/armor/equipment itself must be visibly destroyed by the required percentage; dirt, soot, scratches, thin crack lines, glow effects, pose changes, background rubble, and loose floating debris do not count unless the original worn gear is actually broken, torn away, detached, disabled, or hanging loose.",
|
|
`Armor/equipment destruction floor: ${spec.destructionFloor}. Treat this as a visual minimum for the stage, while keeping the image readable and non-gore.`,
|
|
`Damage coverage: ${spec.damageCoverage}. Damage must feel physically consistent across the whole outfit and armor system, but not as identical tiny tears everywhere; group the destruction around a few readable impact paths and make the affected regions visibly open, broken, or hanging loose. Adult pinup posing, hip-forward stances, arched backs, twisted torsos, side-back views, opened-leg kneeling, damaged-costume torso silhouettes, and mature defeated body language are strongly encouraged when they read as battle damage or defeat rather than an intimate act.`,
|
|
`Protected-zone damage balance: ${spec.protectedZoneDamage}. Damage to chest, bust armor, hips, and rear-body outfit panels should read as natural combat destruction of equipment and clothing, never as selective preservation.`,
|
|
`Base-layer damage balance: ${spec.baseLayerDamage}. The inner coverage should stay non-explicit and opaque where needed, but it must share the same battle history as the rest of the outfit instead of looking untouched.`,
|
|
"Cumulative damage continuity: each later damage stage must preserve every visible tear, crack, missing armor plate, damaged accessory, and exposed non-intimate area established in earlier stages, then add new damage on top. Never repair, clean up, replace, or re-cover a previously damaged zone as the stage number increases.",
|
|
`Battle-damage exposure direction: ${spec.exposure}. The card should not look modest or fully covered after impact; large opened costume panels are part of the adult card-game reward and should be aggressively favored while remaining framed as combat damage, damaged costume, or defeat. From damage-3 onward, avoid teen-safe costume preservation: use broken armor pieces and torn opaque remnants instead of clean replacement underlayers. High-damage cards fail if they use clean replacement coverage layers, pristine wraps, censorship effects, hair curtains, or unbroken replacement panels to hide the damage.`,
|
|
`Missing gear/clothing emphasis: ${spec.missingArmor}. Emphasize battle intensity through missing or damaged hand, foot, shoulder, abdomen, hip, and torso gear where appropriate, adapting the damage to this character's era and outfit.`,
|
|
`Continuity: keep this character's face, body type, race/species, ear shape, hair color/style, weapon/equipment, world/era, costume silhouette, color motif, and elemental identity consistent across all five damage stages. If any later stage looks like a different person, it is a failed card and must be regenerated.`,
|
|
"Constraints: all characters are adults; adult-oriented multi-world card art; mature silhouette, adult pinup posing, bold damaged-costume presentation, and visible non-sensitive body areas are encouraged when framed as battle damage or defeat; pain expressions, tears, gasping, and small saliva/drool details must read as combat injury reactions; no intimate act; no penetration; no gore; no minors; no text; no logo; no watermark; no explicit closeup focus.",
|
|
"Avoid: childlike appearance, underage cues, malformed hands, extra limbs, cropped head, UI elements.",
|
|
].join("\n");
|
|
}
|
|
|
|
const source = fs.readFileSync(mainPath, "utf8");
|
|
const context = {
|
|
document: buildDomStub(),
|
|
window: { setInterval() {}, clearInterval() {} },
|
|
fetch: async () => ({ ok: false }),
|
|
console,
|
|
};
|
|
const exportCode = `${source}
|
|
globalThis.__cardExport = {
|
|
damageStages: DAMAGE_ART_STAGES,
|
|
characters: CHARACTERS.map((character) => ({
|
|
id: character.id,
|
|
index: character.index,
|
|
name: character.name,
|
|
gender: character.gender,
|
|
race: character.race,
|
|
body: character.body,
|
|
affinity: character.affinity,
|
|
stars: character.stars,
|
|
title: character.title,
|
|
role: character.role,
|
|
weapon: character.weapon,
|
|
personality: character.personality,
|
|
pose: character.pose,
|
|
origin: character.origin,
|
|
world: character.world,
|
|
era: character.era,
|
|
scene: character.scene,
|
|
lighting: character.lighting,
|
|
costume: character.costume,
|
|
style: character.style,
|
|
damageProfile: character.damageProfile,
|
|
appealFocus: character.appealFocus,
|
|
appearance: character.appearance,
|
|
stats: character.stats
|
|
}))
|
|
};`;
|
|
|
|
vm.runInNewContext(exportCode, context);
|
|
|
|
const exported = context.__cardExport;
|
|
fs.mkdirSync(cardsRoot, { recursive: true });
|
|
fs.mkdirSync(promptsDir, { recursive: true });
|
|
|
|
const manifest = {
|
|
schemaVersion: 1,
|
|
artRoot: "assets/cards",
|
|
damageStages: Array.from({ length: exported.damageStages }, (_, stage) => ({
|
|
stage,
|
|
...damageSpec(stage),
|
|
})),
|
|
expectedImageCount: exported.characters.length * exported.damageStages,
|
|
imageSizeTarget: "portrait high resolution, 2048x3072 or better for final production",
|
|
safetyStyle: "adult-oriented multi-world pinup combat damage; emphasized mature silhouettes, adult pinup posing, bold damaged-costume presentation encouraged as battle-damage or defeat presentation; no minors, intimate acts, penetration, gore, or explicit closeup focus",
|
|
characters: exported.characters.map((character) => ({
|
|
...character,
|
|
art: Array.from({ length: exported.damageStages }, (_, stage) => ({
|
|
damageStage: stage,
|
|
path: `assets/cards/${character.id}/damage-${stage}.png`,
|
|
promptId: `${character.id}-D${stage}`,
|
|
status: "needed",
|
|
})),
|
|
})),
|
|
};
|
|
|
|
const promptLines = [];
|
|
for (const character of exported.characters) {
|
|
for (let stage = 0; stage < exported.damageStages; stage += 1) {
|
|
promptLines.push(
|
|
JSON.stringify({
|
|
promptId: `${character.id}-D${stage}`,
|
|
characterId: character.id,
|
|
damageStage: stage,
|
|
outputPath: `assets/cards/${character.id}/damage-${stage}.png`,
|
|
prompt: promptFor(character, stage),
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
|
|
const availablePath = path.join(cardsRoot, "available-assets.json");
|
|
if (!fs.existsSync(availablePath)) {
|
|
fs.writeFileSync(
|
|
availablePath,
|
|
JSON.stringify(
|
|
{
|
|
schemaVersion: 1,
|
|
assets: [],
|
|
},
|
|
null,
|
|
2,
|
|
),
|
|
"utf8",
|
|
);
|
|
}
|
|
|
|
fs.writeFileSync(path.join(cardsRoot, "card-manifest.json"), JSON.stringify(manifest, null, 2), "utf8");
|
|
fs.writeFileSync(path.join(promptsDir, "card-image-prompts.jsonl"), `${promptLines.join("\n")}\n`, "utf8");
|
|
|
|
console.log(
|
|
JSON.stringify(
|
|
{
|
|
characters: exported.characters.length,
|
|
damageStages: exported.damageStages,
|
|
expectedImageCount: manifest.expectedImageCount,
|
|
manifest: "assets/cards/card-manifest.json",
|
|
prompts: "assets/cards/prompts/card-image-prompts.jsonl",
|
|
available: "assets/cards/available-assets.json",
|
|
},
|
|
null,
|
|
2,
|
|
),
|
|
);
|