Spaces:
Sleeping
Sleeping
Upload 140 files
Browse files- content/beatcards/E06.yaml +2 -3
- engine/__pycache__/router.cpython-312.pyc +0 -0
- engine/__pycache__/router.cpython-314.pyc +0 -0
- engine/router.py +4 -4
- engine/schemas/__pycache__/chapter.cpython-314.pyc +0 -0
- engine/schemas/__pycache__/validate.cpython-312.pyc +0 -0
- engine/schemas/__pycache__/validate.cpython-314.pyc +0 -0
- engine/schemas/validate.py +3 -2
- engine/services/__pycache__/paging.cpython-314.pyc +0 -0
- engine/services/paging.py +10 -1
- scripts/beatcard_convert.py +7 -1
- web/index.html +103 -78
content/beatcards/E06.yaml
CHANGED
|
@@ -348,14 +348,13 @@ cards:
|
|
| 348 |
- label: ๋๋ง์ ๋น๋ฐ์ ๋์กฐํ๋ค
|
| 349 |
modifiers: &id001 []
|
| 350 |
axis: trust
|
| 351 |
-
result: ๋๋ ๊ณ ๊ฐ๋ฅผ ๋๋์๋ค. ๊ทธ๋
์ ๋ง์ด ์ณ์ ๊ฒ๋ง ๊ฐ์๋ค. ์ฐ๋ฆฌ ์ฌ์ด์ ์ผ์, ๋ค๋ฅธ ๋๊ตฌ๋ ์ ํ์๋ ์์๋ค.
|
| 352 |
next: null
|
| 353 |
preset_branch: E06-10C
|
| 354 |
- label: ๊ทธ๋๋ ๋๊ตฐ๊ฐ์๊ฒ ์๋ ค์ผ ํ๋ค๊ณ ์๊ฐํ๋ค
|
| 355 |
modifiers: *id001
|
| 356 |
axis: doubt
|
| 357 |
-
result: ๊ทธ๋ฐ๋ฐ๋, ๋ง์ ๊น์ ๊ณณ์์ ๋ฌด์ธ๊ฐ๊ฐ ์๊พธ ๊ณ ๊ฐ๋ฅผ ๋ค์๋ค. ์ด๋๋ก ์๋ค๋ค ์ฃฝ์ ์๋ ์๋ค๋, ๊ฐ๋๋ค๋ ์ธ์นจ์ด.
|
| 358 |
-
์ด์ด์ง.
|
| 359 |
next: E06-10
|
| 360 |
r_card_refs: []
|
| 361 |
note: '**[์์งD ํด๊ธ + ์์งC ๋ถ๊ธฐ ์
๋ ฅ]** ํ๋ฉด("์ฃฝ์์ ์ถ๋ณต")์ ์ ์ / **์ฌ์ธต("๊ทธ๊ฑด ๋์ด ์๋์ผ, ์์์ด์ผ")์ ์ ๋ขฐ้ซ์์๋ง** โ โก๋ํ ์ ํน์ ์ง์ ๋์ค(์ ํ=์์ฌ์๋
|
|
|
|
| 348 |
- label: ๋๋ง์ ๋น๋ฐ์ ๋์กฐํ๋ค
|
| 349 |
modifiers: &id001 []
|
| 350 |
axis: trust
|
| 351 |
+
result: ๋๋ ๊ณ ๊ฐ๋ฅผ ๋๋์๋ค. ๊ทธ๋
์ ๋ง์ด ์ณ์ ๊ฒ๋ง ๊ฐ์๋ค. ์ฐ๋ฆฌ ์ฌ์ด์ ์ผ์, ๋ค๋ฅธ ๋๊ตฌ๋ ์ ํ์๋ ์์๋ค.
|
| 352 |
next: null
|
| 353 |
preset_branch: E06-10C
|
| 354 |
- label: ๊ทธ๋๋ ๋๊ตฐ๊ฐ์๊ฒ ์๋ ค์ผ ํ๋ค๊ณ ์๊ฐํ๋ค
|
| 355 |
modifiers: *id001
|
| 356 |
axis: doubt
|
| 357 |
+
result: ๊ทธ๋ฐ๋ฐ๋, ๋ง์ ๊น์ ๊ณณ์์ ๋ฌด์ธ๊ฐ๊ฐ ์๊พธ ๊ณ ๊ฐ๋ฅผ ๋ค์๋ค. ์ด๋๋ก ์๋ค๋ค ์ฃฝ์ ์๋ ์๋ค๋, ๊ฐ๋๋ค๋ ์ธ์นจ์ด.
|
|
|
|
| 358 |
next: E06-10
|
| 359 |
r_card_refs: []
|
| 360 |
note: '**[์์งD ํด๊ธ + ์์งC ๋ถ๊ธฐ ์
๋ ฅ]** ํ๋ฉด("์ฃฝ์์ ์ถ๋ณต")์ ์ ์ / **์ฌ์ธต("๊ทธ๊ฑด ๋์ด ์๋์ผ, ์์์ด์ผ")์ ์ ๋ขฐ้ซ์์๋ง** โ โก๋ํ ์ ํน์ ์ง์ ๋์ค(์ ํ=์์ฌ์๋
|
engine/__pycache__/router.cpython-312.pyc
CHANGED
|
Binary files a/engine/__pycache__/router.cpython-312.pyc and b/engine/__pycache__/router.cpython-312.pyc differ
|
|
|
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
|
@@ -50,8 +50,8 @@ INTRO_IMAGES = [
|
|
| 50 |
SPEAKER_PORTRAIT = {
|
| 51 |
"์นด๋ฅด๋ฐ๋ผ": "/assets/์ธ๋ฌผ/์นด๋ฅด๋ฐ๋ผ.png",
|
| 52 |
"์๋ฒ์ง": "/assets/์ธ๋ฌผ/์๋ฒ์ง.png",
|
| 53 |
-
|
| 54 |
-
|
| 55 |
}
|
| 56 |
|
| 57 |
# ์ฅ์๋ณ ๋ฐฐ๊ฒฝ (๋น์ฃผ์ผ ๋
ธ๋ฒจ) โ ์นด๋ ํ๊ทธ `#์ฅ์/X` ๊ธฐ์ค. ๋งคํ ์๋ ์ฅ์(์ ์ยทํ๋ยท์์ฌ ๋ฑ)๋
|
|
@@ -158,10 +158,10 @@ def _step_json(ps: PlaySession) -> dict:
|
|
| 158 |
"card": {"id": last.id, "title": first.title, "episode": first.episode_id,
|
| 159 |
"episode_title": ps.episode_titles.get(first.episode_id, first.episode_id)},
|
| 160 |
"narration": [{"type": b.type, "text": b.text, "speaker": b.speaker,
|
| 161 |
-
"role": b.role,
|
| 162 |
"portrait": (SPEAKER_PORTRAIT.get(b.speaker)
|
| 163 |
if b.type == "character_dialogue" else None)}
|
| 164 |
-
for b in page.narration],
|
| 165 |
"choices": [{"label": c.label} for c in page.choices],
|
| 166 |
"can_chat": page.can_chat,
|
| 167 |
"image": IMAGE_MAP.get(first.id),
|
|
|
|
| 50 |
SPEAKER_PORTRAIT = {
|
| 51 |
"์นด๋ฅด๋ฐ๋ผ": "/assets/์ธ๋ฌผ/์นด๋ฅด๋ฐ๋ผ.png",
|
| 52 |
"์๋ฒ์ง": "/assets/์ธ๋ฌผ/์๋ฒ์ง.png",
|
| 53 |
+
# ๋ง๋ดยท๋ง๋๋ชจ์์ ค์ ์ ๋ฉด ์คํ
์ด์ง ๋์์์ ์ ์ธ (์ ์ ๊ฒฐ์ โ ์์๋ธ ์ฅ๋ฉด์
|
| 54 |
+
# ๋ณธ๋ฌธ ํ๋ฆ์ผ๋ก. ์์
๋งคํ๋ ์ถ์ ์ด์์). ํ์ ์ ์ฌ๊ธฐ ํ ์ค๋ก ๋ณต์
|
| 55 |
}
|
| 56 |
|
| 57 |
# ์ฅ์๋ณ ๋ฐฐ๊ฒฝ (๋น์ฃผ์ผ ๋
ธ๋ฒจ) โ ์นด๋ ํ๊ทธ `#์ฅ์/X` ๊ธฐ์ค. ๋งคํ ์๋ ์ฅ์(์ ์ยทํ๋ยท์์ฌ ๋ฑ)๋
|
|
|
|
| 158 |
"card": {"id": last.id, "title": first.title, "episode": first.episode_id,
|
| 159 |
"episode_title": ps.episode_titles.get(first.episode_id, first.episode_id)},
|
| 160 |
"narration": [{"type": b.type, "text": b.text, "speaker": b.speaker,
|
| 161 |
+
"role": b.role, "card": cid,
|
| 162 |
"portrait": (SPEAKER_PORTRAIT.get(b.speaker)
|
| 163 |
if b.type == "character_dialogue" else None)}
|
| 164 |
+
for b, cid in zip(page.narration, page.narration_cards)],
|
| 165 |
"choices": [{"label": c.label} for c in page.choices],
|
| 166 |
"can_chat": page.can_chat,
|
| 167 |
"image": IMAGE_MAP.get(first.id),
|
engine/schemas/__pycache__/chapter.cpython-314.pyc
CHANGED
|
Binary files a/engine/schemas/__pycache__/chapter.cpython-314.pyc and b/engine/schemas/__pycache__/chapter.cpython-314.pyc differ
|
|
|
engine/schemas/__pycache__/validate.cpython-312.pyc
CHANGED
|
Binary files a/engine/schemas/__pycache__/validate.cpython-312.pyc and b/engine/schemas/__pycache__/validate.cpython-312.pyc differ
|
|
|
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,9 +119,10 @@ 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 |
-
# ์ ์ ๋ฉํ ๋์ โ
|
|
|
|
| 123 |
import re as _re
|
| 124 |
-
leak = _re.compile(r"\
|
| 125 |
for c in cards.values():
|
| 126 |
for b in c.narration:
|
| 127 |
if leak.search(b.text):
|
|
|
|
| 119 |
if not has_out:
|
| 120 |
warnings.append(f"{c.id}: ๋๊ฐ๋ ๋งํฌ ์์ (์ฑํฐ ์ถ๊ตฌ๊ฐ ์๋๋ฉด ๊ฒฐํจ)")
|
| 121 |
|
| 122 |
+
# ์ ์ ๋ฉํ ๋์ โ ์นด๋ID ์ฐธ์กฐ(E##-##)ยท๋ณผ๋ ๋ง์ปค ๊ฐ์ ํธ์ง ๋ฉ๋ชจ๊ฐ
|
| 123 |
+
# ์ง๋ฌธ/๊ฒฐ๊ณผ์ ๋จ์ผ๋ฉด ํ๋ฉด์ ๋
ธ์ถ๋๋ค (๋ณธ๋ฌธ ๋์ฌยท์ง๋ฌธ์ ์นด๋ID๊ฐ ๋ฑ์ฅํ ์ผ์ ์๋ค)
|
| 124 |
import re as _re
|
| 125 |
+
leak = _re.compile(r"\bE\d{2}-\d{2}[A-C]?\b|\*\*โท|\*\*E\d{2}-")
|
| 126 |
for c in cards.values():
|
| 127 |
for b in c.narration:
|
| 128 |
if leak.search(b.text):
|
engine/services/__pycache__/paging.cpython-314.pyc
CHANGED
|
Binary files a/engine/services/__pycache__/paging.cpython-314.pyc and b/engine/services/__pycache__/paging.cpython-314.pyc differ
|
|
|
engine/services/paging.py
CHANGED
|
@@ -31,6 +31,7 @@ class PageView:
|
|
| 31 |
first: Optional[BeatCard] = None # ํ์ด์ง ์ ๋ชฉยท์ํผ์๋ยท์ฐ์ถ ์ปท์ ๊ธฐ์ค
|
| 32 |
last: Optional[BeatCard] = None # ์ ํ์งยท์ฑํ
ยท์งํ ์์น(current_id)์ ๊ธฐ์ค
|
| 33 |
narration: list[NarrationBlock] = field(default_factory=list)
|
|
|
|
| 34 |
choices: list[Choice] = field(default_factory=list)
|
| 35 |
can_chat: bool = False
|
| 36 |
ending: Optional[EndingRule] = None
|
|
@@ -139,13 +140,16 @@ class Pager:
|
|
| 139 |
first = step.card
|
| 140 |
part = self.partitions[self.part_of[first.id]]
|
| 141 |
narration = list(step.narration)
|
|
|
|
| 142 |
while engine.current_id != part[-1]:
|
| 143 |
step = engine.advance()
|
| 144 |
if step.ending: # ํํฐ์
๋ด ๋งํฌ๋ ์ฑํฐ ์ โ ๋ฐฉ์ด์ ์ฒ๋ฆฌ
|
| 145 |
return PageView(ending=step.ending)
|
| 146 |
narration += step.narration
|
|
|
|
| 147 |
last = step.card
|
| 148 |
return PageView(first=first, last=last, narration=narration,
|
|
|
|
| 149 |
choices=list(last.choices), can_chat=last.can_chat)
|
| 150 |
|
| 151 |
# โโ ๋ฐํ์: ๋ณต์ ๋ ๋ (๋ถ์ํจ๊ณผ ์์) โโโโโโโโโโโโโโโโโโโ
|
|
@@ -155,6 +159,11 @@ class Pager:
|
|
| 155 |
return PageView(ending=engine.ending)
|
| 156 |
part = self.partitions[self.part_of[engine.current_id]]
|
| 157 |
cards = [self.repo.get_card(cid) for cid in part]
|
| 158 |
-
narration = [
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
return PageView(first=cards[0], last=cards[-1], narration=narration,
|
|
|
|
| 160 |
choices=list(cards[-1].choices), can_chat=cards[-1].can_chat)
|
|
|
|
| 31 |
first: Optional[BeatCard] = None # ํ์ด์ง ์ ๋ชฉยท์ํผ์๋ยท์ฐ์ถ ์ปท์ ๊ธฐ์ค
|
| 32 |
last: Optional[BeatCard] = None # ์ ํ์งยท์ฑํ
ยท์งํ ์์น(current_id)์ ๊ธฐ์ค
|
| 33 |
narration: list[NarrationBlock] = field(default_factory=list)
|
| 34 |
+
narration_cards: list[str] = field(default_factory=list) # ๋ธ๋ก๋ณ ์์ ์นด๋ (๋ณ๋ ฌ)
|
| 35 |
choices: list[Choice] = field(default_factory=list)
|
| 36 |
can_chat: bool = False
|
| 37 |
ending: Optional[EndingRule] = None
|
|
|
|
| 140 |
first = step.card
|
| 141 |
part = self.partitions[self.part_of[first.id]]
|
| 142 |
narration = list(step.narration)
|
| 143 |
+
cards = [first.id] * len(step.narration)
|
| 144 |
while engine.current_id != part[-1]:
|
| 145 |
step = engine.advance()
|
| 146 |
if step.ending: # ํํฐ์
๋ด ๋งํฌ๋ ์ฑํฐ ์ โ ๋ฐฉ์ด์ ์ฒ๋ฆฌ
|
| 147 |
return PageView(ending=step.ending)
|
| 148 |
narration += step.narration
|
| 149 |
+
cards += [step.card.id] * len(step.narration)
|
| 150 |
last = step.card
|
| 151 |
return PageView(first=first, last=last, narration=narration,
|
| 152 |
+
narration_cards=cards,
|
| 153 |
choices=list(last.choices), can_chat=last.can_chat)
|
| 154 |
|
| 155 |
# โโ ๋ฐํ์: ๋ณต์ ๋ ๋ (๋ถ์ํจ๊ณผ ์์) โโโโโโโโโโโโโโโโโโโ
|
|
|
|
| 159 |
return PageView(ending=engine.ending)
|
| 160 |
part = self.partitions[self.part_of[engine.current_id]]
|
| 161 |
cards = [self.repo.get_card(cid) for cid in part]
|
| 162 |
+
narration, ncards = [], []
|
| 163 |
+
for c in cards:
|
| 164 |
+
vis = engine.visible_narration(c)
|
| 165 |
+
narration += vis
|
| 166 |
+
ncards += [c.id] * len(vis)
|
| 167 |
return PageView(first=cards[0], last=cards[-1], narration=narration,
|
| 168 |
+
narration_cards=ncards,
|
| 169 |
choices=list(cards[-1].choices), can_chat=cards[-1].can_chat)
|
scripts/beatcard_convert.py
CHANGED
|
@@ -208,6 +208,11 @@ class EpisodeParser:
|
|
| 208 |
def close_result():
|
| 209 |
nonlocal cur_result
|
| 210 |
if cur_result:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 211 |
cur_result["text"] = cur_result["text"].strip()
|
| 212 |
results.append(cur_result)
|
| 213 |
cur_result = None
|
|
@@ -240,8 +245,9 @@ class EpisodeParser:
|
|
| 240 |
flush_prose()
|
| 241 |
close_result()
|
| 242 |
nm = RE_RESULT_NEXT.search(rest)
|
|
|
|
| 243 |
cur_result = {"axis": AXIS_MAP.get(mark), "hint": hint,
|
| 244 |
-
"text":
|
| 245 |
"next": nm.group(1) if nm else None}
|
| 246 |
continue
|
| 247 |
|
|
|
|
| 208 |
def close_result():
|
| 209 |
nonlocal cur_result
|
| 210 |
if cur_result:
|
| 211 |
+
# ์ฐ์ ์ค์ ์์ฌ ๋ ๋ผ์ฐํ
๋ฉ๋ชจ(โ E##-โฆ)๋ ์ ๋จ
|
| 212 |
+
m = RE_RESULT_NEXT.search(cur_result["text"])
|
| 213 |
+
if m:
|
| 214 |
+
cur_result["next"] = cur_result["next"] or m.group(1)
|
| 215 |
+
cur_result["text"] = cur_result["text"][:m.start()]
|
| 216 |
cur_result["text"] = cur_result["text"].strip()
|
| 217 |
results.append(cur_result)
|
| 218 |
cur_result = None
|
|
|
|
| 245 |
flush_prose()
|
| 246 |
close_result()
|
| 247 |
nm = RE_RESULT_NEXT.search(rest)
|
| 248 |
+
# ํ์ดํ๋ถํฐ๋ ๋ผ์ฐํ
๋ฉ๋ชจ("โ E06-10(๋ ์ ๋ฐ๊ฒฌ) ์ดํ โฆ") โ ๋ณธ๋ฌธ์์ ์ ๋จ
|
| 249 |
cur_result = {"axis": AXIS_MAP.get(mark), "hint": hint,
|
| 250 |
+
"text": (rest[:nm.start()] if nm else rest).strip(),
|
| 251 |
"next": nm.group(1) if nm else None}
|
| 252 |
continue
|
| 253 |
|
web/index.html
CHANGED
|
@@ -354,19 +354,27 @@ function speechBox(name, lines, mode) { // ์ฌ๊ฐ ๋งํ์ : ์ข ํ์๋ช
ยท
|
|
| 354 |
return h + `</div></div>`;
|
| 355 |
}
|
| 356 |
|
| 357 |
-
function stageScreen(
|
| 358 |
return `<div class="vn-stage">` +
|
| 359 |
-
`<img class="vn-stage-art" src="${encodeURI(
|
| 360 |
`<div class="vn-stage-panel">${boxHtml}` +
|
| 361 |
`<div id="choice-area">${choices}</div></div></div>`;
|
| 362 |
}
|
| 363 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 364 |
function groupNarration(blocks) {
|
| 365 |
-
// ์ฐ์๋ ๊ฐ์ ํ์์ ๋ฐํ๋ ํ ๊ทธ๋ฃน์ผ๋ก
|
| 366 |
const groups = [];
|
| 367 |
for (let i = 0; i < blocks.length;) {
|
| 368 |
const b = blocks[i];
|
| 369 |
-
if (b.type !== 'character_dialogue') { groups.push({block: b}); i++; continue; }
|
| 370 |
const spk = b.speaker, lines = [];
|
| 371 |
let portrait = null;
|
| 372 |
while (i < blocks.length && blocks[i].type === 'character_dialogue'
|
|
@@ -375,40 +383,66 @@ function groupNarration(blocks) {
|
|
| 375 |
portrait = portrait || blocks[i].portrait;
|
| 376 |
i++;
|
| 377 |
}
|
| 378 |
-
groups.push({spk, portrait, lines});
|
| 379 |
}
|
| 380 |
return groups;
|
| 381 |
}
|
| 382 |
|
| 383 |
-
|
| 384 |
-
- ํ์ดํ์ ์ํผ์๋ ์ฒซ ํ์ด์ง์ ํ(่ฉฑ) ํค๋๋ง โ ์นด๋ ์์ ๋ชฉ์ ํ์ํ์ง ์๋๋ค (์ฑ
์ ํ๋ฆ)
|
| 385 |
-
- ์ด์ ์๋ ํ์์ ๋ฐํ๋ ์ ๋ถ ์ ๋ฉด ์คํ
์ด์ง(์ด๋ฏธ์ง + ์ฌ๊ฐ ๋งํ์ ) ๋จ๋
ํ๋ฉด
|
| 386 |
-
- ์ธํฐ๋์
ํ์ด์ง์ ๋ง์ง๋ง ์ด์ ๋ฐํ๋ถํฐ๋ "๋งํ์ ๋จ๊ณ ์ํ์ค" โ ๊ฐ์ ์ด๋ฏธ์ง ์์์
|
| 387 |
-
๋์ฌ โ (์ดํ ์ง๋ฌธยท๋์ฌ) โ ๋ง์ง๋ง ๋จ๊ณ์ ์ ํ์ง. ์๋ฌธ ์์ ๋ณด์กด + ๋ฐํยท์ ํ์ง ์ธ์
|
| 388 |
-
- ๋๋จธ์ง ์ง๋ฌธยท๋ฐํ๋ ์ธ๊ทธ๋จผํธ๋ณ ์ค์ธก ๊ท ํ ๋ถํ ๋ก ํ๋ฉด์ ์ต๋ํ ์ฑ์ด๋ค */
|
| 389 |
-
function buildScreens(step, fresh) {
|
| 390 |
const c = step.card;
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
head += `<div class="ep-header fade">${esc(c.episode_title || c.episode)}</div>`;
|
| 394 |
lastEpisode = c.episode;
|
|
|
|
|
|
|
| 395 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 396 |
const groups = groupNarration(step.narration);
|
| 397 |
const choices = choicesHtml(step);
|
| 398 |
-
const
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 403 |
}
|
| 404 |
|
|
|
|
| 405 |
const meas = $('page-measure');
|
| 406 |
-
const budget = Math.max((window.innerHeight || 900) - 130, 320);
|
| 407 |
const measure = (html) => { meas.innerHTML = html; return meas.offsetHeight; };
|
| 408 |
-
|
| 409 |
const screens = [];
|
| 410 |
-
|
| 411 |
-
const flushSeg = () => { // ์ธ๊ทธ๋จผํธ๋ฅผ ๊ท ํ ๋ถํ ํด ํ๋ฉด์ผ๋ก
|
| 412 |
if (!seg.length) return;
|
| 413 |
const total = seg.reduce((s, u) => s + u.h, 0);
|
| 414 |
const k = Math.max(1, Math.ceil(total / budget));
|
|
@@ -428,50 +462,52 @@ function buildScreens(step, fresh) {
|
|
| 428 |
}
|
| 429 |
});
|
| 430 |
if (cur) screens.push(cur);
|
| 431 |
-
seg = [];
|
| 432 |
};
|
| 433 |
|
| 434 |
-
const
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
|
|
|
|
|
|
| 440 |
} else {
|
| 441 |
-
const
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
}
|
| 451 |
-
flushSeg();
|
| 452 |
-
} else { // ๋งํ์ ๋จ๊ณ ์ํ์ค (๊ฐ์ ์ด๋ฏธ์ง, ๋ฐ์ค ๋ด์ฉ๋ง ์ ํ)
|
| 453 |
-
flushSeg();
|
| 454 |
-
const sg = groups[seqIdx];
|
| 455 |
-
const steps = [{name: sg.spk, lines: sg.lines, mode: 'speech'}];
|
| 456 |
-
for (let k = seqIdx + 1; k < groups.length; k++) {
|
| 457 |
-
const g = groups[k];
|
| 458 |
-
if (g.lines)
|
| 459 |
-
steps.push({name: PLAYER_SPEAKERS.has(g.spk) ? '๋' : g.spk,
|
| 460 |
-
lines: g.lines, mode: 'speech'});
|
| 461 |
-
else if (g.block.type === 'dialogue_center')
|
| 462 |
-
steps.push({name: null, lines: [g.block.text], mode: 'center'});
|
| 463 |
-
else
|
| 464 |
-
steps.push({name: null, lines: [g.block.text], mode: 'narr'});
|
| 465 |
}
|
| 466 |
-
|
| 467 |
-
screens.push(stageScreen(sg, speechBox(s.name, s.lines, s.mode),
|
| 468 |
-
i === steps.length - 1 ? choices : ''));
|
| 469 |
-
});
|
| 470 |
-
}
|
| 471 |
meas.innerHTML = '';
|
| 472 |
|
| 473 |
-
if (screens.length) screens[0] = head + screens[0];
|
| 474 |
else screens.push(head);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 475 |
return screens;
|
| 476 |
}
|
| 477 |
|
|
@@ -493,21 +529,10 @@ function setProgress(p, epLabel) {
|
|
| 493 |
function showStep(step, fresh) {
|
| 494 |
STEP = step; pendingStep = null; pendingScreens = [];
|
| 495 |
if (step.ending) return renderEnding(step.ending);
|
| 496 |
-
|
| 497 |
-
|
| 498 |
-
|
| 499 |
-
|
| 500 |
-
}
|
| 501 |
-
const screens = buildScreens(step, fresh);
|
| 502 |
-
pushEntry(screens[0], step.progress, step.card.episode_title, step.background);
|
| 503 |
-
pendingScreens = screens.slice(1); // ๋๋จธ์ง๋ ํ์ด์ง ๋๊น์ผ๋ก
|
| 504 |
-
}
|
| 505 |
-
|
| 506 |
-
function pushCutPage(step) { // ์ด๋ฏธ์ง ํ์ด์ง ๋ฒํธ = ๋ณธ๋ฌธ ๋ฒํธ - 1 (์๋ฒ ๊ณ์ฝ)
|
| 507 |
-
const html = `<div class="page-cut"><img src="${encodeURI(step.image)}" alt="">` +
|
| 508 |
-
`<div class="cut-caption">${esc(step.card.title)}</div></div>`;
|
| 509 |
-
pushEntry(html, {index: step.progress.index - 1, total: step.progress.total},
|
| 510 |
-
step.card.episode_title, step.background);
|
| 511 |
}
|
| 512 |
|
| 513 |
function pushEntry(html, progress, ep, bg) {
|
|
@@ -559,8 +584,8 @@ async function go(dir) {
|
|
| 559 |
return;
|
| 560 |
}
|
| 561 |
if (pendingScreens.length) { // ๊ฐ์ ์๋ฒ ํ์ด์ง์ ๋ค์ ํ๋ฉด
|
| 562 |
-
|
| 563 |
-
|
| 564 |
return;
|
| 565 |
}
|
| 566 |
if (pendingStep) { // ์ ํ ๊ฒฐ๊ณผ ํ์ธ ํ ๋ค์ ์ฅ
|
|
|
|
| 354 |
return h + `</div></div>`;
|
| 355 |
}
|
| 356 |
|
| 357 |
+
function stageScreen(art, alt, boxHtml, choices) { // ์ ๋ฉด ์ด๋ฏธ์ง + ๋งํ์ (+์ ํ์ง)
|
| 358 |
return `<div class="vn-stage">` +
|
| 359 |
+
`<img class="vn-stage-art" src="${encodeURI(art)}" alt="${esc(alt)}">` +
|
| 360 |
`<div class="vn-stage-panel">${boxHtml}` +
|
| 361 |
`<div id="choice-area">${choices}</div></div></div>`;
|
| 362 |
}
|
| 363 |
|
| 364 |
+
function groupToStep(g) { // ์คํ
์ด์ง ํ์น ๊ทธ๋ฃน โ ๋งํ์ ๋จ๊ณ
|
| 365 |
+
if (g.lines)
|
| 366 |
+
return {mode: 'speech', name: PLAYER_SPEAKERS.has(g.spk) ? '๋' : g.spk, lines: g.lines};
|
| 367 |
+
if (g.block.type === 'dialogue_center')
|
| 368 |
+
return {mode: 'center', name: null, lines: [g.block.text]};
|
| 369 |
+
return {mode: 'narr', name: null, lines: [g.block.text]};
|
| 370 |
+
}
|
| 371 |
+
|
| 372 |
function groupNarration(blocks) {
|
| 373 |
+
// ์ฐ์๋ ๊ฐ์ ํ์์ ๋ฐํ๋ ํ ๊ทธ๋ฃน์ผ๋ก. card = ์์ ์นด๋ (์คํ
์ด์ง ํ์น ๊ฒฝ๊ณ)
|
| 374 |
const groups = [];
|
| 375 |
for (let i = 0; i < blocks.length;) {
|
| 376 |
const b = blocks[i];
|
| 377 |
+
if (b.type !== 'character_dialogue') { groups.push({card: b.card, block: b}); i++; continue; }
|
| 378 |
const spk = b.speaker, lines = [];
|
| 379 |
let portrait = null;
|
| 380 |
while (i < blocks.length && blocks[i].type === 'character_dialogue'
|
|
|
|
| 383 |
portrait = portrait || blocks[i].portrait;
|
| 384 |
i++;
|
| 385 |
}
|
| 386 |
+
groups.push({card: b.card, spk, portrait, lines});
|
| 387 |
}
|
| 388 |
return groups;
|
| 389 |
}
|
| 390 |
|
| 391 |
+
function epHeader(step, fresh) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 392 |
const c = step.card;
|
| 393 |
+
const h = (fresh || c.episode !== lastEpisode)
|
| 394 |
+
? `<div class="ep-header fade">${esc(c.episode_title || c.episode)}</div>` : '';
|
|
|
|
| 395 |
lastEpisode = c.episode;
|
| 396 |
+
return h;
|
| 397 |
+
}
|
| 398 |
|
| 399 |
+
/* ์๋ฒ ํ์ด์ง โ ํ๋ฉด๋ค {html, prog} (์คํฌ๋กค ์๋ ๋๊น).
|
| 400 |
+
- ์ด์ ํ์(์นด๋ฅด๋ฐ๋ผยท์๋ฒ์ง)์ ๋ฐํ = ์ ๋ฉด ์คํ
์ด์ง. ๊ฐ์ ์นด๋ ์์ ํ์ ์ง๋ฌธยท
|
| 401 |
+
๋ฌด์ด์ ๋ฐํยท์ค์ ๋์ฌ๋ ๊ทธ ์ด๋ฏธ์ง ์ ๋งํ์ ๋จ๊ณ๋ก ํ์น (์๋ฌธ ์์ ๋ณด์กด)
|
| 402 |
+
- ์ง์ ์ ์งง์ ๋ฆฌ๋์ธ(role=lead_in, ๊ฐ์ ์นด๋)์ ์คํ
์ด์ง์ ์ฒซ ๋จ๊ณ(์ง๋ฌธ ๋ฐ์ค)๋ก
|
| 403 |
+
- ๋ง์ง๋ง ์ฅ๋ฉด์ด ์คํ
์ด์ง๋ฉด ์ ํ์ง๋ ์ต์ข
๋จ๊ณ์, ์๋๋ฉด ํ
์คํธ ํ๋ฉด ํ๋จ์
|
| 404 |
+
- ํ
์คํธ๋ ์ธ๊ทธ๋จผํธ๋ณ ์ค์ธก ๊ท ํ ๋ถํ ๋ก ํ๋ฉด์ ์ต๋ํ ์ฑ์ด๋ค */
|
| 405 |
+
function buildScreens(step, fresh) {
|
| 406 |
+
const head = epHeader(step, fresh);
|
| 407 |
const groups = groupNarration(step.narration);
|
| 408 |
const choices = choicesHtml(step);
|
| 409 |
+
const isP = (g) => g.lines && g.portrait && !PLAYER_SPEAKERS.has(g.spk);
|
| 410 |
+
|
| 411 |
+
// 1) ์ฅ๋ฉด ์กฐ๋ฆฝ: text(๊ทธ๋ฃน ๋์ด) | stage(์ด์ + ๋งํ์ ๋จ๊ณ๋ค)
|
| 412 |
+
const scenes = [];
|
| 413 |
+
for (let i = 0; i < groups.length;) {
|
| 414 |
+
const g = groups[i];
|
| 415 |
+
if (!isP(g)) {
|
| 416 |
+
let last = scenes[scenes.length - 1];
|
| 417 |
+
if (!last || last.type !== 'text') { last = {type: 'text', groups: []}; scenes.push(last); }
|
| 418 |
+
last.groups.push(g); i++;
|
| 419 |
+
continue;
|
| 420 |
+
}
|
| 421 |
+
const steps = [];
|
| 422 |
+
const prev = scenes[scenes.length - 1]; // ๋ฆฌ๋์ธ โ ์ฒซ ๋จ๊ณ๋ก ์ด๋
|
| 423 |
+
if (prev && prev.type === 'text' && prev.groups.length) {
|
| 424 |
+
const lg = prev.groups[prev.groups.length - 1];
|
| 425 |
+
if (lg.block && lg.block.role === 'lead_in' && lg.card === g.card) {
|
| 426 |
+
prev.groups.pop();
|
| 427 |
+
if (!prev.groups.length) scenes.pop();
|
| 428 |
+
steps.push({mode: 'narr', name: null, lines: [lg.block.text]});
|
| 429 |
+
}
|
| 430 |
+
}
|
| 431 |
+
steps.push({mode: 'speech', name: g.spk, lines: g.lines});
|
| 432 |
+
i++;
|
| 433 |
+
while (i < groups.length && !isP(groups[i]) && groups[i].card === g.card) {
|
| 434 |
+
steps.push(groupToStep(groups[i])); // ๊ฐ์ ์นด๋ โ ์ด๋ฏธ์ง์ ํ์น
|
| 435 |
+
i++;
|
| 436 |
+
}
|
| 437 |
+
scenes.push({type: 'stage', g, steps});
|
| 438 |
}
|
| 439 |
|
| 440 |
+
// 2) ๋ ๋: ํ
์คํธ ๊ท ํ ๋ถํ + ์คํ
์ด์ง ๋จ๊ณ ํ๋ฉด
|
| 441 |
const meas = $('page-measure');
|
| 442 |
+
const budget = Math.max((window.innerHeight || 900) - 130, 320);
|
| 443 |
const measure = (html) => { meas.innerHTML = html; return meas.offsetHeight; };
|
|
|
|
| 444 |
const screens = [];
|
| 445 |
+
const flushSeg = (seg) => {
|
|
|
|
| 446 |
if (!seg.length) return;
|
| 447 |
const total = seg.reduce((s, u) => s + u.h, 0);
|
| 448 |
const k = Math.max(1, Math.ceil(total / budget));
|
|
|
|
| 462 |
}
|
| 463 |
});
|
| 464 |
if (cur) screens.push(cur);
|
|
|
|
| 465 |
};
|
| 466 |
|
| 467 |
+
const lastScene = scenes[scenes.length - 1];
|
| 468 |
+
scenes.forEach((sc) => {
|
| 469 |
+
if (sc.type === 'stage') {
|
| 470 |
+
sc.steps.forEach((s, si) => {
|
| 471 |
+
const withChoices = (sc === lastScene && si === sc.steps.length - 1) ? choices : '';
|
| 472 |
+
screens.push(stageScreen(sc.g.portrait, sc.g.spk,
|
| 473 |
+
speechBox(s.name, s.lines, s.mode), withChoices));
|
| 474 |
+
});
|
| 475 |
} else {
|
| 476 |
+
const seg = sc.groups.map((g) => {
|
| 477 |
+
const html = g.lines ? charLines(g) : block(g.block);
|
| 478 |
+
return {html, h: measure(html)};
|
| 479 |
+
});
|
| 480 |
+
if (sc === lastScene && choices) {
|
| 481 |
+
const area = `<div id="choice-area">${choices}</div>`;
|
| 482 |
+
seg.push({html: area, h: measure(area)});
|
| 483 |
+
}
|
| 484 |
+
flushSeg(seg);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 485 |
}
|
| 486 |
+
});
|
|
|
|
|
|
|
|
|
|
|
|
|
| 487 |
meas.innerHTML = '';
|
| 488 |
|
| 489 |
+
if (screens.length) screens[0] = head + screens[0];
|
| 490 |
else screens.push(head);
|
| 491 |
+
return screens.map((html) => ({html, prog: step.progress}));
|
| 492 |
+
}
|
| 493 |
+
|
| 494 |
+
/* ์ฐ์ถ ์ปท(์ด๋ฏธ์ง ์นด๋) โ ์บ๋ฒ์ค ํ๋ฉด๋ค: ์บก์
์ปท โ ์ง๋ฌธยท๋ฐํ ๋งํ์ ๋จ๊ณ โ ์ ํ์ง.
|
| 495 |
+
์ปท ํ์ด์ง ๋ฒํธ = ๋ณธ๋ฌธ ๋ฒํธ - 1 (๊ธฐ์กด ๊ณ์ฝ ์ ์ง) */
|
| 496 |
+
function buildCutScreens(step, fresh) {
|
| 497 |
+
const head = epHeader(step, fresh);
|
| 498 |
+
const cut = `<div class="page-cut"><img src="${encodeURI(step.image)}" alt="">` +
|
| 499 |
+
`<div class="cut-caption">${esc(step.card.title)}</div></div>`;
|
| 500 |
+
const screens = [{html: head + cut,
|
| 501 |
+
prog: {index: step.progress.index - 1, total: step.progress.total}}];
|
| 502 |
+
const steps = groupNarration(step.narration).map(groupToStep);
|
| 503 |
+
const choices = choicesHtml(step);
|
| 504 |
+
if (!steps.length && choices) steps.push({mode: 'narr', name: null, lines: []});
|
| 505 |
+
steps.forEach((s, i) => {
|
| 506 |
+
const box = s.lines.length ? speechBox(s.name, s.lines, s.mode) : '';
|
| 507 |
+
screens.push({html: stageScreen(step.image, step.card.title, box,
|
| 508 |
+
i === steps.length - 1 ? choices : ''),
|
| 509 |
+
prog: step.progress});
|
| 510 |
+
});
|
| 511 |
return screens;
|
| 512 |
}
|
| 513 |
|
|
|
|
| 529 |
function showStep(step, fresh) {
|
| 530 |
STEP = step; pendingStep = null; pendingScreens = [];
|
| 531 |
if (step.ending) return renderEnding(step.ending);
|
| 532 |
+
const screens = step.image ? buildCutScreens(step, fresh) : buildScreens(step, fresh);
|
| 533 |
+
const first = screens.shift();
|
| 534 |
+
pushEntry(first.html, first.prog, step.card.episode_title, step.background);
|
| 535 |
+
pendingScreens = screens; // ๋๋จธ์ง๋ ํ์ด์ง ๋๊น์ผ๋ก
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 536 |
}
|
| 537 |
|
| 538 |
function pushEntry(html, progress, ep, bg) {
|
|
|
|
| 584 |
return;
|
| 585 |
}
|
| 586 |
if (pendingScreens.length) { // ๊ฐ์ ์๋ฒ ํ์ด์ง์ ๋ค์ ํ๋ฉด
|
| 587 |
+
const s = pendingScreens.shift();
|
| 588 |
+
pushEntry(s.html, s.prog, STEP.card.episode_title, STEP.background);
|
| 589 |
return;
|
| 590 |
}
|
| 591 |
if (pendingStep) { // ์ ํ ๊ฒฐ๊ณผ ํ์ธ ํ ๋ค์ ์ฅ
|