Spaces:
Sleeping
Sleeping
Upload 140 files
Browse files
content/beatcards/E06.yaml
CHANGED
|
@@ -410,8 +410,6 @@ cards:
|
|
| 410 |
text: κ·Έλ¦¬κ³ κ·Έ μκ°, λ§€μΌ λ°€μ κΏκ³Ό μ΄ λ μ μ΄ λ¬΄κ΄νμ§ μμμ§λ λͺ¨λ₯Έλ€λ μκ°μ΄, μ²μμΌλ‘ λ¨Έλ¦Ώμμ μ€μΉκ³ μ§λκ°λ€.
|
| 411 |
- type: prose
|
| 412 |
text: λλ κ³ κ°λ₯Ό μ μλ€. κ·Έλ¬λ νλ² λ μ€λ₯Έ κ·Έκ²μ, κ±°μΈ μ νΈλ₯Έ λ μ μ²λΌ, μ무리 μΈλ©΄ν΄λ μ¬λΌμ§μ§ μμλ€.
|
| 413 |
-
- type: prose
|
| 414 |
-
text: μ¨κΉ β **E06-10C** (μμ‘΄ μ¬ν, β‘β’ μ¨λ¨ν) Β· μλ¦Ό β **E06-10B** (μμ λ§ μλ, β μ²λ¨ μ¨λ¨ν)
|
| 415 |
choices:
|
| 416 |
- label: λ μ μ κ°λ¦¬κ³ νΌμ κ°μ§νλ€
|
| 417 |
modifiers: &id002 []
|
|
|
|
| 410 |
text: κ·Έλ¦¬κ³ κ·Έ μκ°, λ§€μΌ λ°€μ κΏκ³Ό μ΄ λ μ μ΄ λ¬΄κ΄νμ§ μμμ§λ λͺ¨λ₯Έλ€λ μκ°μ΄, μ²μμΌλ‘ λ¨Έλ¦Ώμμ μ€μΉκ³ μ§λκ°λ€.
|
| 411 |
- type: prose
|
| 412 |
text: λλ κ³ κ°λ₯Ό μ μλ€. κ·Έλ¬λ νλ² λ μ€λ₯Έ κ·Έκ²μ, κ±°μΈ μ νΈλ₯Έ λ μ μ²λΌ, μ무리 μΈλ©΄ν΄λ μ¬λΌμ§μ§ μμλ€.
|
|
|
|
|
|
|
| 413 |
choices:
|
| 414 |
- label: λ μ μ κ°λ¦¬κ³ νΌμ κ°μ§νλ€
|
| 415 |
modifiers: &id002 []
|
engine/__pycache__/router.cpython-314.pyc
CHANGED
|
Binary files a/engine/__pycache__/router.cpython-314.pyc and b/engine/__pycache__/router.cpython-314.pyc differ
|
|
|
engine/router.py
CHANGED
|
@@ -34,9 +34,8 @@ COVER_IMAGE = "/assets/νλμ΄ μ΄λ―Έμ§/1.png"
|
|
| 34 |
IMAGE_MAP = {
|
| 35 |
"E01-07": "/assets/νλμ΄ μ΄λ―Έμ§/4.png", # 6μΈμ λ°€ β "κ·Έλ
λ λ―Έλλ μμ΄ λλ₯Ό λ΄λ €λ€λ³΄μλ€"
|
| 36 |
"E03-03": "/assets/μΈλ¬Ό/μΉ΄λ₯΄λ°λΌ.png", # μ¬ν β κ°μ κΏμ λλ©΄
|
| 37 |
-
"E05-02": "/assets/λ°°κ²½/μ±.png", # μ΄μνμ λ°€ λ¬΄λ ΅ β κ³ μ±
|
| 38 |
"E06-09": "/assets/μΈλ¬Ό/μΉ΄λ₯΄λ°λΌ κ°λ©΄.png", # λλ§μ λΉλ° β κ°μ₯ μνν λ€μ ν¨
|
| 39 |
-
|
| 40 |
}
|
| 41 |
|
| 42 |
# μΈνΈλ‘ μνμ€ β νμ§ "λ°€μ μ°λ€" λ€, μ μμμλ§ (μ΄μ΄ μ½κΈ°λ 건λλ)
|
|
|
|
| 34 |
IMAGE_MAP = {
|
| 35 |
"E01-07": "/assets/νλμ΄ μ΄λ―Έμ§/4.png", # 6μΈμ λ°€ β "κ·Έλ
λ λ―Έλλ μμ΄ λλ₯Ό λ΄λ €λ€λ³΄μλ€"
|
| 36 |
"E03-03": "/assets/μΈλ¬Ό/μΉ΄λ₯΄λ°λΌ.png", # μ¬ν β κ°μ κΏμ λλ©΄
|
|
|
|
| 37 |
"E06-09": "/assets/μΈλ¬Ό/μΉ΄λ₯΄λ°λΌ κ°λ©΄.png", # λλ§μ λΉλ° β κ°μ₯ μνν λ€μ ν¨
|
| 38 |
+
# E05-02(μ±)Β·E06-10(μΉ¨μ€) μ»·μ μ κ±° β μ₯μ λ°°κ²½κ³Ό μ€λ³΅μ΄λΌ μ»· νμ΄μ§κ° κ΅°λλκΈ° (μ μ κ²°μ )
|
| 39 |
}
|
| 40 |
|
| 41 |
# μΈνΈλ‘ μνμ€ β νμ§ "λ°€μ μ°λ€" λ€, μ μμμλ§ (μ΄μ΄ μ½κΈ°λ 건λλ)
|
engine/schemas/__pycache__/validate.cpython-314.pyc
CHANGED
|
Binary files a/engine/schemas/__pycache__/validate.cpython-314.pyc and b/engine/schemas/__pycache__/validate.cpython-314.pyc differ
|
|
|
engine/schemas/validate.py
CHANGED
|
@@ -119,6 +119,17 @@ def check_links(episodes: list[EpisodeFile], chapter, r_cards) -> tuple[list[str
|
|
| 119 |
if not has_out:
|
| 120 |
warnings.append(f"{c.id}: λκ°λ λ§ν¬ μμ (μ±ν° μΆκ΅¬κ° μλλ©΄ κ²°ν¨)")
|
| 121 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
# λΉμ½ μΉ΄λ β νλ©΄ νλμ μ΅μ λ°λ λ―Έλ¬ (μμ± κ°μ΄λ: docs/contract/beatcard_guide.md)
|
| 123 |
MIN_CARD_CHARS = 200
|
| 124 |
for c in cards.values():
|
|
|
|
| 119 |
if not has_out:
|
| 120 |
warnings.append(f"{c.id}: λκ°λ λ§ν¬ μμ (μ±ν° μΆκ΅¬κ° μλλ©΄ κ²°ν¨)")
|
| 121 |
|
| 122 |
+
# μ μ λ©ν λμ β λ³Όλ μΉ΄λID(**E##-) κ°μ νΈμ§ λ©λͺ¨κ° μ§λ¬Έ/κ²°κ³Όμ λ¨μΌλ©΄ νλ©΄μ λ
ΈμΆλλ€
|
| 123 |
+
import re as _re
|
| 124 |
+
leak = _re.compile(r"\*\*E\d{2}-|\*\*β·")
|
| 125 |
+
for c in cards.values():
|
| 126 |
+
for b in c.narration:
|
| 127 |
+
if leak.search(b.text):
|
| 128 |
+
warnings.append(f"{c.id}: μ§λ¬Έμ μ μ λ©ν λμ μμ¬ β {b.text[:40]}β¦")
|
| 129 |
+
for ch in c.choices:
|
| 130 |
+
if leak.search(ch.result or ""):
|
| 131 |
+
warnings.append(f"{c.id}: μ ν κ²°κ³Όμ μ μ λ©ν λμ μμ¬ β {ch.result[:40]}β¦")
|
| 132 |
+
|
| 133 |
# λΉμ½ μΉ΄λ β νλ©΄ νλμ μ΅μ λ°λ λ―Έλ¬ (μμ± κ°μ΄λ: docs/contract/beatcard_guide.md)
|
| 134 |
MIN_CARD_CHARS = 200
|
| 135 |
for c in cards.values():
|
scripts/beatcard_convert.py
CHANGED
|
@@ -40,6 +40,9 @@ RE_RESULT_NEXT = re.compile(r"β\s*\*{0,2}([A-Z]\d{2}-\d{2}[A-Z]?)\*{0,2}")
|
|
| 40 |
RE_BRANCH_MAP = re.compile(r"([κ°-ν£]+)\s*β\s*\*\*([A-Z]\d{2}-\d{2}[A-Z]?)\*\*")
|
| 41 |
RE_RCARD_REF = re.compile(r"R-[κ°-ν£\w]+")
|
| 42 |
RE_SECTION_STOP = re.compile(r"^(?:βΈ\s*λ
ΈνΈ:|π¬|πΌ)")
|
|
|
|
|
|
|
|
|
|
| 43 |
RE_STAGE = re.compile(r"^###\s*(\d)λ¨κ³\s*[ββ-]\s*(.+)$", re.M)
|
| 44 |
|
| 45 |
|
|
@@ -216,6 +219,8 @@ class EpisodeParser:
|
|
| 216 |
continue
|
| 217 |
if RE_SECTION_STOP.match(s):
|
| 218 |
break
|
|
|
|
|
|
|
| 219 |
|
| 220 |
if RE_CHOICE_HEADER.match(s):
|
| 221 |
flush_prose()
|
|
|
|
| 40 |
RE_BRANCH_MAP = re.compile(r"([κ°-ν£]+)\s*β\s*\*\*([A-Z]\d{2}-\d{2}[A-Z]?)\*\*")
|
| 41 |
RE_RCARD_REF = re.compile(r"R-[κ°-ν£\w]+")
|
| 42 |
RE_SECTION_STOP = re.compile(r"^(?:βΈ\s*λ
ΈνΈ:|π¬|πΌ)")
|
| 43 |
+
# μ μ λ©ν λΌμΈ β μ§λ¬ΈμΌλ‘ μλ©΄ μ λλ λΆκΈ° λ§€ν λ©λͺ¨ (μ: "μ¨κΉ β **E06-10C** (β¦)").
|
| 44 |
+
# λ³Όλ μΉ΄λID μ°Έμ‘°κ° λ³Έλ¬Έ λμ¬/μ§λ¬Έμ λ±μ₯ν μΌμ μλ€
|
| 45 |
+
RE_META_LINE = re.compile(r"\*\*E\d{2}-")
|
| 46 |
RE_STAGE = re.compile(r"^###\s*(\d)λ¨κ³\s*[ββ-]\s*(.+)$", re.M)
|
| 47 |
|
| 48 |
|
|
|
|
| 219 |
continue
|
| 220 |
if RE_SECTION_STOP.match(s):
|
| 221 |
break
|
| 222 |
+
if RE_META_LINE.search(s): # λΆκΈ° λ§€ν λ©λͺ¨ β μ§λ¬Έ λμ μ°¨λ¨
|
| 223 |
+
continue
|
| 224 |
|
| 225 |
if RE_CHOICE_HEADER.match(s):
|
| 226 |
flush_prose()
|
web/index.html
CHANGED
|
@@ -36,6 +36,9 @@
|
|
| 36 |
transition: width .8s ease; box-shadow: 0 0 6px rgba(201,168,106,.5); }
|
| 37 |
#progress-label { position: fixed; top: 10px; right: 16px; color: var(--gold-dim);
|
| 38 |
font-size: .74rem; letter-spacing: .22em; opacity: .85; z-index: 40; }
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
/* ββ λ³Έλ¬Έ β νλ©΄ λ¨μ μ½κΈ°: ν νλ©΄μ λ·°ν¬νΈ μμ λ€μ΄κ°λ€ (μ€ν¬λ‘€ λμ νμ΄μ§ λκΉ) ββ */
|
| 41 |
#stage { max-width: 680px; margin: 0 auto; padding: 54px 24px 96px;
|
|
@@ -64,6 +67,7 @@
|
|
| 64 |
.vn-stage { position: relative; margin: 1.2em auto 0; height: min(76vh, 860px);
|
| 65 |
overflow: hidden; border: 1px solid rgba(201,168,106,.28);
|
| 66 |
box-shadow: 0 0 80px rgba(0,0,0,.9); }
|
|
|
|
| 67 |
.vn-stage-art { position: absolute; inset: 0; width: 100%; height: 100%;
|
| 68 |
object-fit: cover; object-position: top center; }
|
| 69 |
.vn-stage::after { content: ""; position: absolute; inset: 0;
|
|
@@ -209,6 +213,7 @@
|
|
| 209 |
|
| 210 |
<div id="progress-wrap"><div id="progress-bar"></div></div>
|
| 211 |
<div id="progress-label"></div>
|
|
|
|
| 212 |
<div id="browse-ribbon">μ§λμ¨ νμ΄μ§ β μ€λ₯Έμͺ½μ λλ¬ μ§κΈμΌλ‘</div>
|
| 213 |
|
| 214 |
<div id="cover">
|
|
@@ -343,8 +348,8 @@ function charLines(g) { // μμ¬ μ λ°ν β μ’ νμλͺ
Β·
|
|
| 343 |
`<div class="char-line fade"><span class="spk">${esc(name)}</span>β${esc(t)}β</div>`).join('');
|
| 344 |
}
|
| 345 |
|
| 346 |
-
function vnStage(g, choices) {
|
| 347 |
-
let h = `<div class="vn-stage fade">` +
|
| 348 |
`<img class="vn-stage-art" src="${encodeURI(g.portrait)}" alt="${esc(g.spk)}">` +
|
| 349 |
`<div class="vn-stage-panel"><div class="vn-name">${esc(g.spk)}</div>`;
|
| 350 |
for (const t of g.lines) h += `<div class="vn-line">β${esc(t)}β</div>`;
|
|
@@ -424,8 +429,16 @@ function buildScreens(step, fresh) {
|
|
| 424 |
|
| 425 |
groups.forEach((g, k) => {
|
| 426 |
if (k === stageIdx) { // μ€ν
μ΄μ§ = λ
립 νλ©΄ (μ΄λ―Έμ§+λμ¬λ§)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 427 |
flushSeg();
|
| 428 |
-
screens.push(vnStage(g, stageIsLast ? choices : ''));
|
| 429 |
return;
|
| 430 |
}
|
| 431 |
const html = g.lines ? charLines(g) : block(g.block);
|
|
@@ -454,7 +467,8 @@ function choicesHtml(step) {
|
|
| 454 |
function setProgress(p, epLabel) {
|
| 455 |
if (!p) return;
|
| 456 |
$('progress-bar').style.width = (p.index / p.total * 100).toFixed(1) + '%';
|
| 457 |
-
$('progress-label').textContent = `${
|
|
|
|
| 458 |
}
|
| 459 |
|
| 460 |
function showStep(step, fresh) {
|
|
|
|
| 36 |
transition: width .8s ease; box-shadow: 0 0 6px rgba(201,168,106,.5); }
|
| 37 |
#progress-label { position: fixed; top: 10px; right: 16px; color: var(--gold-dim);
|
| 38 |
font-size: .74rem; letter-spacing: .22em; opacity: .85; z-index: 40; }
|
| 39 |
+
#chapter-label { position: fixed; top: 10px; left: 50%; transform: translateX(-50%);
|
| 40 |
+
color: var(--gold-dim); font-size: .72rem; letter-spacing: .3em;
|
| 41 |
+
text-indent: .3em; opacity: .8; z-index: 40; white-space: nowrap; }
|
| 42 |
|
| 43 |
/* ββ λ³Έλ¬Έ β νλ©΄ λ¨μ μ½κΈ°: ν νλ©΄μ λ·°ν¬νΈ μμ λ€μ΄κ°λ€ (μ€ν¬λ‘€ λμ νμ΄μ§ λκΉ) ββ */
|
| 44 |
#stage { max-width: 680px; margin: 0 auto; padding: 54px 24px 96px;
|
|
|
|
| 67 |
.vn-stage { position: relative; margin: 1.2em auto 0; height: min(76vh, 860px);
|
| 68 |
overflow: hidden; border: 1px solid rgba(201,168,106,.28);
|
| 69 |
box-shadow: 0 0 80px rgba(0,0,0,.9); }
|
| 70 |
+
.vn-stage.with-lead { height: min(62vh, 720px); } /* 리λμΈ μ§λ¬Έμ΄ μμ λΆλ νλ©΄ */
|
| 71 |
.vn-stage-art { position: absolute; inset: 0; width: 100%; height: 100%;
|
| 72 |
object-fit: cover; object-position: top center; }
|
| 73 |
.vn-stage::after { content: ""; position: absolute; inset: 0;
|
|
|
|
| 213 |
|
| 214 |
<div id="progress-wrap"><div id="progress-bar"></div></div>
|
| 215 |
<div id="progress-label"></div>
|
| 216 |
+
<div id="chapter-label"></div>
|
| 217 |
<div id="browse-ribbon">μ§λμ¨ νμ΄μ§ β μ€λ₯Έμͺ½μ λλ¬ μ§κΈμΌλ‘</div>
|
| 218 |
|
| 219 |
<div id="cover">
|
|
|
|
| 348 |
`<div class="char-line fade"><span class="spk">${esc(name)}</span>β${esc(t)}β</div>`).join('');
|
| 349 |
}
|
| 350 |
|
| 351 |
+
function vnStage(g, choices, withLead) { // μ΄μ + μ΄λ¦ν + κ·Έ μΊλ¦ν°μ λμ¬ (+μ νμ§)
|
| 352 |
+
let h = `<div class="vn-stage${withLead ? ' with-lead' : ''} fade">` +
|
| 353 |
`<img class="vn-stage-art" src="${encodeURI(g.portrait)}" alt="${esc(g.spk)}">` +
|
| 354 |
`<div class="vn-stage-panel"><div class="vn-name">${esc(g.spk)}</div>`;
|
| 355 |
for (const t of g.lines) h += `<div class="vn-line">β${esc(t)}β</div>`;
|
|
|
|
| 429 |
|
| 430 |
groups.forEach((g, k) => {
|
| 431 |
if (k === stageIdx) { // μ€ν
μ΄μ§ = λ
립 νλ©΄ (μ΄λ―Έμ§+λμ¬λ§)
|
| 432 |
+
// μ§μ 리λμΈ(role=lead_in, μ§§μ λμ
μ§λ¬Έ)μ κ°μ νλ©΄μ νλ μ "λ° μμͺ½"μ β
|
| 433 |
+
// νΌμ νλ©΄μ μ°¨μ§νμ§λ, μ΄λ―Έμ§μ κ²ΉμΉμ§λ μκ². νλ μμ with-leadλ‘ μΆμ
|
| 434 |
+
let leadHtml = '';
|
| 435 |
+
const prev = groups[k - 1];
|
| 436 |
+
if (prev && prev.block && prev.block.role === 'lead_in'
|
| 437 |
+
&& seg.length && seg[seg.length - 1].html === block(prev.block)) {
|
| 438 |
+
leadHtml = seg.pop().html;
|
| 439 |
+
}
|
| 440 |
flushSeg();
|
| 441 |
+
screens.push(leadHtml + vnStage(g, stageIsLast ? choices : '', !!leadHtml));
|
| 442 |
return;
|
| 443 |
}
|
| 444 |
const html = g.lines ? charLines(g) : block(g.block);
|
|
|
|
| 467 |
function setProgress(p, epLabel) {
|
| 468 |
if (!p) return;
|
| 469 |
$('progress-bar').style.width = (p.index / p.total * 100).toFixed(1) + '%';
|
| 470 |
+
$('progress-label').textContent = `${p.index} / ${p.total}`;
|
| 471 |
+
$('chapter-label').textContent = epLabel || ''; // νμ¬ μ₯(θ©±) β μλ¨ μ€μ μμ νμ
|
| 472 |
}
|
| 473 |
|
| 474 |
function showStep(step, fresh) {
|