Inline speakers

This commit is contained in:
dazedanon 2026-03-08 17:40:57 -05:00
parent ae2ab15cdc
commit 0a8786add6
3 changed files with 36 additions and 2 deletions

View file

@ -109,7 +109,7 @@ class ConfigIntegration:
# Find configuration lines
config_patterns = [
r'^(FIRSTLINESPEAKERS|FACENAME101|NAMES|BRFLAG|FIXTEXTWRAP|IGNORETLTEXT)\s*=\s*(True|False)',
r'^(FIRSTLINESPEAKERS|INLINE401SPEAKERS|FACENAME101|NAMES|BRFLAG|FIXTEXTWRAP|IGNORETLTEXT)\s*=\s*(True|False)',
r'^(CODE\d+)\s*=\s*(True|False)'
]
@ -152,6 +152,7 @@ class ConfigIntegration:
return {
# General config
"FIRSTLINESPEAKERS": False,
"INLINE401SPEAKERS": False,
"FACENAME101": False,
"NAMES": False,
"BRFLAG": False,
@ -185,6 +186,7 @@ class ConfigIntegration:
"""Get descriptions for all configuration options."""
return {
"FIRSTLINESPEAKERS": "If 1st line of 401 is a speaker, set to True",
"INLINE401SPEAKERS": "Detect speaker from Name\u300cdialogue\u300d inline format in 401 lines",
"FACENAME101": "Find Speakers in 101 Codes based on Face Name",
"NAMES": "Output a list of all the character names found",
"BRFLAG": "If the game uses <br> instead of newlines",

View file

@ -50,6 +50,7 @@ class RPGMakerTab(QWidget):
DEFAULT_CONFIG = {
# General settings
"FIRSTLINESPEAKERS": False,
"INLINE401SPEAKERS": False,
"FACENAME101": False,
"BRFLAG": False,
"FIXTEXTWRAP": True,
@ -256,6 +257,11 @@ class RPGMakerTab(QWidget):
)
col1.addLayout(layout)
self.inline401speakers_cb, layout = self._create_checkbox_with_description(
"Inline 401 Speaker", "Detect Name「dialogue」 speaker format", "Extracts speaker from Name\u300cdialogue\u300d inline format in 401 lines."
)
col1.addLayout(layout)
self.facename101_cb, layout = self._create_checkbox_with_description(
"Face → Speaker", "Use face image as speaker name", "Uses face filenames to identify speakers."
)
@ -502,6 +508,7 @@ class RPGMakerTab(QWidget):
try:
# General settings checkboxes
self.first_line_speakers_cb.stateChanged.disconnect()
self.inline401speakers_cb.stateChanged.disconnect()
self.facename101_cb.stateChanged.disconnect()
self.brflag_cb.stateChanged.disconnect()
self.fixtextwrap_cb.stateChanged.disconnect()
@ -544,6 +551,7 @@ class RPGMakerTab(QWidget):
"""Connect all checkboxes to auto-apply changes when modified."""
# General settings checkboxes
self.first_line_speakers_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
self.inline401speakers_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
self.facename101_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
self.brflag_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
self.fixtextwrap_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
@ -584,6 +592,7 @@ class RPGMakerTab(QWidget):
config = {
# General settings
"FIRSTLINESPEAKERS": self.first_line_speakers_cb.isChecked(),
"INLINE401SPEAKERS": self.inline401speakers_cb.isChecked(),
"FACENAME101": self.facename101_cb.isChecked(),
"BRFLAG": self.brflag_cb.isChecked(),
"FIXTEXTWRAP": self.fixtextwrap_cb.isChecked(),
@ -628,6 +637,7 @@ class RPGMakerTab(QWidget):
"""Set configuration from dictionary."""
# General settings
self.first_line_speakers_cb.setChecked(config.get("FIRSTLINESPEAKERS", False))
self.inline401speakers_cb.setChecked(config.get("INLINE401SPEAKERS", False))
self.facename101_cb.setChecked(config.get("FACENAME101", False))
self.brflag_cb.setChecked(config.get("BRFLAG", False))
self.fixtextwrap_cb.setChecked(config.get("FIXTEXTWRAP", True))

View file

@ -73,6 +73,8 @@ LEAVE = False
# Config (Default)
# FIRSTLINESPEAKERS: Guess speaker from first line.
FIRSTLINESPEAKERS = False
# INLINE401SPEAKERS: Extract speaker from "Name「dialogue」" inline format on 401 lines.
INLINE401SPEAKERS = True
# FACENAME101: Map face name -> speaker.
FACENAME101 = False
# Face name -> speaker mapping for FACENAME101.
@ -1774,6 +1776,12 @@ def searchCodes(page, pbar, jobList, filename):
jaString,
)
# Inline Japanese quote speakers (e.g. "トルテ「dialogue」" or "エルミナ「first line...")
if len(speakerList) == 0 and INLINE401SPEAKERS:
inlineQuoteDetect = re.match(r"^([^\s「」。、\\\n]{1,20})「", jaString)
if inlineQuoteDetect:
speakerList = [inlineQuoteDetect.group(1).strip()]
# First Line Speakers
if len(speakerList) == 0 and FIRSTLINESPEAKERS is True:
# Test Speaker
@ -1811,7 +1819,9 @@ def searchCodes(page, pbar, jobList, filename):
if len(speakerList) != 0:
# Check if speaker+dialogue are on same line (【speaker】dialogue)
sameLineMatch = re.match(r"^\s*【([^】]+)】(.+)", jaString, re.DOTALL)
# Check if speaker+dialogue are on same line via Japanese quote (Name「dialogue)
inlineQuoteMatch = re.match(r"^([^\s「」。、\\\n]{1,20})「(.*)", jaString, re.DOTALL) if INLINE401SPEAKERS else None
if sameLineMatch and len(speakerList) == 1:
# Translate speaker
response = getSpeaker(speakerList[0])
@ -1824,6 +1834,18 @@ def searchCodes(page, pbar, jobList, filename):
if not setData:
nametag = f"{speaker}" + nametag
# Don't skip to next line - continue with current line
elif inlineQuoteMatch and len(speakerList) == 1:
# Inline quote format: strip "Name「" prefix entirely, keep raw dialogue
response = getSpeaker(speakerList[0])
speaker = response[0]
totalTokens[0] += response[1][0]
totalTokens[1] += response[1][1]
# Remove speaker name and opening 「 from jaString; strip trailing 」 too
jaString = re.sub(r"\s*$", "", inlineQuoteMatch.group(2))
# Format as [Speaker]: for output
if not setData:
nametag = f"[{speaker}]: " + nametag
# Don't skip to next line - continue with current line
elif codeList[i + 1]["code"] in [401, 405, -1]:
# Original behavior: speaker on its own line, dialogue on next line
# Single