Skip to content

fix: a_until_dialog_end breaks after a single A press (wrong dict key) - #6

Open
golldyck wants to merge 1 commit into
NousResearch:mainfrom
golldyck:fix/a-until-dialog-end-single-press
Open

fix: a_until_dialog_end breaks after a single A press (wrong dict key)#6
golldyck wants to merge 1 commit into
NousResearch:mainfrom
golldyck:fix/a-until-dialog-end-single-press

Conversation

@golldyck

@golldyck golldyck commented Jul 1, 2026

Copy link
Copy Markdown

The a_until_dialog_end action is supposed to press A every 30 frames until the dialog clears (max 300), but it exits after a single press.

Cause

In _execute_action (pokemon_agent/server.py), the loop checks:

state = _get_state_dict()
if not state.get("dialog_active", False):
    break

Dialog state is nested. _get_state_dict() returns state["dialog"] = reader.read_dialog(), which is {"active": bool, ...}. There is no top-level "dialog_active" key. So .get("dialog_active", False) always returns False, not False is True, and the loop breaks on the first iteration.

The rest of the codebase already reads this flag from the nested location:

  • autopilot.py:95 uses (state.get("dialog") or {}).get("active")
  • dashboard/history.py:240 uses state.get("dialog", {}).get("active")

Effect

a_until_dialog_end presses A exactly once (30 frames) instead of looping. Multi-box NPC dialogs and long text are never advanced by this action, which defeats the purpose of the command.

Fix

Read the nested flag with the same defensive pattern used elsewhere. This also handles dialog being None, e.g. FireRed where read_dialog is not yet implemented:

if not (state.get("dialog") or {}).get("active", False):
    break

Tests

Adds test_dialog_action.py with a fake emulator/reader (no ROM needed):

  • loops until the dialog clears: 4 A presses across 3 active checks plus the clearing one
  • respects the 300-frame cap: stops at 10 presses when the dialog never clears
  • stops immediately when no dialog: 1 press

The first two fail on main (1 press each, proving the single-press bug) and pass with this change.

_execute_action('a_until_dialog_end') checked state.get('dialog_active'),
but dialog state is nested under state['dialog']['active'] (see read_dialog
and the state builder; autopilot.py and dashboard/history.py read it the
same way). The top-level 'dialog_active' key never exists, so the lookup
always returned the default False and the loop broke after a single A
press — long/multi-box NPC dialogs were never advanced.

Read the nested dialog flag with the same defensive pattern used elsewhere
((state.get('dialog') or {}).get('active', False)) so the loop runs until
the dialog clears or the 300-frame cap is hit. Adds unit tests covering
the loop-until-clear, max-cap, and no-dialog cases.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant