gameboy_worlds.emulation.runes_of_virtue.parsers
Runes of Virtue specific game state parser implementations.
Visual screen-region matching is used (mirroring the Pokemon module's design) so that adding additional Runes of Virtue variants only requires per-variant capture data and optional region overrides, not a fork of the parser logic.
CORE DESIGN PRINCIPLE: Never branch the parser subclasses for a given variant. The inheritance tree for a parser after the game variant parser should always be a tree with only one child per layer. This is to ensure that we don't double effort, any capability added to a parser will always be valid for that game variant. If this principle is followed, any state tracker can always use the STRONGEST (lowest level) parser for a given variant without concern for missing functionality.
1""" 2Runes of Virtue specific game state parser implementations. 3 4Visual screen-region matching is used (mirroring the Pokemon module's design) so that 5adding additional Runes of Virtue variants only requires per-variant capture data and 6optional region overrides, not a fork of the parser logic. 7 8CORE DESIGN PRINCIPLE: Never branch the parser subclasses for a given variant. The inheritance tree for a parser after the game variant parser should always be a tree with only one child per layer. 9This is to ensure that we don't double effort, any capability added to a parser will always be valid for that game variant. 10If this principle is followed, any state tracker can always use the STRONGEST (lowest level) parser for a given variant without concern for missing functionality. 11""" 12 13import os 14from abc import ABC, abstractmethod 15from enum import Enum 16from typing import Dict, List, Tuple 17 18import numpy as np 19from pyboy import PyBoy 20 21from gameboy_worlds.emulation.parser import ( 22 NamedScreenRegion, 23 StateParser, 24 _get_proper_regions, 25) 26from gameboy_worlds.utils import log_error, verify_parameters 27 28 29class AgentState(Enum): 30 """ 31 0. FREE_ROAM: The agent is freely roaming the game world. 32 1. IN_MENU: The agent is in the inventory/status menu. 33 2. IN_DIALOGUE: The agent is in an NPC dialogue overlay. 34 """ 35 36 FREE_ROAM = 0 37 IN_MENU = 1 38 IN_DIALOGUE = 2 39 40 41class RunesOfVirtueStateParser(StateParser, ABC): 42 """ 43 Base class for Runes of Virtue game state parsers. Uses visual screen regions to parse game state. 44 Defines common named screen regions and methods for determining game states such as being in a menu. 45 46 Can be used to determine the exact AgentState. 47 """ 48 49 COMMON_REGIONS: List[Tuple[str, int, int, int, int]] = [] 50 """ List of common named screen regions for Runes of Virtue games. Subclasses extend this with variant-specific regions via override_regions. """ 51 52 COMMON_MULTI_TARGET_REGIONS: List[Tuple[str, int, int, int, int]] = [] 53 """ List of common multi-target named screen regions for Runes of Virtue games. """ 54 55 COMMON_MULTI_TARGETS: Dict[str, List[str]] = {} 56 """ Common multi-targets for the common multi-target named screen regions. """ 57 58 def __init__( 59 self, 60 variant: str, 61 pyboy: PyBoy, 62 parameters: dict, 63 additional_named_screen_region_details: List[ 64 Tuple[str, int, int, int, int] 65 ] = [], 66 additional_multi_target_named_screen_region_details: List[ 67 Tuple[str, int, int, int, int] 68 ] = [], 69 override_multi_targets: Dict[str, List[str]] = {}, 70 ): 71 """ 72 Initializes the RunesOfVirtueStateParser. 73 Args: 74 variant (str): The variant of the Runes of Virtue game. 75 pyboy (PyBoy): The PyBoy emulator instance. 76 parameters (dict): Configuration parameters for the emulator. 77 additional_named_screen_region_details: Additional named screen region tuples to register. 78 additional_multi_target_named_screen_region_details: Additional multi-target named screen region tuples to register. 79 override_multi_targets: Dictionary mapping region names to lists of target names for multi-target regions. 80 """ 81 verify_parameters(parameters) 82 regions = _get_proper_regions( 83 override_regions=additional_named_screen_region_details, 84 base_regions=self.COMMON_REGIONS, 85 ) 86 self.variant = variant 87 if f"{variant}_rom_data_path" not in parameters: 88 log_error( 89 f"ROM data path not found for variant: {variant}. Add {variant}_rom_data_path to the config files. See configs/rom_data_path_vars.yaml for an example", 90 parameters, 91 ) 92 self.rom_data_path = parameters[f"{variant}_rom_data_path"] 93 """ Path to the ROM data directory for the specific Runes of Virtue variant.""" 94 captures_dir = os.path.join(self.rom_data_path, "captures") 95 named_screen_regions = [] 96 for region_name, x, y, w, h in regions: 97 region = NamedScreenRegion( 98 region_name, 99 x, 100 y, 101 w, 102 h, 103 parameters=parameters, 104 target_path=os.path.join(captures_dir, region_name), 105 ) 106 named_screen_regions.append(region) 107 multi_target_regions = _get_proper_regions( 108 override_regions=additional_multi_target_named_screen_region_details, 109 base_regions=self.COMMON_MULTI_TARGET_REGIONS, 110 ) 111 multi_target_region_names = [region[0] for region in multi_target_regions] 112 multi_targets = {k: list(v) for k, v in self.COMMON_MULTI_TARGETS.items()} 113 for key in override_multi_targets: 114 if key in multi_targets: 115 multi_targets[key].extend(override_multi_targets[key]) 116 else: 117 multi_targets[key] = list(override_multi_targets[key]) 118 if not set(multi_targets.keys()).issubset(set(multi_target_region_names)): 119 log_error( 120 f"Multi-target regions provided in multi_targets do not match the defined multi-target regions. Provided: {list(multi_targets.keys())}, Defined: {multi_target_region_names}", 121 parameters, 122 ) 123 for region_name, x, y, w, h in multi_target_regions: 124 region_target_paths = {} 125 subdir = os.path.join(captures_dir, region_name) 126 for target_name in multi_targets.get(region_name, []): 127 region_target_paths[target_name] = os.path.join(subdir, target_name) 128 region = NamedScreenRegion( 129 region_name, 130 x, 131 y, 132 w, 133 h, 134 parameters=parameters, 135 multi_target_paths=region_target_paths, 136 ) 137 named_screen_regions.append(region) 138 super().__init__(pyboy, parameters, named_screen_regions) 139 140 @abstractmethod 141 def is_in_menu(self, current_screen: np.ndarray) -> bool: 142 """ 143 Determines if the inventory/status menu is currently open. 144 145 Args: 146 current_screen (np.ndarray): The current screen frame from the emulator. 147 Returns: 148 bool: True if the menu is open, False otherwise. 149 """ 150 raise NotImplementedError 151 152 @abstractmethod 153 def is_in_dialogue(self, current_screen: np.ndarray) -> bool: 154 """ 155 Determines if an NPC dialogue overlay is currently visible. 156 157 Args: 158 current_screen (np.ndarray): The current screen frame from the emulator. 159 Returns: 160 bool: True if any dialogue overlay is visible, False otherwise. 161 """ 162 raise NotImplementedError 163 164 def get_agent_state(self, current_screen: np.ndarray) -> AgentState: 165 """ 166 Determines the current agent state based on the screen. 167 168 Args: 169 current_screen (np.ndarray): The current screen frame from the emulator. 170 171 Returns: 172 AgentState: The current agent state. 173 """ 174 if self.is_in_menu(current_screen): 175 return AgentState.IN_MENU 176 if self.is_in_dialogue(current_screen): 177 return AgentState.IN_DIALOGUE 178 return AgentState.FREE_ROAM 179 180 def dialogue_box_open(self, current_screen: np.ndarray) -> bool: 181 """ 182 Determines if a dialogue box is currently open. 183 """ 184 box = self.capture_named_region( 185 current_frame=current_screen, name=self.get_dialogue_ocr_region_name() 186 ) 187 white_rows = np.mean(box == 255, axis=(1, 2)) 188 return np.count_nonzero(white_rows > 0.98) >= 10 189 190 def dialogue_box_empty(self, current_screen: np.ndarray) -> bool: 191 """ 192 Determines if the dialogue OCR region has no meaningful text content. 193 """ 194 box = self.capture_named_region( 195 current_frame=current_screen, name=self.get_dialogue_ocr_region_name() 196 ) 197 return np.mean(box < 255) < 0.05 198 199 def get_dialogue_ocr_region_name(self) -> str: 200 return "dialogue_ocr_region" 201 202 def __repr__(self) -> str: 203 return f"<RunesOfVirtueParser(variant={self.variant})>" 204 205 206class RunesOfVirtue1StateParser(RunesOfVirtueStateParser): 207 """ 208 State parser for Ultima: Runes of Virtue (1991). 209 210 Screen regions: 211 - menu_indicator: A region (y=30-50, x=20-60) that is uniformly white when the 212 inventory/status menu is open (triggered by START), and contains game world 213 content during normal gameplay. 214 - dialog_indicator: A multi-target dialogue box region shared by NPC dialogue 215 targets with the same screen coordinates. 216 - dialogue_ocr_region: Dialogue text area captured for OCR. 217 """ 218 219 REGIONS = [ 220 ("menu_indicator", 20, 30, 40, 20), 221 ] 222 """ Additional named screen regions specific to Runes of Virtue 1. 223 - menu_indicator: A patch above the dialogue box that goes white when the inventory/status menu opens. Open the START menu to capture this. 224 """ 225 226 MULTI_TARGET_REGIONS = [ 227 ("king_dialog_indicator", 5, 100, 115, 40), 228 ("dialog_indicator", 5, 90, 115, 40), 229 ("cave_indicator", 0, 0, 100, 70), 230 ("playfield_indicator", 0, 0, 144, 144), 231 ("top_playfield_indicator", 0, 0, 144, 24), 232 ("telescope_view_indicator", 40, 40, 80, 60), 233 ("death_screen_indicator", 60, 100, 30, 30), 234 ("dialogue_ocr_region", 5, 90, 135, 54), 235 ] 236 """ Additional multi-target named screen regions specific to Runes of Virtue 1. """ 237 238 MULTI_TARGETS = { 239 "king_dialog_indicator": ["king_dialog"], 240 "dialog_indicator": [ 241 "chuckles_dialog", 242 "gnu_gnu_1_dialog", 243 "gnu_gnu_2_dialog", 244 "sherry_dialog", 245 "dr_cat_cats_lair_dialog", 246 "cavern_of_cowardice_sherry_floor_4_dialog", 247 ], 248 "cave_indicator": [ 249 "cavern_of_hatred", 250 "cavern_of_deceit", 251 "cavern_of_cowardice", 252 ], 253 "playfield_indicator": [ 254 "king_dialog", 255 "chuckles_dialog", 256 "gnu_gnu_1_dialog", 257 "gnu_gnu_2_dialog", 258 "sherry_dialog", 259 "dr_cat_cats_lair_dialog", 260 "cavern_of_cowardice_sherry_floor_4_dialog", 261 "cavern_of_hatred_chest_floor_1_opened", 262 "dr_cat_dialog", 263 "ship_ridden", 264 "basement_ladder_unlocked", 265 "basement_chest_opened", 266 "cavern_of_cowardice_enter_floor_2", 267 "cavern_of_cowardice_enter_floor_3", 268 "cavern_of_cowardice_floor_3_chest_opened", 269 "cavern_of_cowardice_take_stew_floor_4", 270 "cavern_of_cowardice_obtain_coin_floor_4", 271 "cavern_of_hatred_enter_floor_2", 272 "cavern_of_hatred_sherry_floor_2_dialog", 273 "cavern_of_hatred_choose_door_with_sherry", 274 "cavern_of_hatred_choose_right_door_melissa_dialog", 275 "cavern_of_hatred_enter_floor_3", 276 ], 277 "top_playfield_indicator": [ 278 "cavern_of_hatred_enter_floor_4", 279 "cavern_of_cowardice_enter_floor_4", 280 ], 281 "telescope_view_indicator": ["telescope_view"], 282 "death_screen_indicator": ["death_screen"], 283 } 284 """ Multi-target names for Runes of Virtue 1 regions. """ 285 286 def __init__( 287 self, 288 pyboy: PyBoy, 289 parameters: dict, 290 override_regions: List[Tuple[str, int, int, int, int]] = [], 291 override_multi_target_regions: List[Tuple[str, int, int, int, int]] = [], 292 override_multi_targets: Dict[str, List[str]] = {}, 293 ): 294 regions = _get_proper_regions( 295 override_regions=override_regions, base_regions=self.REGIONS 296 ) 297 multi_target_regions = _get_proper_regions( 298 override_regions=override_multi_target_regions, 299 base_regions=self.MULTI_TARGET_REGIONS, 300 ) 301 multi_targets = {k: list(v) for k, v in self.MULTI_TARGETS.items()} 302 for key in override_multi_targets: 303 if key in multi_targets: 304 multi_targets[key].extend(override_multi_targets[key]) 305 else: 306 multi_targets[key] = list(override_multi_targets[key]) 307 super().__init__( 308 variant="runes_of_virtue_1", 309 pyboy=pyboy, 310 parameters=parameters, 311 additional_named_screen_region_details=regions, 312 additional_multi_target_named_screen_region_details=multi_target_regions, 313 override_multi_targets=multi_targets, 314 ) 315 316 _DIALOG_TARGETS = ( 317 ("king_dialog_indicator", "king_dialog"), 318 ("dialog_indicator", "chuckles_dialog"), 319 ("dialog_indicator", "gnu_gnu_1_dialog"), 320 ("dialog_indicator", "gnu_gnu_2_dialog"), 321 ("dialog_indicator", "sherry_dialog"), 322 ("dialog_indicator", "dr_cat_cats_lair_dialog"), 323 ("dialog_indicator", "cavern_of_cowardice_sherry_floor_4_dialog"), 324 ) 325 """ Multi-target screen regions that indicate an NPC dialogue overlay is visible. """ 326 327 def is_in_menu(self, current_screen: np.ndarray) -> bool: 328 return self.named_region_matches_target(current_screen, "menu_indicator") 329 330 def is_in_dialogue(self, current_screen: np.ndarray) -> bool: 331 return any( 332 self.named_region_matches_multi_target(current_screen, name, target_name) 333 for name, target_name in self._DIALOG_TARGETS 334 ) 335 336 337class RunesOfVirtue2StateParser(RunesOfVirtueStateParser): 338 """ 339 State parser for Ultima: Runes of Virtue II. 340 341 Screen regions: 342 - menu_indicator: A first-pass region matching the RoV1 menu detector. Recapture 343 this for RoV2 before relying on the task. 344 - playfield_indicator: Playfield multi-target for opened-book and location screens, 345 excluding the right HUD strip. 346 - dialog_indicator: The left multi-target dialogue panel used for NPC dialogue. 347 - death_screen_indicator: Small death/game-over screen indicator matching RoV1's 348 task region. 349 """ 350 351 REGIONS: List[Tuple[str, int, int, int, int]] = [ 352 ("menu_indicator", 20, 30, 40, 20), 353 ] 354 """ Additional named screen regions specific to Runes of Virtue 2. """ 355 356 MULTI_TARGET_REGIONS: List[Tuple[str, int, int, int, int]] = [ 357 ("playfield_indicator", 0, 0, 144, 144), 358 ("left_playfield_indicator", 0, 0, 56, 144), 359 ("dialog_indicator", 5, 5, 135, 136), 360 ("death_screen_indicator", 60, 100, 30, 30), 361 ] 362 """ Additional multi-target named screen regions specific to Runes of Virtue 2. """ 363 364 MULTI_TARGETS = { 365 "playfield_indicator": [ 366 "book_open", 367 "cave_of_dishonour", 368 "cavern_of_hatred", 369 "cave_of_dishonour_enter_floor_2", 370 "cave_of_dishonour_enter_floor_3", 371 "kitchen_cheese_grabbed", 372 "cheese_given_to_sherry", 373 "ladder_behind_locked_door_climbed", 374 "table_map_interacted", 375 "castle_ladder_back_found", 376 "cavern_of_hatred_gate_1_unlocked", 377 "blocked_room_entered", 378 "cavern_of_hatred_ladder_room_2", 379 "cavern_of_hatred_ladder_2", 380 "cavern_of_hatred_grab_key", 381 "cavern_of_hatred_enter_floor_4", 382 "cavern_of_hatred_enter_floor_5", 383 "cavern_of_hatred_enter_floor_6", 384 "cavern_of_hatred_enter_floor_7", 385 ], 386 "left_playfield_indicator": ["cavern_of_hatred_ladder_out_found"], 387 "dialog_indicator": [ 388 "nystul_dialog", 389 "blacksmith_fail_buy_shield", 390 "sherry_mouse_dialog", 391 "sandy_cook_dialog", 392 "lord_whitsaber_dialog", 393 "tholden_saved", 394 "tholden_brought_back_to_king", 395 "castle_ceremony_attended", 396 ], 397 "death_screen_indicator": ["death_screen"], 398 } 399 """ Multi-target names for Runes of Virtue 2 regions. """ 400 401 def __init__( 402 self, 403 pyboy: PyBoy, 404 parameters: dict, 405 override_regions: List[Tuple[str, int, int, int, int]] = [], 406 override_multi_target_regions: List[Tuple[str, int, int, int, int]] = [], 407 override_multi_targets: Dict[str, List[str]] = {}, 408 ): 409 regions = _get_proper_regions( 410 override_regions=override_regions, base_regions=self.REGIONS 411 ) 412 multi_target_regions = _get_proper_regions( 413 override_regions=override_multi_target_regions, 414 base_regions=self.MULTI_TARGET_REGIONS, 415 ) 416 multi_targets = {k: list(v) for k, v in self.MULTI_TARGETS.items()} 417 for key in override_multi_targets: 418 if key in multi_targets: 419 multi_targets[key].extend(override_multi_targets[key]) 420 else: 421 multi_targets[key] = list(override_multi_targets[key]) 422 super().__init__( 423 variant="runes_of_virtue_2", 424 pyboy=pyboy, 425 parameters=parameters, 426 additional_named_screen_region_details=regions, 427 additional_multi_target_named_screen_region_details=multi_target_regions, 428 override_multi_targets=multi_targets, 429 ) 430 431 _DIALOG_TARGETS = ( 432 ("dialog_indicator", "nystul_dialog"), 433 ("dialog_indicator", "blacksmith_fail_buy_shield"), 434 ("dialog_indicator", "sherry_mouse_dialog"), 435 ("dialog_indicator", "sandy_cook_dialog"), 436 ("dialog_indicator", "lord_whitsaber_dialog"), 437 ("dialog_indicator", "tholden_saved"), 438 ("dialog_indicator", "tholden_brought_back_to_king"), 439 ("dialog_indicator", "castle_ceremony_attended"), 440 ) 441 """ Multi-target screen regions that indicate an NPC dialogue overlay is visible. """ 442 443 def is_in_menu(self, current_screen: np.ndarray) -> bool: 444 if "menu_indicator" not in self.named_screen_regions: 445 return False 446 return self.named_region_matches_target(current_screen, "menu_indicator") 447 448 def is_in_dialogue(self, current_screen: np.ndarray) -> bool: 449 return any( 450 self.named_region_matches_multi_target(current_screen, name, target_name) 451 for name, target_name in self._DIALOG_TARGETS 452 ) 453 454 def get_dialogue_ocr_region_name(self) -> str: 455 return "dialog_indicator"
30class AgentState(Enum): 31 """ 32 0. FREE_ROAM: The agent is freely roaming the game world. 33 1. IN_MENU: The agent is in the inventory/status menu. 34 2. IN_DIALOGUE: The agent is in an NPC dialogue overlay. 35 """ 36 37 FREE_ROAM = 0 38 IN_MENU = 1 39 IN_DIALOGUE = 2
- FREE_ROAM: The agent is freely roaming the game world.
- IN_MENU: The agent is in the inventory/status menu.
- IN_DIALOGUE: The agent is in an NPC dialogue overlay.
42class RunesOfVirtueStateParser(StateParser, ABC): 43 """ 44 Base class for Runes of Virtue game state parsers. Uses visual screen regions to parse game state. 45 Defines common named screen regions and methods for determining game states such as being in a menu. 46 47 Can be used to determine the exact AgentState. 48 """ 49 50 COMMON_REGIONS: List[Tuple[str, int, int, int, int]] = [] 51 """ List of common named screen regions for Runes of Virtue games. Subclasses extend this with variant-specific regions via override_regions. """ 52 53 COMMON_MULTI_TARGET_REGIONS: List[Tuple[str, int, int, int, int]] = [] 54 """ List of common multi-target named screen regions for Runes of Virtue games. """ 55 56 COMMON_MULTI_TARGETS: Dict[str, List[str]] = {} 57 """ Common multi-targets for the common multi-target named screen regions. """ 58 59 def __init__( 60 self, 61 variant: str, 62 pyboy: PyBoy, 63 parameters: dict, 64 additional_named_screen_region_details: List[ 65 Tuple[str, int, int, int, int] 66 ] = [], 67 additional_multi_target_named_screen_region_details: List[ 68 Tuple[str, int, int, int, int] 69 ] = [], 70 override_multi_targets: Dict[str, List[str]] = {}, 71 ): 72 """ 73 Initializes the RunesOfVirtueStateParser. 74 Args: 75 variant (str): The variant of the Runes of Virtue game. 76 pyboy (PyBoy): The PyBoy emulator instance. 77 parameters (dict): Configuration parameters for the emulator. 78 additional_named_screen_region_details: Additional named screen region tuples to register. 79 additional_multi_target_named_screen_region_details: Additional multi-target named screen region tuples to register. 80 override_multi_targets: Dictionary mapping region names to lists of target names for multi-target regions. 81 """ 82 verify_parameters(parameters) 83 regions = _get_proper_regions( 84 override_regions=additional_named_screen_region_details, 85 base_regions=self.COMMON_REGIONS, 86 ) 87 self.variant = variant 88 if f"{variant}_rom_data_path" not in parameters: 89 log_error( 90 f"ROM data path not found for variant: {variant}. Add {variant}_rom_data_path to the config files. See configs/rom_data_path_vars.yaml for an example", 91 parameters, 92 ) 93 self.rom_data_path = parameters[f"{variant}_rom_data_path"] 94 """ Path to the ROM data directory for the specific Runes of Virtue variant.""" 95 captures_dir = os.path.join(self.rom_data_path, "captures") 96 named_screen_regions = [] 97 for region_name, x, y, w, h in regions: 98 region = NamedScreenRegion( 99 region_name, 100 x, 101 y, 102 w, 103 h, 104 parameters=parameters, 105 target_path=os.path.join(captures_dir, region_name), 106 ) 107 named_screen_regions.append(region) 108 multi_target_regions = _get_proper_regions( 109 override_regions=additional_multi_target_named_screen_region_details, 110 base_regions=self.COMMON_MULTI_TARGET_REGIONS, 111 ) 112 multi_target_region_names = [region[0] for region in multi_target_regions] 113 multi_targets = {k: list(v) for k, v in self.COMMON_MULTI_TARGETS.items()} 114 for key in override_multi_targets: 115 if key in multi_targets: 116 multi_targets[key].extend(override_multi_targets[key]) 117 else: 118 multi_targets[key] = list(override_multi_targets[key]) 119 if not set(multi_targets.keys()).issubset(set(multi_target_region_names)): 120 log_error( 121 f"Multi-target regions provided in multi_targets do not match the defined multi-target regions. Provided: {list(multi_targets.keys())}, Defined: {multi_target_region_names}", 122 parameters, 123 ) 124 for region_name, x, y, w, h in multi_target_regions: 125 region_target_paths = {} 126 subdir = os.path.join(captures_dir, region_name) 127 for target_name in multi_targets.get(region_name, []): 128 region_target_paths[target_name] = os.path.join(subdir, target_name) 129 region = NamedScreenRegion( 130 region_name, 131 x, 132 y, 133 w, 134 h, 135 parameters=parameters, 136 multi_target_paths=region_target_paths, 137 ) 138 named_screen_regions.append(region) 139 super().__init__(pyboy, parameters, named_screen_regions) 140 141 @abstractmethod 142 def is_in_menu(self, current_screen: np.ndarray) -> bool: 143 """ 144 Determines if the inventory/status menu is currently open. 145 146 Args: 147 current_screen (np.ndarray): The current screen frame from the emulator. 148 Returns: 149 bool: True if the menu is open, False otherwise. 150 """ 151 raise NotImplementedError 152 153 @abstractmethod 154 def is_in_dialogue(self, current_screen: np.ndarray) -> bool: 155 """ 156 Determines if an NPC dialogue overlay is currently visible. 157 158 Args: 159 current_screen (np.ndarray): The current screen frame from the emulator. 160 Returns: 161 bool: True if any dialogue overlay is visible, False otherwise. 162 """ 163 raise NotImplementedError 164 165 def get_agent_state(self, current_screen: np.ndarray) -> AgentState: 166 """ 167 Determines the current agent state based on the screen. 168 169 Args: 170 current_screen (np.ndarray): The current screen frame from the emulator. 171 172 Returns: 173 AgentState: The current agent state. 174 """ 175 if self.is_in_menu(current_screen): 176 return AgentState.IN_MENU 177 if self.is_in_dialogue(current_screen): 178 return AgentState.IN_DIALOGUE 179 return AgentState.FREE_ROAM 180 181 def dialogue_box_open(self, current_screen: np.ndarray) -> bool: 182 """ 183 Determines if a dialogue box is currently open. 184 """ 185 box = self.capture_named_region( 186 current_frame=current_screen, name=self.get_dialogue_ocr_region_name() 187 ) 188 white_rows = np.mean(box == 255, axis=(1, 2)) 189 return np.count_nonzero(white_rows > 0.98) >= 10 190 191 def dialogue_box_empty(self, current_screen: np.ndarray) -> bool: 192 """ 193 Determines if the dialogue OCR region has no meaningful text content. 194 """ 195 box = self.capture_named_region( 196 current_frame=current_screen, name=self.get_dialogue_ocr_region_name() 197 ) 198 return np.mean(box < 255) < 0.05 199 200 def get_dialogue_ocr_region_name(self) -> str: 201 return "dialogue_ocr_region" 202 203 def __repr__(self) -> str: 204 return f"<RunesOfVirtueParser(variant={self.variant})>"
Base class for Runes of Virtue game state parsers. Uses visual screen regions to parse game state. Defines common named screen regions and methods for determining game states such as being in a menu.
Can be used to determine the exact AgentState.
59 def __init__( 60 self, 61 variant: str, 62 pyboy: PyBoy, 63 parameters: dict, 64 additional_named_screen_region_details: List[ 65 Tuple[str, int, int, int, int] 66 ] = [], 67 additional_multi_target_named_screen_region_details: List[ 68 Tuple[str, int, int, int, int] 69 ] = [], 70 override_multi_targets: Dict[str, List[str]] = {}, 71 ): 72 """ 73 Initializes the RunesOfVirtueStateParser. 74 Args: 75 variant (str): The variant of the Runes of Virtue game. 76 pyboy (PyBoy): The PyBoy emulator instance. 77 parameters (dict): Configuration parameters for the emulator. 78 additional_named_screen_region_details: Additional named screen region tuples to register. 79 additional_multi_target_named_screen_region_details: Additional multi-target named screen region tuples to register. 80 override_multi_targets: Dictionary mapping region names to lists of target names for multi-target regions. 81 """ 82 verify_parameters(parameters) 83 regions = _get_proper_regions( 84 override_regions=additional_named_screen_region_details, 85 base_regions=self.COMMON_REGIONS, 86 ) 87 self.variant = variant 88 if f"{variant}_rom_data_path" not in parameters: 89 log_error( 90 f"ROM data path not found for variant: {variant}. Add {variant}_rom_data_path to the config files. See configs/rom_data_path_vars.yaml for an example", 91 parameters, 92 ) 93 self.rom_data_path = parameters[f"{variant}_rom_data_path"] 94 """ Path to the ROM data directory for the specific Runes of Virtue variant.""" 95 captures_dir = os.path.join(self.rom_data_path, "captures") 96 named_screen_regions = [] 97 for region_name, x, y, w, h in regions: 98 region = NamedScreenRegion( 99 region_name, 100 x, 101 y, 102 w, 103 h, 104 parameters=parameters, 105 target_path=os.path.join(captures_dir, region_name), 106 ) 107 named_screen_regions.append(region) 108 multi_target_regions = _get_proper_regions( 109 override_regions=additional_multi_target_named_screen_region_details, 110 base_regions=self.COMMON_MULTI_TARGET_REGIONS, 111 ) 112 multi_target_region_names = [region[0] for region in multi_target_regions] 113 multi_targets = {k: list(v) for k, v in self.COMMON_MULTI_TARGETS.items()} 114 for key in override_multi_targets: 115 if key in multi_targets: 116 multi_targets[key].extend(override_multi_targets[key]) 117 else: 118 multi_targets[key] = list(override_multi_targets[key]) 119 if not set(multi_targets.keys()).issubset(set(multi_target_region_names)): 120 log_error( 121 f"Multi-target regions provided in multi_targets do not match the defined multi-target regions. Provided: {list(multi_targets.keys())}, Defined: {multi_target_region_names}", 122 parameters, 123 ) 124 for region_name, x, y, w, h in multi_target_regions: 125 region_target_paths = {} 126 subdir = os.path.join(captures_dir, region_name) 127 for target_name in multi_targets.get(region_name, []): 128 region_target_paths[target_name] = os.path.join(subdir, target_name) 129 region = NamedScreenRegion( 130 region_name, 131 x, 132 y, 133 w, 134 h, 135 parameters=parameters, 136 multi_target_paths=region_target_paths, 137 ) 138 named_screen_regions.append(region) 139 super().__init__(pyboy, parameters, named_screen_regions)
Initializes the RunesOfVirtueStateParser.
Arguments:
- variant (str): The variant of the Runes of Virtue game.
- pyboy (PyBoy): The PyBoy emulator instance.
- parameters (dict): Configuration parameters for the emulator.
- additional_named_screen_region_details: Additional named screen region tuples to register.
- additional_multi_target_named_screen_region_details: Additional multi-target named screen region tuples to register.
- override_multi_targets: Dictionary mapping region names to lists of target names for multi-target regions.
List of common named screen regions for Runes of Virtue games. Subclasses extend this with variant-specific regions via override_regions.
List of common multi-target named screen regions for Runes of Virtue games.
Common multi-targets for the common multi-target named screen regions.
153 @abstractmethod 154 def is_in_dialogue(self, current_screen: np.ndarray) -> bool: 155 """ 156 Determines if an NPC dialogue overlay is currently visible. 157 158 Args: 159 current_screen (np.ndarray): The current screen frame from the emulator. 160 Returns: 161 bool: True if any dialogue overlay is visible, False otherwise. 162 """ 163 raise NotImplementedError
Determines if an NPC dialogue overlay is currently visible.
Arguments:
- current_screen (np.ndarray): The current screen frame from the emulator.
Returns:
bool: True if any dialogue overlay is visible, False otherwise.
165 def get_agent_state(self, current_screen: np.ndarray) -> AgentState: 166 """ 167 Determines the current agent state based on the screen. 168 169 Args: 170 current_screen (np.ndarray): The current screen frame from the emulator. 171 172 Returns: 173 AgentState: The current agent state. 174 """ 175 if self.is_in_menu(current_screen): 176 return AgentState.IN_MENU 177 if self.is_in_dialogue(current_screen): 178 return AgentState.IN_DIALOGUE 179 return AgentState.FREE_ROAM
Determines the current agent state based on the screen.
Arguments:
- current_screen (np.ndarray): The current screen frame from the emulator.
Returns:
AgentState: The current agent state.
181 def dialogue_box_open(self, current_screen: np.ndarray) -> bool: 182 """ 183 Determines if a dialogue box is currently open. 184 """ 185 box = self.capture_named_region( 186 current_frame=current_screen, name=self.get_dialogue_ocr_region_name() 187 ) 188 white_rows = np.mean(box == 255, axis=(1, 2)) 189 return np.count_nonzero(white_rows > 0.98) >= 10
Determines if a dialogue box is currently open.
191 def dialogue_box_empty(self, current_screen: np.ndarray) -> bool: 192 """ 193 Determines if the dialogue OCR region has no meaningful text content. 194 """ 195 box = self.capture_named_region( 196 current_frame=current_screen, name=self.get_dialogue_ocr_region_name() 197 ) 198 return np.mean(box < 255) < 0.05
Determines if the dialogue OCR region has no meaningful text content.
Inherited Members
- gameboy_worlds.emulation.parser.StateParser
- named_screen_regions
- image_references
- bit_count
- read_m
- read_bits
- read_bit
- read_m_bit
- get_raised_flags
- get_current_frame
- capture_box
- capture_square_centered
- draw_box
- draw_square_centered
- capture_named_region
- compare_named_region_against_target
- named_region_matches_target
- compare_named_region_against_multi_target
- named_region_matches_multi_target
- draw_named_region
- draw_grid_overlay
- capture_grid_cells
- reform_image
- get_quadrant_frame
- get_image_reference
207class RunesOfVirtue1StateParser(RunesOfVirtueStateParser): 208 """ 209 State parser for Ultima: Runes of Virtue (1991). 210 211 Screen regions: 212 - menu_indicator: A region (y=30-50, x=20-60) that is uniformly white when the 213 inventory/status menu is open (triggered by START), and contains game world 214 content during normal gameplay. 215 - dialog_indicator: A multi-target dialogue box region shared by NPC dialogue 216 targets with the same screen coordinates. 217 - dialogue_ocr_region: Dialogue text area captured for OCR. 218 """ 219 220 REGIONS = [ 221 ("menu_indicator", 20, 30, 40, 20), 222 ] 223 """ Additional named screen regions specific to Runes of Virtue 1. 224 - menu_indicator: A patch above the dialogue box that goes white when the inventory/status menu opens. Open the START menu to capture this. 225 """ 226 227 MULTI_TARGET_REGIONS = [ 228 ("king_dialog_indicator", 5, 100, 115, 40), 229 ("dialog_indicator", 5, 90, 115, 40), 230 ("cave_indicator", 0, 0, 100, 70), 231 ("playfield_indicator", 0, 0, 144, 144), 232 ("top_playfield_indicator", 0, 0, 144, 24), 233 ("telescope_view_indicator", 40, 40, 80, 60), 234 ("death_screen_indicator", 60, 100, 30, 30), 235 ("dialogue_ocr_region", 5, 90, 135, 54), 236 ] 237 """ Additional multi-target named screen regions specific to Runes of Virtue 1. """ 238 239 MULTI_TARGETS = { 240 "king_dialog_indicator": ["king_dialog"], 241 "dialog_indicator": [ 242 "chuckles_dialog", 243 "gnu_gnu_1_dialog", 244 "gnu_gnu_2_dialog", 245 "sherry_dialog", 246 "dr_cat_cats_lair_dialog", 247 "cavern_of_cowardice_sherry_floor_4_dialog", 248 ], 249 "cave_indicator": [ 250 "cavern_of_hatred", 251 "cavern_of_deceit", 252 "cavern_of_cowardice", 253 ], 254 "playfield_indicator": [ 255 "king_dialog", 256 "chuckles_dialog", 257 "gnu_gnu_1_dialog", 258 "gnu_gnu_2_dialog", 259 "sherry_dialog", 260 "dr_cat_cats_lair_dialog", 261 "cavern_of_cowardice_sherry_floor_4_dialog", 262 "cavern_of_hatred_chest_floor_1_opened", 263 "dr_cat_dialog", 264 "ship_ridden", 265 "basement_ladder_unlocked", 266 "basement_chest_opened", 267 "cavern_of_cowardice_enter_floor_2", 268 "cavern_of_cowardice_enter_floor_3", 269 "cavern_of_cowardice_floor_3_chest_opened", 270 "cavern_of_cowardice_take_stew_floor_4", 271 "cavern_of_cowardice_obtain_coin_floor_4", 272 "cavern_of_hatred_enter_floor_2", 273 "cavern_of_hatred_sherry_floor_2_dialog", 274 "cavern_of_hatred_choose_door_with_sherry", 275 "cavern_of_hatred_choose_right_door_melissa_dialog", 276 "cavern_of_hatred_enter_floor_3", 277 ], 278 "top_playfield_indicator": [ 279 "cavern_of_hatred_enter_floor_4", 280 "cavern_of_cowardice_enter_floor_4", 281 ], 282 "telescope_view_indicator": ["telescope_view"], 283 "death_screen_indicator": ["death_screen"], 284 } 285 """ Multi-target names for Runes of Virtue 1 regions. """ 286 287 def __init__( 288 self, 289 pyboy: PyBoy, 290 parameters: dict, 291 override_regions: List[Tuple[str, int, int, int, int]] = [], 292 override_multi_target_regions: List[Tuple[str, int, int, int, int]] = [], 293 override_multi_targets: Dict[str, List[str]] = {}, 294 ): 295 regions = _get_proper_regions( 296 override_regions=override_regions, base_regions=self.REGIONS 297 ) 298 multi_target_regions = _get_proper_regions( 299 override_regions=override_multi_target_regions, 300 base_regions=self.MULTI_TARGET_REGIONS, 301 ) 302 multi_targets = {k: list(v) for k, v in self.MULTI_TARGETS.items()} 303 for key in override_multi_targets: 304 if key in multi_targets: 305 multi_targets[key].extend(override_multi_targets[key]) 306 else: 307 multi_targets[key] = list(override_multi_targets[key]) 308 super().__init__( 309 variant="runes_of_virtue_1", 310 pyboy=pyboy, 311 parameters=parameters, 312 additional_named_screen_region_details=regions, 313 additional_multi_target_named_screen_region_details=multi_target_regions, 314 override_multi_targets=multi_targets, 315 ) 316 317 _DIALOG_TARGETS = ( 318 ("king_dialog_indicator", "king_dialog"), 319 ("dialog_indicator", "chuckles_dialog"), 320 ("dialog_indicator", "gnu_gnu_1_dialog"), 321 ("dialog_indicator", "gnu_gnu_2_dialog"), 322 ("dialog_indicator", "sherry_dialog"), 323 ("dialog_indicator", "dr_cat_cats_lair_dialog"), 324 ("dialog_indicator", "cavern_of_cowardice_sherry_floor_4_dialog"), 325 ) 326 """ Multi-target screen regions that indicate an NPC dialogue overlay is visible. """ 327 328 def is_in_menu(self, current_screen: np.ndarray) -> bool: 329 return self.named_region_matches_target(current_screen, "menu_indicator") 330 331 def is_in_dialogue(self, current_screen: np.ndarray) -> bool: 332 return any( 333 self.named_region_matches_multi_target(current_screen, name, target_name) 334 for name, target_name in self._DIALOG_TARGETS 335 )
State parser for Ultima: Runes of Virtue (1991).
Screen regions:
- menu_indicator: A region (y=30-50, x=20-60) that is uniformly white when the inventory/status menu is open (triggered by START), and contains game world content during normal gameplay.
- dialog_indicator: A multi-target dialogue box region shared by NPC dialogue targets with the same screen coordinates.
- dialogue_ocr_region: Dialogue text area captured for OCR.
287 def __init__( 288 self, 289 pyboy: PyBoy, 290 parameters: dict, 291 override_regions: List[Tuple[str, int, int, int, int]] = [], 292 override_multi_target_regions: List[Tuple[str, int, int, int, int]] = [], 293 override_multi_targets: Dict[str, List[str]] = {}, 294 ): 295 regions = _get_proper_regions( 296 override_regions=override_regions, base_regions=self.REGIONS 297 ) 298 multi_target_regions = _get_proper_regions( 299 override_regions=override_multi_target_regions, 300 base_regions=self.MULTI_TARGET_REGIONS, 301 ) 302 multi_targets = {k: list(v) for k, v in self.MULTI_TARGETS.items()} 303 for key in override_multi_targets: 304 if key in multi_targets: 305 multi_targets[key].extend(override_multi_targets[key]) 306 else: 307 multi_targets[key] = list(override_multi_targets[key]) 308 super().__init__( 309 variant="runes_of_virtue_1", 310 pyboy=pyboy, 311 parameters=parameters, 312 additional_named_screen_region_details=regions, 313 additional_multi_target_named_screen_region_details=multi_target_regions, 314 override_multi_targets=multi_targets, 315 )
Initializes the RunesOfVirtueStateParser.
Arguments:
- variant (str): The variant of the Runes of Virtue game.
- pyboy (PyBoy): The PyBoy emulator instance.
- parameters (dict): Configuration parameters for the emulator.
- additional_named_screen_region_details: Additional named screen region tuples to register.
- additional_multi_target_named_screen_region_details: Additional multi-target named screen region tuples to register.
- override_multi_targets: Dictionary mapping region names to lists of target names for multi-target regions.
Additional named screen regions specific to Runes of Virtue 1.
- menu_indicator: A patch above the dialogue box that goes white when the inventory/status menu opens. Open the START menu to capture this.
Additional multi-target named screen regions specific to Runes of Virtue 1.
Multi-target names for Runes of Virtue 1 regions.
331 def is_in_dialogue(self, current_screen: np.ndarray) -> bool: 332 return any( 333 self.named_region_matches_multi_target(current_screen, name, target_name) 334 for name, target_name in self._DIALOG_TARGETS 335 )
Determines if an NPC dialogue overlay is currently visible.
Arguments:
- current_screen (np.ndarray): The current screen frame from the emulator.
Returns:
bool: True if any dialogue overlay is visible, False otherwise.
Inherited Members
- RunesOfVirtueStateParser
- COMMON_REGIONS
- COMMON_MULTI_TARGET_REGIONS
- COMMON_MULTI_TARGETS
- variant
- rom_data_path
- get_agent_state
- dialogue_box_open
- dialogue_box_empty
- get_dialogue_ocr_region_name
- gameboy_worlds.emulation.parser.StateParser
- named_screen_regions
- image_references
- bit_count
- read_m
- read_bits
- read_bit
- read_m_bit
- get_raised_flags
- get_current_frame
- capture_box
- capture_square_centered
- draw_box
- draw_square_centered
- capture_named_region
- compare_named_region_against_target
- named_region_matches_target
- compare_named_region_against_multi_target
- named_region_matches_multi_target
- draw_named_region
- draw_grid_overlay
- capture_grid_cells
- reform_image
- get_quadrant_frame
- get_image_reference
338class RunesOfVirtue2StateParser(RunesOfVirtueStateParser): 339 """ 340 State parser for Ultima: Runes of Virtue II. 341 342 Screen regions: 343 - menu_indicator: A first-pass region matching the RoV1 menu detector. Recapture 344 this for RoV2 before relying on the task. 345 - playfield_indicator: Playfield multi-target for opened-book and location screens, 346 excluding the right HUD strip. 347 - dialog_indicator: The left multi-target dialogue panel used for NPC dialogue. 348 - death_screen_indicator: Small death/game-over screen indicator matching RoV1's 349 task region. 350 """ 351 352 REGIONS: List[Tuple[str, int, int, int, int]] = [ 353 ("menu_indicator", 20, 30, 40, 20), 354 ] 355 """ Additional named screen regions specific to Runes of Virtue 2. """ 356 357 MULTI_TARGET_REGIONS: List[Tuple[str, int, int, int, int]] = [ 358 ("playfield_indicator", 0, 0, 144, 144), 359 ("left_playfield_indicator", 0, 0, 56, 144), 360 ("dialog_indicator", 5, 5, 135, 136), 361 ("death_screen_indicator", 60, 100, 30, 30), 362 ] 363 """ Additional multi-target named screen regions specific to Runes of Virtue 2. """ 364 365 MULTI_TARGETS = { 366 "playfield_indicator": [ 367 "book_open", 368 "cave_of_dishonour", 369 "cavern_of_hatred", 370 "cave_of_dishonour_enter_floor_2", 371 "cave_of_dishonour_enter_floor_3", 372 "kitchen_cheese_grabbed", 373 "cheese_given_to_sherry", 374 "ladder_behind_locked_door_climbed", 375 "table_map_interacted", 376 "castle_ladder_back_found", 377 "cavern_of_hatred_gate_1_unlocked", 378 "blocked_room_entered", 379 "cavern_of_hatred_ladder_room_2", 380 "cavern_of_hatred_ladder_2", 381 "cavern_of_hatred_grab_key", 382 "cavern_of_hatred_enter_floor_4", 383 "cavern_of_hatred_enter_floor_5", 384 "cavern_of_hatred_enter_floor_6", 385 "cavern_of_hatred_enter_floor_7", 386 ], 387 "left_playfield_indicator": ["cavern_of_hatred_ladder_out_found"], 388 "dialog_indicator": [ 389 "nystul_dialog", 390 "blacksmith_fail_buy_shield", 391 "sherry_mouse_dialog", 392 "sandy_cook_dialog", 393 "lord_whitsaber_dialog", 394 "tholden_saved", 395 "tholden_brought_back_to_king", 396 "castle_ceremony_attended", 397 ], 398 "death_screen_indicator": ["death_screen"], 399 } 400 """ Multi-target names for Runes of Virtue 2 regions. """ 401 402 def __init__( 403 self, 404 pyboy: PyBoy, 405 parameters: dict, 406 override_regions: List[Tuple[str, int, int, int, int]] = [], 407 override_multi_target_regions: List[Tuple[str, int, int, int, int]] = [], 408 override_multi_targets: Dict[str, List[str]] = {}, 409 ): 410 regions = _get_proper_regions( 411 override_regions=override_regions, base_regions=self.REGIONS 412 ) 413 multi_target_regions = _get_proper_regions( 414 override_regions=override_multi_target_regions, 415 base_regions=self.MULTI_TARGET_REGIONS, 416 ) 417 multi_targets = {k: list(v) for k, v in self.MULTI_TARGETS.items()} 418 for key in override_multi_targets: 419 if key in multi_targets: 420 multi_targets[key].extend(override_multi_targets[key]) 421 else: 422 multi_targets[key] = list(override_multi_targets[key]) 423 super().__init__( 424 variant="runes_of_virtue_2", 425 pyboy=pyboy, 426 parameters=parameters, 427 additional_named_screen_region_details=regions, 428 additional_multi_target_named_screen_region_details=multi_target_regions, 429 override_multi_targets=multi_targets, 430 ) 431 432 _DIALOG_TARGETS = ( 433 ("dialog_indicator", "nystul_dialog"), 434 ("dialog_indicator", "blacksmith_fail_buy_shield"), 435 ("dialog_indicator", "sherry_mouse_dialog"), 436 ("dialog_indicator", "sandy_cook_dialog"), 437 ("dialog_indicator", "lord_whitsaber_dialog"), 438 ("dialog_indicator", "tholden_saved"), 439 ("dialog_indicator", "tholden_brought_back_to_king"), 440 ("dialog_indicator", "castle_ceremony_attended"), 441 ) 442 """ Multi-target screen regions that indicate an NPC dialogue overlay is visible. """ 443 444 def is_in_menu(self, current_screen: np.ndarray) -> bool: 445 if "menu_indicator" not in self.named_screen_regions: 446 return False 447 return self.named_region_matches_target(current_screen, "menu_indicator") 448 449 def is_in_dialogue(self, current_screen: np.ndarray) -> bool: 450 return any( 451 self.named_region_matches_multi_target(current_screen, name, target_name) 452 for name, target_name in self._DIALOG_TARGETS 453 ) 454 455 def get_dialogue_ocr_region_name(self) -> str: 456 return "dialog_indicator"
State parser for Ultima: Runes of Virtue II.
Screen regions:
- menu_indicator: A first-pass region matching the RoV1 menu detector. Recapture this for RoV2 before relying on the task.
- playfield_indicator: Playfield multi-target for opened-book and location screens, excluding the right HUD strip.
- dialog_indicator: The left multi-target dialogue panel used for NPC dialogue.
- death_screen_indicator: Small death/game-over screen indicator matching RoV1's task region.
402 def __init__( 403 self, 404 pyboy: PyBoy, 405 parameters: dict, 406 override_regions: List[Tuple[str, int, int, int, int]] = [], 407 override_multi_target_regions: List[Tuple[str, int, int, int, int]] = [], 408 override_multi_targets: Dict[str, List[str]] = {}, 409 ): 410 regions = _get_proper_regions( 411 override_regions=override_regions, base_regions=self.REGIONS 412 ) 413 multi_target_regions = _get_proper_regions( 414 override_regions=override_multi_target_regions, 415 base_regions=self.MULTI_TARGET_REGIONS, 416 ) 417 multi_targets = {k: list(v) for k, v in self.MULTI_TARGETS.items()} 418 for key in override_multi_targets: 419 if key in multi_targets: 420 multi_targets[key].extend(override_multi_targets[key]) 421 else: 422 multi_targets[key] = list(override_multi_targets[key]) 423 super().__init__( 424 variant="runes_of_virtue_2", 425 pyboy=pyboy, 426 parameters=parameters, 427 additional_named_screen_region_details=regions, 428 additional_multi_target_named_screen_region_details=multi_target_regions, 429 override_multi_targets=multi_targets, 430 )
Initializes the RunesOfVirtueStateParser.
Arguments:
- variant (str): The variant of the Runes of Virtue game.
- pyboy (PyBoy): The PyBoy emulator instance.
- parameters (dict): Configuration parameters for the emulator.
- additional_named_screen_region_details: Additional named screen region tuples to register.
- additional_multi_target_named_screen_region_details: Additional multi-target named screen region tuples to register.
- override_multi_targets: Dictionary mapping region names to lists of target names for multi-target regions.
Additional named screen regions specific to Runes of Virtue 2.
Additional multi-target named screen regions specific to Runes of Virtue 2.
Multi-target names for Runes of Virtue 2 regions.
449 def is_in_dialogue(self, current_screen: np.ndarray) -> bool: 450 return any( 451 self.named_region_matches_multi_target(current_screen, name, target_name) 452 for name, target_name in self._DIALOG_TARGETS 453 )
Determines if an NPC dialogue overlay is currently visible.
Arguments:
- current_screen (np.ndarray): The current screen frame from the emulator.
Returns:
bool: True if any dialogue overlay is visible, False otherwise.
Inherited Members
- RunesOfVirtueStateParser
- COMMON_REGIONS
- COMMON_MULTI_TARGET_REGIONS
- COMMON_MULTI_TARGETS
- variant
- rom_data_path
- get_agent_state
- dialogue_box_open
- dialogue_box_empty
- gameboy_worlds.emulation.parser.StateParser
- named_screen_regions
- image_references
- bit_count
- read_m
- read_bits
- read_bit
- read_m_bit
- get_raised_flags
- get_current_frame
- capture_box
- capture_square_centered
- draw_box
- draw_square_centered
- capture_named_region
- compare_named_region_against_target
- named_region_matches_target
- compare_named_region_against_multi_target
- named_region_matches_multi_target
- draw_named_region
- draw_grid_overlay
- capture_grid_cells
- reform_image
- get_quadrant_frame
- get_image_reference