"""
RPG Maker MV/MZ Tab - Configuration for RPG Maker specific settings
"""
import json
from pathlib import Path
from PyQt5.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QFormLayout, QCheckBox,
QPushButton, QGroupBox, QLabel, QMessageBox, QScrollArea,
QTextEdit, QSpinBox, QFrame, QGridLayout
)
from PyQt5.QtCore import Qt, pyqtSignal
try:
from .config_integration import ConfigIntegration
except ImportError:
# Fallback if config_integration is not available
ConfigIntegration = None
try:
# Prefer importing the project's canonical defaults if available
from util.defaults import DEFAULTS as CANONICAL_DEFAULTS
except Exception:
CANONICAL_DEFAULTS = None
def create_section_label(text):
"""Create a section label for grouping settings."""
from gui.qt_icons import make_section_header
return make_section_header(
text,
"QLabel {"
"font-size: 13px;"
"font-weight: bold;"
"color: #007acc;"
"padding: 6px 0px 4px 0px;"
"background-color: transparent;"
"}",
)
class RPGMakerTab(QWidget):
"""RPG Maker MV/MZ configuration tab."""
# Default configuration values for RPG Maker MV/MZ
DEFAULT_CONFIG = {
# General settings
"FIRSTLINESPEAKERS": False,
"INLINE401SPEAKERS": False,
"FACENAME101": False,
"BRFLAG": False,
"FIXTEXTWRAP": True,
"IGNORETLTEXT": False,
"TLSYSTEMVARIABLES": False,
"TLSYSTEMSWITCHES": False,
# Main Codes (enabled by default)
"CODE401": True, # Show Text
"CODE405": True, # Show Text (line 4+)
"CODE102": True, # Show Choices
# Optional codes (disabled by default)
"CODE101": False, # Show Text (face)
"CODE408": False, # Show Text (line continuation)
# Variable codes (disabled by default)
"CODE122": False, # Control Variables
"CODE122_VAR_MIN": 0, # Minimum variable ID to translate
"CODE122_VAR_MAX": 2000, # Maximum variable ID to translate
# Other codes (all disabled by default)
"CODE103": False, # Input Number
"CODE104": False, # Select Item
"CODE111": False, # Conditional Branch
"CODE117": False, # Common Event
"CODE119": False, # Set Movement Route
"CODE121": False, # Control Switches
"CODE125": False, # Change Gold
"CODE126": False, # Change Items
"CODE127": False, # Change Weapons
"CODE128": False, # Change Armors
"CODE129": False, # Change Party Member
"CODE132": False, # Change Battle BGM
"CODE133": False, # Change Victory ME
"CODE134": False, # Change Save Access
"CODE135": False, # Change Menu Access
"CODE136": False, # Change Encounter
"CODE137": False, # Change Formation Access
"CODE138": False, # Change Window Color
"CODE139": False, # Change Defeat ME
"CODE140": False, # Change Vehicle BGM
"CODE201": False, # Transfer Player
"CODE202": False, # Set Vehicle Location
"CODE203": False, # Set Event Location
"CODE204": False, # Scroll Map
"CODE205": False, # Set Move Route
"CODE206": False, # Get On/Off Vehicle
"CODE211": False, # Change Transparency
"CODE212": False, # Show Animation
"CODE213": False, # Show Balloon Icon
"CODE214": False, # Erase Event
"CODE216": False, # Change Player Followers
"CODE217": False, # Gather Followers
"CODE221": False, # Fadeout Screen
"CODE222": False, # Fadein Screen
"CODE223": False, # Tint Screen
"CODE224": False, # Flash Screen
"CODE225": False, # Shake Screen
"CODE230": False, # Wait
"CODE231": False, # Show Picture
"CODE232": False, # Move Picture
"CODE233": False, # Rotate Picture
"CODE234": False, # Tint Picture
"CODE235": False, # Erase Picture
"CODE236": False, # Set Weather Effect
"CODE241": False, # Play BGM
"CODE242": False, # Fadeout BGM
"CODE243": False, # Save BGM
"CODE244": False, # Resume BGM
"CODE245": False, # Play BGS
"CODE246": False, # Fadeout BGS
"CODE249": False, # Play ME
"CODE250": False, # Play SE
"CODE251": False, # Stop SE
"CODE261": False, # Show Movie
"CODE281": False, # Change Map Name Display
"CODE282": False, # Change Tileset
"CODE283": False, # Change Battle Background
"CODE284": False, # Change Parallax
"CODE285": False, # Get Location Info
"CODE301": False, # Battle Processing
"CODE302": False, # Shop Processing
"CODE303": False, # Name Input Processing
"CODE311": False, # Change HP
"CODE312": False, # Change MP
"CODE313": False, # Change State
"CODE314": False, # Recover All
"CODE315": False, # Change EXP
"CODE316": False, # Change Level
"CODE317": False, # Change Parameters
"CODE318": False, # Change Skills
"CODE319": False, # Change Equipment
"CODE320": False, # Change Name
"CODE321": False, # Change Class
"CODE322": False, # Change Actor Graphic
"CODE323": False, # Change Vehicle Graphic
"CODE324": False, # Change Nickname
"CODE325": False, # Change Profile
"CODE326": False, # Change TP
"CODE331": False, # Change Enemy HP
"CODE332": False, # Change Enemy MP
"CODE333": False, # Change Enemy State
"CODE334": False, # Enemy Recover All
"CODE335": False, # Enemy Appear
"CODE336": False, # Enemy Transform
"CODE337": False, # Show Battle Animation
"CODE339": False, # Force Action
"CODE340": False, # Abort Battle
"CODE351": False, # Open Menu Screen
"CODE352": False, # Open Save Screen
"CODE353": False, # Game Over
"CODE354": False, # Return to Title Screen
"CODE355": False, # Script
"CODE356": False, # Plugin Command
}
config_changed = pyqtSignal()
def __init__(self, engine: str = "MVMZ"):
super().__init__()
self.engine = engine.upper()
self.config_integration = ConfigIntegration() if ConfigIntegration else None
self.init_ui()
# Load configuration from the module when available. We must avoid
# auto-applying/writing to the module at startup. Therefore:
# - Set the UI state from the module config if present
# - Otherwise fall back to DEFAULT_CONFIG
# - Only after the UI is initialized and set, connect auto-apply
try:
# Ensure signals are not connected yet (safe no-op if not)
self.disconnect_auto_apply()
loaded_config = None
if self.config_integration:
module_path = Path("modules") / "rpgmakermvmz.py"
loaded_config = self.config_integration.read_current_config(module_path)
# Use loaded module config if available, otherwise use canonical defaults
if loaded_config:
self.set_config(loaded_config)
else:
# Prefer project-level canonical defaults when present
defaults = CANONICAL_DEFAULTS if CANONICAL_DEFAULTS is not None else self.DEFAULT_CONFIG
# Ensure boolean values (set_config expects booleans)
self.set_config(defaults)
except Exception:
# On any problem while reading module config, fall back to canonical/defaults
defaults = CANONICAL_DEFAULTS if CANONICAL_DEFAULTS is not None else self.DEFAULT_CONFIG
self.set_config(defaults)
# Now connect auto-apply so user changes will update the module
self.connect_auto_apply()
def _create_checkbox_with_description(self, label_text, description_text, tooltip_text=None):
"""Create a checkbox with description on the same line."""
container = QHBoxLayout()
container.setSpacing(8)
container.setContentsMargins(0, 2, 0, 2)
checkbox = QCheckBox(label_text)
checkbox.setStyleSheet("QCheckBox { font-size: 11px; }")
checkbox.setMinimumWidth(175)
checkbox.setMaximumWidth(180)
if tooltip_text:
checkbox.setToolTip(tooltip_text)
container.addWidget(checkbox)
if description_text:
desc_label = QLabel(f"— {description_text}")
desc_label.setStyleSheet("color: #888888; font-size: 10px;")
desc_label.setWordWrap(False)
container.addWidget(desc_label, 1)
return checkbox, container
def init_ui(self):
"""Initialize the user interface with three-column layout."""
main_layout = QVBoxLayout()
main_layout.setContentsMargins(15, 15, 15, 15)
main_layout.setSpacing(12)
# Title
title_label = QLabel("RPG Maker MV/MZ Translation Settings")
title_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #007acc;")
main_layout.addWidget(title_label)
# Three-column layout
columns_layout = QHBoxLayout()
columns_layout.setSpacing(30)
# ==================== COLUMN 1: PROCESSING ====================
col1 = QVBoxLayout()
col1.setSpacing(4)
col1.addWidget(create_section_label("⚙️ Processing Options"))
self.first_line_speakers_cb, layout = self._create_checkbox_with_description(
"First Line = Speaker", "Treats first line as speaker name", "First line of text is treated as speaker."
)
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."
)
col1.addLayout(layout)
self.brflag_cb, layout = self._create_checkbox_with_description(
"
Line Breaks", "Use br instead of newlines", "For games using
instead of \\n."
)
col1.addLayout(layout)
self.fixtextwrap_cb, layout = self._create_checkbox_with_description(
"Dazed Text Wrap", "Re-wrap text to fit dialogue box", "Re-wraps translated text based on width settings."
)
col1.addLayout(layout)
self.ignoretltext_cb, layout = self._create_checkbox_with_description(
"Skip Translated", "Skip lines without Japanese text", "Skips already translated content."
)
col1.addLayout(layout)
self.tlsystemvariables_cb, layout = self._create_checkbox_with_description(
"System Variables", "Translate variable names in System.json", "Translates the variables array in System.json. Can break stuff."
)
col1.addLayout(layout)
self.tlsystemswitches_cb, layout = self._create_checkbox_with_description(
"System Switches", "Translate switch names in System.json", "Translates the switches array in System.json."
)
col1.addLayout(layout)
self.join408_cb, layout = self._create_checkbox_with_description(
"Merge 408 Lines", "Join multi-line comments together", "Joins CODE408 lines into one string."
)
col1.addLayout(layout)
col1.addStretch()
# ==================== COLUMN 2: DIALOGUE ====================
col2 = QVBoxLayout()
col2.setSpacing(4)
col2.addWidget(create_section_label("💬 Dialogue Content"))
self.code401_cb, layout = self._create_checkbox_with_description(
"Show Text (401)", "Standard dialogue text boxes", "Translates standard message window text."
)
col2.addLayout(layout)
self.code101_cb, layout = self._create_checkbox_with_description(
"Speakers (101)", "Speaker name field in Show Text", "Translates speaker names from Show Text header."
)
col2.addLayout(layout)
self.code405_cb, layout = self._create_checkbox_with_description(
"Scrolling Text (405)", "Credits, intros, scroll text", "Text that scrolls across the screen."
)
col2.addLayout(layout)
self.code102_cb, layout = self._create_checkbox_with_description(
"Choices (102)", "Player choice menus (Yes/No)", "Player choice selection menus."
)
col2.addLayout(layout)
col2.addSpacing(15)
# Extended Content in column 2
col2.addWidget(create_section_label("📝 Extended Content"))
self.code408_cb, layout = self._create_checkbox_with_description(
"Comments (408)", "⚠️ Plugin text in comments (costly!)", "Plugin dialogue in comment text."
)
self.code408_cb.setStyleSheet("QCheckBox { font-size: 11px; color: #ffaa66; }")
col2.addLayout(layout)
self.code122_cb, layout = self._create_checkbox_with_description(
"Variables (122)", "Text stored in game variables", "String values in game variables."
)
col2.addLayout(layout)
# Variable Range
var_layout = QHBoxLayout()
var_layout.setContentsMargins(20, 4, 0, 0)
var_layout.setSpacing(6)
lbl = QLabel("Variables ID Range:")
lbl.setStyleSheet("color: #888; font-size: 10px;")
var_layout.addWidget(lbl)
from PyQt5.QtWidgets import QLineEdit
from PyQt5.QtGui import QIntValidator
self.code122_var_min_spin = QLineEdit()
self.code122_var_min_spin.setValidator(QIntValidator(0, 99999))
self.code122_var_min_spin.setText("0")
self.code122_var_min_spin.setFixedWidth(60)
self.code122_var_min_spin.setAlignment(Qt.AlignCenter)
self.code122_var_min_spin.setStyleSheet("QLineEdit { padding: 4px; font-size: 11px; }")
var_layout.addWidget(self.code122_var_min_spin)
dash_lbl = QLabel("-")
dash_lbl.setStyleSheet("font-size: 11px;")
var_layout.addWidget(dash_lbl)
self.code122_var_max_spin = QLineEdit()
self.code122_var_max_spin.setValidator(QIntValidator(1, 99999))
self.code122_var_max_spin.setText("2000")
self.code122_var_max_spin.setFixedWidth(60)
self.code122_var_max_spin.setAlignment(Qt.AlignCenter)
self.code122_var_max_spin.setStyleSheet("QLineEdit { padding: 4px; font-size: 11px; }")
var_layout.addWidget(self.code122_var_max_spin)
var_layout.addStretch()
col2.addLayout(var_layout)
col2.addStretch()
# ==================== COLUMN 3: ACTORS & SCRIPTS ====================
col3 = QVBoxLayout()
col3.setSpacing(4)
col3.addWidget(create_section_label("👤 Actor Changes"))
self.code320_cb, layout = self._create_checkbox_with_description(
"Change Name (320)", "Actor name change commands", "Dynamic character name changes."
)
col3.addLayout(layout)
self.code324_cb, layout = self._create_checkbox_with_description(
"Nickname (324)", "Actor nickname/title changes", "Character nickname changes."
)
col3.addLayout(layout)
self.code325_cb, layout = self._create_checkbox_with_description(
"Profile (325)", "Actor profile/biography text", "Profile/biography text."
)
col3.addLayout(layout)
col3.addSpacing(15)
col3.addWidget(create_section_label("🔧 Scripts & Plugins"))
self.code355655_cb, layout = self._create_checkbox_with_description(
"Scripts (355/655)", "Text in inline JavaScript calls", "Text within script commands."
)
col3.addLayout(layout)
self.code356_cb, layout = self._create_checkbox_with_description(
"Plugin MV (356)", "MV plugin command parameters", "MV-style plugin parameters."
)
col3.addLayout(layout)
self.code357_cb, layout = self._create_checkbox_with_description(
"Plugin MZ (357)", "MZ plugin command parameters", "MZ-style plugin parameters."
)
col3.addLayout(layout)
self.code657_cb, layout = self._create_checkbox_with_description(
"Plugin Ext (657)", "Extended multi-line plugin data", "Extended plugin command lines."
)
col3.addLayout(layout)
self.code111_cb, layout = self._create_checkbox_with_description(
"Conditionals (111)", "Text in conditional branch checks", "Text in conditional branches."
)
col3.addLayout(layout)
self.code108_cb, layout = self._create_checkbox_with_description(
"Comments (108)", "Event comment blocks (plugins)", "Comment blocks for plugins."
)
col3.addLayout(layout)
col3.addStretch()
# Add all three columns
columns_layout.addLayout(col1, 1)
columns_layout.addLayout(col2, 1)
columns_layout.addLayout(col3, 1)
main_layout.addLayout(columns_layout, 1)
# ==================== WORKFLOW GUIDE ====================
workflow_label = create_section_label("📋 Translation Workflow")
main_layout.addWidget(workflow_label)
workflow_text = QLabel(
"
| 1. | Parse Speakers → vocab.txt | " "6. | Replace any \\\\n[0-999] variables with actor names | |
| 2. | Identify speaker genders (use Copilot with map files) | " "7. | Translate Maps & CommonEvents | |
| 3. | Translate Actors.json, MapInfos.json | " "8. | Edit plugins for menus/text | |
| 4. | Translate Items, System, Weapons, etc. | " "9. | Translate CODE 122 vars, 356 plugins as needed | |
| 5. | Find speaker names (101, brackets, first lines) | " "10. | Playtest → OCR → Search → Fix → Repeat |