diff --git a/gui/workflow_tab.py b/gui/workflow_tab.py index 57d6480..8daa3fb 100644 --- a/gui/workflow_tab.py +++ b/gui/workflow_tab.py @@ -51,6 +51,27 @@ from PyQt5.QtWidgets import ( # Phase profiles applied to rpgmakermvmz.py before each translation run # --------------------------------------------------------------------------- +# Core database files — translated first (names/descriptions) +_DB_FILES = { + "Actors.json", "Armors.json", "Classes.json", "Enemies.json", + "Items.json", "MapInfos.json", "Skills.json", "States.json", + "System.json", "Weapons.json", +} + +# Event files — translated in phases 1 / 1b / 2 +_EVENT_FILES_EXACT = {"CommonEvents.json", "Troops.json"} +# Any Map????.json is also an event file (matched by prefix below) + +PHASE0_CONFIG = { + # All event codes OFF — DB files use top-level name/description fields + "CODE101": False, "CODE401": False, "CODE405": False, + "CODE102": False, "CODE408": False, + "CODE111": False, "CODE122": False, "CODE357": False, + "CODE355655": False, "CODE657": False, "CODE356": False, + "CODE320": False, "CODE324": False, "CODE325": False, + "CODE108": False, +} + PHASE1_CONFIG = { # Safe dialogue / choices "CODE101": True, @@ -702,173 +723,168 @@ class WorkflowTab(QWidget): _WRAP_PROMPT = ( "You are helping me configure text-wrap widths for a Japanese RPGMaker MV/MZ translation tool.\n" "\n" - "The tool wraps translated English text using a character-count limit — NOT pixels — so\n" - "I need to know how many English characters fit comfortably on one line for three\n" - "different window types:\n" + "The tool wraps translated English text using a character-count limit (not pixels).\n" + "I need three values:\n" "\n" - " width — main dialogue / message box (Show Text / code 401)\n" + " width — main dialogue / message box (Show Text)\n" " listWidth — item / skill / help description windows\n" " noteWidth — database note fields (item, weapon, armour, skill descriptions)\n" "\n" - "## Step 1 — Gather screen and font settings\n" + "To calculate them:\n" + " 1. Read screenWidth and fontSize from System.json.\n" + " Check js/plugins.js for any MessageCore or Window plugin that overrides these.\n" + " 2. For each window type, estimate its pixel width, subtract ~48px padding, then:\n" + " chars = floor(content_px / (font_size × 0.58))\n" + " listWidth window is usually full or half screen width.\n" + " noteWidth window is usually the narrowest description pane (~40–50% screen width).\n" + " 3. If the font size is above 26px and reducing it would meaningfully increase\n" + " characters per line, note where to change it (System.json or plugin parameter).\n" "\n" - "Read System.json and report:\n" - " - screenWidth and screenHeight (or window_width / window_height for older MV)\n" - " - fontSize (default 26 for MV; may be absent in MZ if using default)\n" - " - advanced.uiAreaWidth / advanced.uiAreaHeight if present\n" - "\n" - "## Step 2 — Check plugins for window overrides\n" - "\n" - "Search plugins.js (or js/plugins.js) for any of the following and report their\n" - "relevant parameters if present:\n" - "\n" - " VisuMZ_1_MessageCore → MessageWidth, DefaultRows, FontSize\n" - " YEP_MessageCore → messageWidth, messageRows\n" - " MOG_MessageSystem → message_width, message_height\n" - " Any other plugin whose name contains 'Message' or 'Window' with size config\n" - "\n" - "## Step 3 — Calculate character widths\n" - "\n" - "Use this formula for each window type:\n" - "\n" - " content_px = window_pixel_width - (horizontal_padding × 2)\n" - " assume 24px padding per side if unknown\n" - " avg_char_width_px = font_size_px × 0.58 (empirical average for English sans-serif)\n" - " chars_per_line = floor(content_px / avg_char_width_px)\n" - "\n" - "For listWidth, the relevant window is the help/description pane in the menu\n" - "(usually full screen width or half, depending on the game's menu layout).\n" - "For noteWidth, use the narrowest description area you can find — often an item\n" - "detail window that is roughly 40–50% of screen width.\n" - "\n" - "Show your working for each of the three values.\n" - "\n" - "## Step 4 — Font size advice\n" - "\n" - "If the game's font size is larger than 26px, consider whether reducing it would\n" - "let more translated text fit without excessive wrapping. Only recommend a change\n" - "if the gain is meaningful (e.g. 30px → 26px adds ~6 characters per line).\n" - "\n" - "If a font-size change is advisable, identify exactly where to make it:\n" - " a) System.json 'fontSize' field\n" - " b) VisuMZ_1_MessageCore or YEP_MessageCore 'FontSize' parameter in plugins.js\n" - " c) A CSS / font override file if the game uses a custom web font\n" - "\n" - "## Required output format\n" - "\n" - "Reply with EXACTLY this block — fill in the numbers from your analysis:\n" + "Do not show calculations. Just give me the final answer in this exact format:\n" "\n" "```\n" - "# Text wrap recommendations\n" - "\n" - "Screen resolution : x (source: System.json / plugin override)\n" - "Font size : px (source: System.json / plugin override)\n" - "\n" - "Dialogue window : px wide → width=\n" - "List/help window : px wide → listWidth=\n" - "Note window : px wide → noteWidth=\n" - "\n" - "Font size change : <'No change needed' OR 'Change Xpx → Ypx in '>\n" + "width=\n" + "listWidth=\n" + "noteWidth=\n" + "fontSize= # or: no change needed\n" "```\n" "\n" - "After the block, include a short paragraph explaining any assumptions you made\n" - "(e.g. padding estimates, windows whose pixel width could not be determined directly).\n" + "Followed by one sentence of assumptions if anything was estimated.\n" ) - _RISKY_PROMPT = ( + _PLUGIN_PROMPT = ( "You are helping me safely translate a Japanese RPGMaker MV/MZ game.\n" - "Before I run Phase 2 (risky codes), I need you to audit four code types\n" - "and tell me exactly what is safe to translate and what must be left alone.\n" + "Before I run Phase 2, I need you to audit several optional code types and tell me\n" + "(a) which ones contain player-visible Japanese text that needs translation, and\n" + "(b) for code 122, exactly which variable ID ranges carry display text vs internal keys.\n" "\n" "Attach the game's event files (CommonEvents.json, Troops.json, Map*.json)\n" - "and plugins.js before running this prompt.\n" + "and js/plugins.js before running this prompt.\n" "\n" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" - "## AUDIT 1 — Code 111 (Conditional Branch) string comparisons\n" + "## AUDIT 1 — Code 122 (Control Variables) — which variable IDs carry display text\n" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" "\n" - "Search all event lists for code 111 entries that compare a string value\n" - "(operationType 0 or where parameters[3] is a string literal).\n" + "A code 122 entry looks like:\n" + " { \"code\": 122, \"parameters\": [startVarId, endVarId, 0, 4, \"\\\"some string\\\"\"] }\n" + "parameters[0] is the variable ID being set; parameters[4] is the string value\n" + "(only present when parameters[3] == 4, direct string assignment).\n" "\n" - "For each one found, report:\n" - " - The variable or switch being tested\n" - " - The string it is compared against (e.g. === 'ノーマル')\n" - " - Whether the same string appears as a value SET by a code 122 elsewhere\n" - " (meaning translating code 122 would break this 111 comparison)\n" + "Collect every code 122 where parameters[3] == 4. For each variable ID found:\n" + " 1. Is the string also tested in a code 111 $gameVariables comparison?\n" + " → DO NOT TRANSLATE (would break game logic)\n" + " 2. Is the string used as an internal ID / plugin key / script argument?\n" + " → DO NOT TRANSLATE\n" + " 3. Is the string purely player-visible display text?\n" + " → SAFE TO TRANSLATE\n" "\n" - "Verdict for each: SAFE TO TRANSLATE / DO NOT TRANSLATE / NEEDS MANUAL CHECK\n" + "Summarise translatable variable IDs as compact numeric ranges,\n" + "e.g. 'Translate IDs: 5, 10-18, 42'. Separately list any DO NOT TRANSLATE IDs\n" + "with the reason. The ranges will be entered as min/max in the translation tool.\n" "\n" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" - "## AUDIT 2 — Code 122 (Control Variables) string values\n" + "## AUDIT 2 — Plugin codes with visible text (357 / 356 / 355-655 / 657 / 320 / 324 / 325 / 108)\n" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" "\n" - "Search for code 122 entries where parameters[4] is a quoted string.\n" - "For each unique string value found:\n" - " - Is this string also tested in a 111 comparison? → DO NOT TRANSLATE\n" - " - Is this string used as a key / ID in a plugin call or script? → DO NOT TRANSLATE\n" - " - Is this string purely display text (shown to the player)? → SAFE TO TRANSLATE\n" + "For EACH of the following code types, scan all events and report whether any\n" + "instance contains Japanese text that is visible to the player at runtime.\n" "\n" - "List any variables by variable-ID that should be blacklisted from translation.\n" - "\n" - "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" - "## AUDIT 3 — Code 357 (Plugin Commands) — which plugins need enabling\n" - "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" - "\n" - "Search all event lists for code 357 entries and collect every unique\n" - "parameters[0] (plugin header string) that contains translatable Japanese text\n" - "in its parameters[3] dict.\n" - "\n" - "The translation module already has built-in handlers for these headers:\n" - " AdvExtentionllk, VisuMZ_4_ProximityMessages, LL_GalgeChoiceWindow\n" - "\n" - "For every OTHER header you find with Japanese text, check whether it matches\n" - "one of these commented-out entries in the module's headerMappings dict:\n" - "\n" - " LL_InfoPopupWIndow → messageText\n" - " QuestSystem → DetailNote\n" - " BalloonInBattle → text\n" - " MNKR_CommonPopupCoreMZ → text\n" - " DestinationWindow → destination\n" - " _TMLogWindowMZ → text\n" - " TorigoyaMZ_NotifyMessage → message\n" - " SoR_GabWindow → arg1\n" - " DarkPlasma_CharacterText → text\n" - " DTextPicture → text\n" - " TextPicture → text\n" - " TRP_SkitMZ → name\n" - " LogWindow → text\n" - " BattleLogOutput → message\n" - " TorigoyaMZ_NotifyMessage_CommandMessage → message\n" - " NUUN_SaveScreen → AnyName\n" - " build/ARPG_Core → Text, SkillByName\n" - "\n" - "For each match, output the EXACT line to uncomment in headerMappings, e.g.:\n" + "--- Code 357 (MZ Plugin Commands) ---\n" + "parameters[0] = plugin header; parameters[3] = data dict.\n" + "Look up each unique header in js/plugins.js.\n" + "Does any key in parameters[3] hold Japanese text shown on screen?\n" + "The module already handles: AdvExtentionllk, VisuMZ_4_ProximityMessages, LL_GalgeChoiceWindow\n" + "For other headers with visible text, check the commented-out headerMappings list:\n" " \"LL_InfoPopupWIndow\": ([\"messageText\"], None),\n" + " \"QuestSystem\": ([\"DetailNote\"], None),\n" + " \"BalloonInBattle\": ([\"text\"], None),\n" + " \"MNKR_CommonPopupCoreMZ\": ([\"text\"], None),\n" + " \"DestinationWindow\": ([\"destination\"], None),\n" + " \"_TMLogWindowMZ\": ([\"text\"], None),\n" + " \"TorigoyaMZ_NotifyMessage\": ([\"message\"], None),\n" + " \"SoR_GabWindow\": ([\"arg1\"], None),\n" + " \"DarkPlasma_CharacterText\": ([\"text\"], None),\n" + " \"DTextPicture\": ([\"text\"], None),\n" + " \"TextPicture\": ([\"text\"], None),\n" + " \"TRP_SkitMZ\": ([\"name\"], None),\n" + " \"LogWindow\": ([\"text\"], None),\n" + " \"BattleLogOutput\": ([\"message\"], None),\n" + " \"TorigoyaMZ_NotifyMessage_CommandMessage\": ([\"message\"], None),\n" + " \"NUUN_SaveScreen\": ([\"AnyName\"], None),\n" + " \"build/ARPG_Core\": ([\"Text\", \"SkillByName\"], None),\n" + "Output: EXACT uncomment line, or describe parameters[3] if header is new.\n" "\n" - "For headers with Japanese text that do NOT appear in the list above, describe\n" - "the parameters[3] structure so a new entry can be written manually.\n" + "--- Code 356 (MV Plugin Commands) ---\n" + "parameters[0] is a space-delimited string, e.g. 'D_TEXT テキスト 24'.\n" + "The module already handles: D_TEXT, ShowInfo, PushGab, addLog, DW_*, CommonPopup, AddCustomChoice.\n" + "Does any 356 line have Japanese that is shown on screen?\n" + "If yes: ENABLE CODE356 and list the command keywords found.\n" + "If no: SKIP CODE356.\n" + "\n" + "--- Code 355/655 (Inline Scripts) ---\n" + "Code 355 starts a script block; code 655 continues it.\n" + "parameters[0] is the raw JS/script text.\n" + "For each block with Japanese in a string passed to a message/popup/log function:\n" + " • The leading keyword/pattern that identifies the block\n" + " • A regex capturing only the display substring, e.g. テキスト-(.+)\n" + " • Whether it is single-line (355 only) or multi-line (355 + 655)\n" + " • The exact entry to add to the patterns dict:\n" + " \"\": (r\"\", ),\n" + "\n" + "--- Code 657 (Picture Text) ---\n" + "parameters[0] is a plain string drawn onto a picture.\n" + "Does any 657 entry contain Japanese text visible on screen (not a filename)?\n" + "If yes: ENABLE CODE657. If no: SKIP CODE657.\n" + "\n" + "--- Codes 320 / 324 / 325 (Actor Name / Nickname / Profile) ---\n" + "parameters[1] is the new name/nickname/profile string.\n" + "Do any of these codes set Japanese strings visible to the player\n" + "(not just internal IDs or filenames)?\n" + "If yes: ENABLE the respective code. If no: SKIP.\n" + "\n" + "--- Code 108 (Comment / Notetag) ---\n" + "parameters[0] is a comment string used as a plugin notetag.\n" + "The module only translates 108 lines that match specific patterns:\n" + " info:, ActiveMessage:, event_text, Menu Name, text_indicator\n" + "Do any 108 entries matching those patterns have Japanese visible to the player?\n" + "If yes: ENABLE CODE108 and list the patterns found. If no: SKIP CODE108.\n" "\n" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" "## Required output format\n" "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" "\n" - "Reply with four sections using these exact headers:\n" + "Reply with these exact sections:\n" "\n" - "### Code 111 — Dangerous Comparisons\n" - "\n" - "If none found: 'No dangerous string comparisons found.'\n" - "\n" - "### Code 122 — Variable String Safety\n" - "\n" - "If all safe: 'All code 122 string values appear to be display text only.'\n" + "### Code 122 — Variable Translation Ranges\n" + " Translate IDs : \n" + " Do NOT translate: \n" + "If none: 'No display-text string assignments found.'\n" "\n" "### Code 357 — Plugin Handlers to Enable\n" - "For each plugin header with Japanese text:\n" - " • Already handled :
— no action needed\n" - " • Enable in module : uncomment this line in headerMappings:\n" - " \"
\": ([\"\"], None),\n" - " • New entry needed :
— parameters[3] looks like: \n" - "If no 357 codes with Japanese found: 'No code 357 plugin text found.'\n" + " • Already handled :
— no action needed\n" + " • Enable in module : uncomment → \"
\": ([\"\"], None),\n" + " • New entry needed :
— parameters[3] structure: \n" + " • No visible text :
— internal only, skip\n" + "If none: 'No code 357 visible text found.'\n" + "\n" + "### Code 356 — Enable or Skip\n" + " ENABLE / SKIP — keywords found: \n" + "\n" + "### Code 355/655 — Script Patterns to Add\n" + " • Pattern key : \n" + " • Regex : \n" + " • Multiline : \n" + " • Module line : \"\": (r\"\", ),\n" + "If none: 'No translatable 355/655 scripts found.'\n" + "\n" + "### Code 657 — Enable or Skip\n" + " ENABLE / SKIP — \n" + "\n" + "### Codes 320 / 324 / 325 — Enable or Skip\n" + " 320: ENABLE / SKIP 324: ENABLE / SKIP 325: ENABLE / SKIP\n" + "\n" + "### Code 108 — Enable or Skip\n" + " ENABLE / SKIP — patterns found: \n" ) # ── Step 1 (Optional): Pre-process ──────────────────────────────── @@ -1274,6 +1290,37 @@ class WorkflowTab(QWidget): layout.addWidget(wrap_box) + p0_box = QGroupBox("Phase 0 – Core Database Files") + p0_box.setStyleSheet( + "QGroupBox{color:#ccc;border:1px solid #444;border-radius:3px;" + "margin-top:8px;font-size:11px;}" + "QGroupBox::title{padding:0 6px;}" + ) + p0_inner = QVBoxLayout(p0_box) + p0_desc = QLabel( + "Files: Actors, Armors, Weapons, Items, Skills, States, Classes, Enemies, System, MapInfos\n" + "Translates names, descriptions, and notes in the core database. " + "Run this first before any event-code phases." + ) + p0_desc.setStyleSheet("color:#888;font-size:10px;") + p0_desc.setWordWrap(True) + p0_inner.addWidget(p0_desc) + p0_row = QHBoxLayout() + run_p0 = _make_btn("⚙ Set Codes → Go to Translation", "#4a7a4a") + run_p0.setToolTip( + "Sets engine to RPG Maker MV/MZ, selects only DB files " + "(Actors, Armors … etc.), all event codes OFF — then opens Translation tab." + ) + run_p0.clicked.connect(lambda: self._run_phase(0)) + p0_row.addWidget(run_p0) + sync_p0 = _make_btn("🔄 Sync translated/ → files/", "#3a4a6a") + sync_p0.setToolTip("After translation finishes, copy translated/ back into files/ so the next phase starts from the latest state") + sync_p0.clicked.connect(self._copy_translated_to_files) + p0_row.addWidget(sync_p0) + p0_row.addStretch() + p0_inner.addLayout(p0_row) + layout.addWidget(p0_box) + p1_box = QGroupBox("Phase 1 – Safe Codes (dialogue + choices)") p1_box.setStyleSheet( "QGroupBox{color:#ccc;border:1px solid #444;border-radius:3px;" @@ -1294,6 +1341,10 @@ class WorkflowTab(QWidget): run_p1.setToolTip("Applies Phase 1 code settings and switches to the Translation tab") run_p1.clicked.connect(lambda: self._run_phase(1)) p1_row.addWidget(run_p1) + sync_p1 = _make_btn("🔄 Sync translated/ → files/", "#3a4a6a") + sync_p1.setToolTip("After translation finishes, copy translated/ back into files/ so the next phase starts from the latest state") + sync_p1.clicked.connect(self._copy_translated_to_files) + p1_row.addWidget(sync_p1) p1_row.addStretch() p1_inner.addLayout(p1_row) layout.addWidget(p1_box) @@ -1320,6 +1371,10 @@ class WorkflowTab(QWidget): run_p1b.setToolTip("Applies Phase 1b code settings (111 only) and switches to the Translation tab") run_p1b.clicked.connect(lambda: self._run_phase("1b")) p1b_row.addWidget(run_p1b) + sync_p1b = _make_btn("🔄 Sync translated/ → files/", "#3a4a6a") + sync_p1b.setToolTip("After translation finishes, copy translated/ back into files/ so the next phase starts from the latest state") + sync_p1b.clicked.connect(self._copy_translated_to_files) + p1b_row.addWidget(sync_p1b) p1b_row.addStretch() p1b_inner.addLayout(p1b_row) layout.addWidget(p1b_box) @@ -1334,28 +1389,75 @@ class WorkflowTab(QWidget): p2_desc = QLabel( "Codes ON: 122 (Control Variables), 357 (Plugin Commands)\n" "Strings here are often used in script comparisons — mistranslations can break game " - "logic. Use Scan first to preview what will be translated, then apply and go translate." + "logic. Use the Plugin Prompt to audit the game first, then set the var range below." ) p2_desc.setStyleSheet("color:#888;font-size:10px;") p2_desc.setWordWrap(True) p2_inner.addWidget(p2_desc) copy_risky_row = QHBoxLayout() - copy_risky_btn = _make_btn("📋 Copy Risky Prompt", "#555") + copy_risky_btn = _make_btn("📋 Copy Plugin Prompt", "#555") copy_risky_btn.setToolTip( - "Copy a Copilot prompt that audits code 111 string comparisons, " - "code 122 variable safety, and code 357 plugin handlers." + "Copy a Copilot prompt that audits code 122 variable ranges and all optional " + "plugin/script codes (357, 356, 355/655, 657, 320, 324, 325, 108) for visible text." ) - copy_risky_btn.clicked.connect(self._copy_risky_prompt) + copy_risky_btn.clicked.connect(self._copy_plugin_prompt) copy_risky_row.addWidget(copy_risky_btn) copy_risky_row.addStretch() p2_inner.addLayout(copy_risky_row) + # Code 122 variable ID range + var_range_row = QHBoxLayout() + var_range_lbl = QLabel("Code 122 var range:") + var_range_lbl.setStyleSheet("color:#aaa;font-size:10px;") + var_range_row.addWidget(var_range_lbl) + from PyQt5.QtWidgets import QLineEdit + from PyQt5.QtGui import QIntValidator + self._p2_var_min = QLineEdit("0") + self._p2_var_min.setValidator(QIntValidator(0, 99999)) + self._p2_var_min.setFixedWidth(55) + self._p2_var_min.setAlignment(Qt.AlignCenter) + self._p2_var_min.setStyleSheet("QLineEdit{padding:3px;font-size:10px;}") + self._p2_var_min.setToolTip("Minimum variable ID to translate (inclusive)") + var_range_row.addWidget(self._p2_var_min) + dash = QLabel("–") + dash.setStyleSheet("color:#aaa;font-size:10px;") + var_range_row.addWidget(dash) + self._p2_var_max = QLineEdit("2000") + self._p2_var_max.setValidator(QIntValidator(1, 99999)) + self._p2_var_max.setFixedWidth(55) + self._p2_var_max.setAlignment(Qt.AlignCenter) + self._p2_var_max.setStyleSheet("QLineEdit{padding:3px;font-size:10px;}") + self._p2_var_max.setToolTip("Maximum variable ID to translate (exclusive)") + var_range_row.addWidget(self._p2_var_max) + apply_range_btn = _make_btn("Apply", "#3a3a3a") + apply_range_btn.setFixedWidth(54) + apply_range_btn.setToolTip("Write CODE122_VAR_MIN / CODE122_VAR_MAX to the module") + apply_range_btn.clicked.connect(self._apply_var_range) + var_range_row.addWidget(apply_range_btn) + var_range_row.addStretch() + p2_inner.addLayout(var_range_row) + + # Pre-populate range from current module values + try: + from gui.config_integration import ConfigIntegration + cur = ConfigIntegration().read_current_config() + if "CODE122_VAR_MIN" in cur: + self._p2_var_min.setText(str(cur["CODE122_VAR_MIN"])) + if "CODE122_VAR_MAX" in cur: + self._p2_var_max.setText(str(cur["CODE122_VAR_MAX"])) + except Exception: + pass + p2_row = QHBoxLayout() run_p2 = _make_btn("⚙ Set Codes \u2192 Go to Translation", "#7a4a00") run_p2.setToolTip("Applies Phase 2 code settings and switches to the Translation tab") run_p2.clicked.connect(lambda: self._run_phase(2)) p2_row.addWidget(run_p2) + sync_p2 = _make_btn("🔄 Sync translated/ → files/", "#3a4a6a") + sync_p2.setToolTip("After translation finishes, copy translated/ back into files/ so the next phase starts from the latest state") + sync_p2.clicked.connect(self._copy_translated_to_files) + p2_row.addWidget(sync_p2) p2_row.addStretch() p2_inner.addLayout(p2_row) layout.addWidget(p2_box) @@ -1656,8 +1758,36 @@ class WorkflowTab(QWidget): QApplication.clipboard().setText(self._WRAP_PROMPT) self._log("Text-wrap analysis prompt copied to clipboard.") - def _copy_risky_prompt(self): - QApplication.clipboard().setText(self._RISKY_PROMPT) + def _apply_var_range(self): + """Write CODE122_VAR_MIN / CODE122_VAR_MAX to the module file.""" + try: + var_min = int(self._p2_var_min.text() or 0) + var_max = int(self._p2_var_max.text() or 2000) + except ValueError: + self._log("❌ Var range: invalid numbers") + return + cfg = {"CODE122_VAR_MIN": var_min, "CODE122_VAR_MAX": var_max} + try: + from gui.config_integration import ConfigIntegration + ConfigIntegration().update_rpgmaker_config(cfg) + self._log(f"✅ Code 122 var range set: {var_min}–{var_max}") + # Sync to the Settings tab if open + try: + if self.parent_window and hasattr(self.parent_window, "config_tab"): + ct = self.parent_window.config_tab + if hasattr(ct, "rpgmaker_tab") and ct.rpgmaker_tab: + rt = ct.rpgmaker_tab + if hasattr(rt, "code122_var_min_spin"): + rt.code122_var_min_spin.setText(str(var_min)) + if hasattr(rt, "code122_var_max_spin"): + rt.code122_var_max_spin.setText(str(var_max)) + except Exception: + pass + except Exception as exc: + self._log(f"❌ Could not apply var range: {exc}") + + def _copy_plugin_prompt(self): + QApplication.clipboard().setText(self._PLUGIN_PROMPT) self._log("Risky codes analysis prompt copied to clipboard.") def _apply_wrap_config(self): @@ -1716,15 +1846,22 @@ class WorkflowTab(QWidget): # ───────────────────────────────────────────────────────────────────────── def _run_phase(self, phase): - if phase == 1: + if phase == 0: + config = PHASE0_CONFIG + label = "Phase 0 (core DB files)" + file_preset = "db" + elif phase == 1: config = PHASE1_CONFIG label = "Phase 1 (safe codes)" + file_preset = "events" elif phase == "1b": config = PHASE1B_CONFIG label = "Phase 1b (code 111 cache)" + file_preset = "events" else: config = PHASE2_CONFIG label = "Phase 2 (risky codes)" + file_preset = "events" # Apply config profile so the Translation tab uses the right codes try: @@ -1753,7 +1890,14 @@ class WorkflowTab(QWidget): return # Phase-specific guidance - if phase == 1: + if phase == 0: + self._log("") + self._log("─" * 54) + self._log("👉 Switch to the Translation tab and start the run.") + self._log(" Phase 0 translates core DB file fields (names,") + self._log(" descriptions, notes). Event codes are all OFF.") + self._log("─" * 54) + elif phase == 1: self._log("") self._log("─" * 54) self._log("👉 Switch to the Translation tab and start the run.") @@ -1780,13 +1924,72 @@ class WorkflowTab(QWidget): self._log(" Phase 1b has been run first to build the 111 cache.") self._log("─" * 54) - # Navigate to the Translation tab (index 0 in content_stack) + # Navigate to Translation tab and configure it + self._navigate_to_translation(file_preset) + + def _navigate_to_translation(self, file_preset: str): + """Switch to Translation tab, set engine to MVMZ, and check/uncheck files. + + file_preset: + 'db' — check only core DB files, uncheck event files + 'events' — check CommonEvents, Troops, and Map*.json; uncheck DB files + """ try: - if self.parent_window and hasattr(self.parent_window, "content_stack"): - self.parent_window.content_stack.setCurrentIndex(0) - # Keep nav buttons in sync - if hasattr(self.parent_window, "nav_buttons"): - for i, btn in enumerate(self.parent_window.nav_buttons): + pw = self.parent_window + if not pw: + return + tt = getattr(pw, "translation_tab", None) + if tt is None: + return + + # 1. Set engine to RPG Maker MV/MZ + try: + combo = tt.module_combo + for i in range(combo.count()): + if "RPG Maker MV/MZ" in combo.itemText(i): + combo.setCurrentIndex(i) + break + except Exception: + pass + + # 2. Determine which files belong to each preset + files_dir = getattr(tt, "files_dir", None) + if files_dir is None: + files_dir = __import__("pathlib").Path("files") + + def _is_event(name: str) -> bool: + return ( + name in _EVENT_FILES_EXACT + or (name.startswith("Map") and name.endswith(".json") and name not in _DB_FILES) + ) + + def _is_db(name: str) -> bool: + return name in _DB_FILES + + if file_preset == "db": + should_check = _is_db + else: # "events" + should_check = _is_event + + # 3. Apply check states to the file list + try: + tt.refresh_file_lists() + fl = tt.file_list + from PyQt5.QtCore import Qt as _Qt + for idx in range(fl.count()): + item = fl.item(idx) + name = item.text() + item.setCheckState( + _Qt.Checked if should_check(name) else _Qt.Unchecked + ) + except Exception: + pass + + # 4. Navigate + if hasattr(pw, "content_stack"): + pw.content_stack.setCurrentIndex(0) + if hasattr(pw, "nav_buttons"): + for i, btn in enumerate(pw.nav_buttons): btn.setChecked(i == 0) except Exception: pass @@ -1795,6 +1998,32 @@ class WorkflowTab(QWidget): # Step 5 – Export to game # ───────────────────────────────────────────────────────────────────────── + def _copy_translated_to_files(self): + """Copy translated/ files back into files/ (only matching names).""" + import shutil + files_dir = Path("files") + transl_dir = Path("translated") + + if not transl_dir.exists(): + self._log("⚠ translated/ folder not found — nothing to sync.") + return + + active = {fp.name for fp in files_dir.glob("*.json")} if files_dir.exists() else set() + to_copy = [fp for fp in transl_dir.glob("*.json") if not active or fp.name in active] + + if not to_copy: + self._log("⚠ No matching files found in translated/ to sync.") + return + + files_dir.mkdir(exist_ok=True) + copied = 0 + for src in to_copy: + dst = files_dir / src.name + shutil.copy2(src, dst) + copied += 1 + + self._log(f"✅ Synced {copied} file(s) from translated/ → files/") + def _export_active_files(self): """Export only translated files whose names match what is in files/.""" files_dir = Path("files")