Expand card worlds beyond medieval fantasy

This commit is contained in:
2026-06-10 12:08:05 +09:00
parent a1cb2c4d2e
commit d4e87b38c8
8 changed files with 30017 additions and 24690 deletions

311
main.js
View File

@@ -74,6 +74,289 @@ const WEAPONS = [
{ name: "월도", role: "월도병", bias: { hp: 1.04, atk: 1.06, def: 0.99 } },
];
const WORLD_ARCHETYPES = [
{
world: "현대 한국 캠퍼스 이능 결투",
era: "현시대 대학가",
affinity: "fire",
role: "화염술 대학생",
weapon: "불꽃을 쏘는 스마트폰 촉매와 강의노트",
origin: "서울 사립대 마법동아리 출신",
pose: "후드 집업을 휘날리며 손끝에서 불꽃을 쏘는 자세",
scene: "night university campus plaza, lecture buildings, vending machines, burning magic circles, fire elemental atmosphere",
costume: "modern adult university outfit with cropped tactical jacket, pleated skirt or denim, sneakers, charm accessories, hidden protective pads",
style: "modern urban fantasy trading card key art, cinematic campus lighting, contemporary Korean character design",
damageProfile: "재킷 소매, 가방끈, 운동화 보호대, 허리 장식이 전투 중 찢어지고 그을린다",
bias: { hp: 0.98, atk: 1.12, def: 0.94 },
},
{
world: "K-pop 수상 무대 이능 배틀",
era: "현시대 엔터테인먼트 무대",
affinity: "water",
role: "수류 아이돌",
weapon: "물의 리본 마이크와 무대 드론",
origin: "대형 기획사 비밀 유닛 출신",
pose: "마이크를 든 채 파도 리본을 회전시키는 퍼포먼스 자세",
scene: "concert stage with LED screens, rain rig, spotlights, water ribbons, backstage haze, water elemental atmosphere",
costume: "adult idol stage outfit with jeweled performance jacket, fitted bodice, thigh-high boots, headset mic, waterproof accessories",
style: "glossy music-video fantasy trading card art, stage lights, water choreography, premium idol styling",
damageProfile: "무대 재킷, 부츠 장식, 마이크 리본, 반짝이는 장신구가 젖고 찢어진다",
bias: { hp: 0.94, atk: 1.08, def: 0.98 },
},
{
world: "네온 사이버펑크 거리전",
era: "근미래 메가시티",
affinity: "metal",
role: "메탈 해커",
weapon: "자성 나노드론과 손목 해킹덱",
origin: "지하 데이터 시장 출신",
pose: "한 손으로 홀로그램을 조작하고 다른 손으로 금속 드론을 날리는 자세",
scene: "rainy neon alley, hologram billboards, magnetic drones, chrome sparks, metal elemental atmosphere",
costume: "sleek cyber jacket, reflective bodysuit panels, utility belt, visor, exposed cable ornaments",
style: "cyberpunk TCG key art, neon reflections, chrome detail, high contrast rain lighting",
damageProfile: "전술 재킷, 해킹덱 스트랩, 바이저, 크롬 보호판이 깨지고 스파크가 튄다",
bias: { hp: 0.96, atk: 1.1, def: 1 },
},
{
world: "우주 정거장 무중력 결투",
era: "스페이스 오페라",
affinity: "earth",
role: "중력 조율사",
weapon: "중력 앵커 부츠와 궤도 링",
origin: "달 궤도 정비국 출신",
pose: "무중력에서 몸을 비틀며 중력장을 접는 자세",
scene: "space station observation deck, Earth below, floating debris, gravity lensing, earth elemental atmosphere",
costume: "fitted space suit with armored collar, utility harness, magnetic boots, translucent pressure cape",
style: "space opera fantasy card art, cinematic starfield, polished sci-fi armor, orbital lighting",
damageProfile: "우주복 외피, 어깨 링, 자력 부츠, 산소팩 보호판이 찢기고 깨진다",
bias: { hp: 1.04, atk: 1.02, def: 1.06 },
},
{
world: "포스트 아포칼립스 황무지 레이스",
era: "붕괴 이후 근미래",
affinity: "fire",
role: "폭염 바이크 레이서",
weapon: "연소 엔진 랜스와 점화 장갑",
origin: "사막 고속도로 연맹 출신",
pose: "바이크 옆에서 점화 장갑을 켜고 돌진을 준비하는 자세",
scene: "dusty desert highway, wrecked vehicles, orange sunset, engine flames, fire elemental atmosphere",
costume: "racing leather, armored crop jacket, reinforced boots, torn scarf, heat-resistant plates",
style: "post-apocalyptic action card art, dust, sparks, speed lines, hard sunlight",
damageProfile: "라이더 재킷, 무릎 보호대, 부츠, 목 스카프가 타고 찢어진다",
bias: { hp: 1.02, atk: 1.12, def: 0.96 },
},
{
world: "현대 병원 퇴마 응급실",
era: "현시대 메디컬 오컬트",
affinity: "wood",
role: "생명술 외과의",
weapon: "치유 나이프와 생체 덩굴 실",
origin: "야간 응급센터 출신",
pose: "수술 장갑 낀 손으로 생체 덩굴 실을 펼치는 자세",
scene: "emergency room corridor, monitors, ritual plants, surgical lights, wood elemental atmosphere",
costume: "fitted medical coat, tactical corset harness, gloves, ID lanyard, protective boots",
style: "medical occult fantasy card art, sterile lights mixed with organic magic, dramatic realism",
damageProfile: "의료 코트, 장갑, 보호 앞치마, 허리 하네스가 찢어지고 오염된다",
bias: { hp: 1.08, atk: 0.98, def: 1.04 },
},
{
world: "스팀펑크 공중도시",
era: "증기기관 대체역사",
affinity: "metal",
role: "공중정비 기사",
weapon: "기어 해머와 증기 윙팩",
origin: "부유 항구 정비조합 출신",
pose: "윙팩을 펼치고 기어 해머를 어깨에 얹은 자세",
scene: "floating steampunk harbor, airships, brass gears, steam clouds, metal elemental atmosphere",
costume: "brass corset armor, mechanic gloves, goggles, layered work skirt, wingpack harness",
style: "steampunk collectible card art, brass machinery, warm steam, ornate engineering detail",
damageProfile: "황동 코르셋, 고글, 장갑, 윙팩 하네스와 치마 레이어가 파손된다",
bias: { hp: 1.04, atk: 1.05, def: 1.04 },
},
{
world: "느와르 탐정 도시",
era: "1940년대풍 마법 느와르",
affinity: "water",
role: "비의 탐정",
weapon: "우산 지팡이와 빗물 탄환",
origin: "항만 사립탐정 사무소 출신",
pose: "우산을 기울인 채 빗물 탄환을 모으는 측면 자세",
scene: "rainy noir street, streetlamp, old cars, puddles, cigarette smoke, water elemental atmosphere",
costume: "fitted trench coat, pencil skirt, gloves, rain boots, detective hat with occult charm",
style: "noir fantasy TCG art, moody rain, rim-lit silhouette, elegant retro styling",
damageProfile: "트렌치코트, 장갑, 모자 장식, 우산 천이 찢어지고 젖는다",
bias: { hp: 0.98, atk: 1.04, def: 1.04 },
},
{
world: "현대 스포츠 스타 이능 리그",
era: "현시대 프로 스포츠",
affinity: "earth",
role: "암석 배구 에이스",
weapon: "암석 스파이크 글러브와 코트 라인 결계",
origin: "프로 배구 이능 리그 출신",
pose: "점프 스파이크 직전 공중에서 암석장을 만드는 자세",
scene: "indoor sports arena, court lights, cheering screens, floating stone fragments, earth elemental atmosphere",
costume: "adult pro athlete uniform, compression gear, knee pads, team jacket, reinforced sneakers",
style: "dynamic sports anime trading card art, arena lighting, motion blur, collectible finish",
damageProfile: "팀 재킷, 무릎 보호대, 압박복, 운동화와 팔 보호대가 찢어진다",
bias: { hp: 1.08, atk: 1.06, def: 0.98 },
},
{
world: "심해 왕국 탐사전",
era: "해저 판타지 SF",
affinity: "water",
role: "심해 탐사공주",
weapon: "수압 창과 산호 드론",
origin: "심해 연구 왕정 출신",
pose: "수중에서 창을 뒤로 당기며 물살을 가르는 자세",
scene: "underwater palace ruins, bioluminescent coral, bubbles, pressure waves, water elemental atmosphere",
costume: "sleek aquatic armor, translucent fins, pearl harness, waterproof bodice, coral ornaments",
style: "underwater fantasy card art, bioluminescence, floating hair, soft blue light",
damageProfile: "수중 갑피, 지느러미 장식, 산호 하네스와 진주 끈이 깨지고 흩어진다",
bias: { hp: 1, atk: 1.06, def: 1.02 },
},
{
world: "시간감옥 크로노 판타지",
era: "시간선 밖의 결투장",
affinity: "metal",
role: "시계장치 사도",
weapon: "초침 레이피어와 시간 톱니",
origin: "멈춘 시계탑 출신",
pose: "공중의 톱니를 밟고 초침 검을 찌르는 자세",
scene: "fractured clock tower outside time, floating gears, frozen glass shards, metal elemental atmosphere",
costume: "clockwork bodice armor, capelet, thigh boots, gear halo, ornate gloves",
style: "chrono fantasy TCG key art, suspended motion, gold and silver gears, surreal lighting",
damageProfile: "시계장치 흉갑, 장갑, 망토, 톱니 장식이 부서지고 시간 파편이 흩어진다",
bias: { hp: 0.98, atk: 1.08, def: 1.02 },
},
{
world: "현대 셰프 길드 화염전",
era: "현시대 미식 배틀",
affinity: "fire",
role: "화염 셰프",
weapon: "불꽃 웍과 식칼 부적",
origin: "심야 푸드트럭 결투장 출신",
pose: "웍에서 불기둥을 뽑아내며 한 발을 내딛는 자세",
scene: "night food market, neon signs, steam, flying spices, fire elemental atmosphere",
costume: "stylish chef jacket, waist apron, fitted pants, heatproof gloves, metal accessories",
style: "gourmet battle fantasy card art, steam and flame, vivid street-food lighting",
damageProfile: "셰프 재킷, 앞치마, 장갑, 허리 벨트가 그을리고 찢어진다",
bias: { hp: 1, atk: 1.1, def: 0.98 },
},
{
world: "북극 연구기지 정령전",
era: "현대 극지 연구",
affinity: "water",
role: "빙하 연구원",
weapon: "얼음 코어 드릴과 서리 결계",
origin: "남극 심층빙 연구소 출신",
pose: "드릴을 바닥에 꽂고 빙결장을 펼치는 자세",
scene: "polar research base, aurora, ice core machines, snowstorm, water elemental atmosphere",
costume: "thermal research suit, fur-trim hood, harness, insulated boots, cracked goggles",
style: "arctic science fantasy card art, aurora lighting, snow particles, crisp detail",
damageProfile: "방한복, 고글, 하네스, 부츠, 장갑이 찢어지고 얼음 조각이 박힌다",
bias: { hp: 1.1, atk: 1, def: 1.04 },
},
{
world: "화성 개척지 광산전",
era: "근미래 화성 식민지",
affinity: "earth",
role: "화성 지질술사",
weapon: "레일 픽액스와 중력 곡괭이",
origin: "마스 돔 광산 7구역 출신",
pose: "붉은 암석을 끌어올리며 곡괭이를 내리치는 자세",
scene: "Mars colony mine, red dust, habitat domes, low gravity rocks, earth elemental atmosphere",
costume: "dusty pressure-mining suit, armored suspenders, visor, utility thigh packs, heavy boots",
style: "Mars frontier sci-fi fantasy card art, red dust, industrial lights, rugged detail",
damageProfile: "광산복, 바이저, 중력 곡괭이 스트랩, 부츠 보호대가 찢기고 깨진다",
bias: { hp: 1.12, atk: 1.04, def: 1 },
},
{
world: "고대 신전 런웨이 의식",
era: "신화와 패션이 섞인 초현실 세계",
affinity: "wood",
role: "성목 런웨이 프리스트",
weapon: "꽃가지 왕홀과 활주로 결계",
origin: "세계수 패션 사원 출신",
pose: "런웨이 워킹 중 꽃가지 왕홀로 결계를 펼치는 자세",
scene: "ancient temple runway, giant tree roots, floating petals, golden spotlights, wood elemental atmosphere",
costume: "high-fashion ritual dress, botanical armor pieces, vine heels, jeweled shoulder ornaments",
style: "mythic fashion fantasy card art, editorial runway pose, ornate botanical detail",
damageProfile: "의식 드레스, 식물 갑주, 어깨 장식, 힐 스트랩이 찢어지고 꽃잎이 흩어진다",
bias: { hp: 0.96, atk: 1.06, def: 1.04 },
},
{
world: "서부 개척지 마법 총격전",
era: "와일드 웨스트 대체역사",
affinity: "fire",
role: "태양탄 보안관",
weapon: "태양 룬 리볼버와 불꽃 올가미",
origin: "사막 철도 마을 출신",
pose: "코트를 젖히고 리볼버를 뽑는 결투 자세",
scene: "dusty western town, railroad, saloon lights, solar fire runes, fire elemental atmosphere",
costume: "western corset vest, long coat, leather belt, boots, sheriff badge, brimmed hat",
style: "western fantasy trading card art, dust and sun flare, cinematic duel framing",
damageProfile: "롱코트, 모자, 벨트, 부츠, 조끼 단추가 찢기고 탄 자국이 난다",
bias: { hp: 0.98, atk: 1.12, def: 0.98 },
},
{
world: "가상현실 e스포츠 아레나",
era: "근미래 VR 리그",
affinity: "metal",
role: "VR 검투 스트리머",
weapon: "빛의 컨트롤러 블레이드와 데이터 방패",
origin: "프로 VR 아레나 출신",
pose: "컨트롤러 블레이드를 교차하며 관중석을 향해 도발하는 자세",
scene: "virtual esports arena, pixel particles, giant screens, data shields, metal elemental atmosphere",
costume: "streamer performance suit, LED jacket, headset, fingerless gloves, hologram belt",
style: "neon esports fantasy card art, digital particles, energetic streaming-stage composition",
damageProfile: "LED 재킷, 헤드셋, 장갑, 홀로그램 벨트와 데이터 방패가 깨진다",
bias: { hp: 0.94, atk: 1.1, def: 1 },
},
{
world: "전통 시장 도깨비 야간전",
era: "현대 민속 판타지",
affinity: "wood",
role: "부적 상인",
weapon: "나무 부적 타래와 접이식 장대",
origin: "심야 전통시장 노점 출신",
pose: "부적 타래를 펼치며 좁은 골목을 막아서는 자세",
scene: "night traditional market, paper lanterns, wooden talismans, alley mist, wood elemental atmosphere",
costume: "modern hanbok jacket mixed with streetwear, waist pouch, boots, talisman ornaments",
style: "Korean urban folklore card art, lantern light, street texture, magical realism",
damageProfile: "개량 한복 재킷, 부적 끈, 허리 주머니, 부츠 장식이 찢어지고 흩어진다",
bias: { hp: 1, atk: 1.02, def: 1.08 },
},
{
world: "럭셔리 카지노 운명전",
era: "현대 오컬트 카지노",
affinity: "metal",
role: "운명 딜러",
weapon: "금속 카드덱과 룰렛 링",
origin: "해안 카지노 VIP룸 출신",
pose: "카드를 부채꼴로 펼치며 룰렛 링을 띄우는 자세",
scene: "luxury casino floor, roulette lights, gold chips, mirrored ceiling, metal elemental atmosphere",
costume: "elegant dealer suit, fitted vest, high heels, metallic card garters, gloves",
style: "luxury occult casino card art, gold reflections, dramatic spotlight, sleek glamour",
damageProfile: "딜러 조끼, 장갑, 카드 홀더, 하이힐 스트랩과 금속 장식이 파손된다",
bias: { hp: 0.96, atk: 1.06, def: 1.04 },
},
{
world: "정글 유적 드론 탐사",
era: "현대 고고학 어드벤처",
affinity: "earth",
role: "유적 드론 고고학자",
weapon: "석판 드론과 진동 채찍",
origin: "열대 유적 탐사팀 출신",
pose: "석판 드론을 띄우고 채찍으로 바위를 가르는 자세",
scene: "jungle ruins, vines, ancient stones, scanning drones, earth elemental atmosphere",
costume: "adventurer crop jacket, utility shorts or pants, boots, satchel, protective straps",
style: "adventure fantasy trading card art, jungle light shafts, ancient stone detail",
damageProfile: "탐사 재킷, 가방끈, 부츠, 보호 스트랩과 장갑이 찢어진다",
bias: { hp: 1.04, atk: 1.06, def: 1 },
},
];
const PERSONALITIES = [
"냉정한 전략가",
"낙천적인 돌파형",
@@ -546,12 +829,20 @@ function buildCharacterPool(count) {
const gender = "여성";
const race = weightedPick(RACE_PROFILES, rng);
const body = pickRng(BODY_TYPES, rng);
const weapon = pickRng(WEAPONS, rng);
const legacyWeapon = pickRng(WEAPONS, rng);
const worldProfile = index < 3 ? null : WORLD_ARCHETYPES[(index - 3) % WORLD_ARCHETYPES.length];
const weapon = worldProfile
? {
name: worldProfile.weapon,
role: worldProfile.role,
bias: worldProfile.bias,
}
: legacyWeapon;
const personality = pickRng(PERSONALITIES, rng);
const pose = pickRng(POSES, rng);
const origin = pickRng(ORIGINS, rng);
const damageProfile = pickRng(DAMAGE_PROFILES, rng);
const affinity = ELEMENT_KEYS[(index + Math.floor(rng() * ELEMENT_KEYS.length)) % ELEMENT_KEYS.length];
const pose = worldProfile ? worldProfile.pose : pickRng(POSES, rng);
const origin = worldProfile ? worldProfile.origin : pickRng(ORIGINS, rng);
const damageProfile = worldProfile ? worldProfile.damageProfile : pickRng(DAMAGE_PROFILES, rng);
const affinity = worldProfile?.affinity ?? ELEMENT_KEYS[(index + Math.floor(rng() * ELEMENT_KEYS.length)) % ELEMENT_KEYS.length];
const tier = weightedPick(STAR_TIERS, rng);
const name = uniqueName(createName(index, gender, race, rng), usedNames, index);
const title = `${pickByIndex(TITLE_PREFIXES, index, tier.stars)} ${ELEMENTS[affinity].name}${pickByIndex(TITLE_NOUNS, index, weapon.name.length)}`;
@@ -579,6 +870,11 @@ function buildCharacterPool(count) {
personality,
pose,
origin,
world: worldProfile?.world ?? "고전 판타지 전장",
era: worldProfile?.era ?? "중세풍 판타지",
scene: worldProfile?.scene ?? `fantasy duel arena with ${ELEMENTS[affinity].name} elemental atmosphere, no other characters`,
costume: worldProfile?.costume ?? "ornate fantasy armor, cloak, weapon accessories, and battle-ready protective gear",
style: worldProfile?.style ?? "polished semi-real painterly TCG key art, high-detail Korean/Asian fantasy mixed with global high-fantasy design",
damageProfile,
appealFocus,
appearance,
@@ -830,10 +1126,13 @@ function makeCardNode(card) {
<span class="tag">${card.character.race}</span>
<span class="tag">${card.character.body}</span>
<span class="tag">${card.character.weapon}</span>
<span class="tag">${card.character.era}</span>
</div>
<div class="character-notes">
<div><strong>성향</strong><span>${card.character.personality}</span></div>
<div><strong>세계관</strong><span>${card.character.world}</span></div>
<div><strong>복장/장비</strong><span>${card.character.costume}</span></div>
<div><strong>성인 연출</strong><span>${card.character.appealFocus}</span></div>
<div><strong>외형 DNA</strong><span>${appearanceSummary(card.character.appearance)}</span></div>
<div><strong>포즈</strong><span>${card.character.pose}</span></div>
@@ -1088,7 +1387,7 @@ function renderRoster() {
<span>${character.id} · ${character.gender} · ${character.race}</span>
<span class="element-chip">${element.name}</span>
</div>
<p>${character.personality} · ${character.origin}</p>
<p>${character.personality} · ${character.world}</p>
<small>${character.appearance.faceShape} · ${character.appearance.eyeStyle} · ${character.appearance.bodyDetail}</small>
<small>${character.appealFocus}</small>
<small>${character.pose}</small>