gameboy_worlds.emulation.sword_of_hope.parsers
1import os 2from typing import List, Tuple, Dict 3 4from gameboy_worlds.utils import verify_parameters, log_error 5from gameboy_worlds.emulation.parser import StateParser, NamedScreenRegion, _get_proper_regions 6 7 8class _BaseSwordOfHopeParser(StateParser): 9 """ 10 Base parser for Sword of Hope variants. 11 Defines common screen regions for the SoH UI layout: 12 - Top stats bar (LV, HP, MP) 13 - Game viewport + Gold/EX 14 - Status bar (MOVE | ROOM NAME) 15 - Command grid (Look/Use/Open/Magic/Hit/Power + directional pad) 16 """ 17 18 VARIANT = "" 19 20 COMMON_MULTI_TARGET_REGIONS = [ 21 ("room_label", 48, 72, 104, 16), 22 ("game_viewport", 4, 16, 92, 56), 23 ("status_bar", 4, 72, 152, 16), 24 ("command_area", 4, 88, 152, 56), 25 ("battle_full", 4, 16, 152, 128), 26 ("battle_viewport", 4, 16, 152, 56), 27 ("battle_command", 4, 74, 152, 70), 28 ("status_command", 4, 72, 152, 72), 29 ("full_screen", 0, 0, 160, 144), 30 ("soh2_enemy_viewport", 4, 0, 152, 68), 31 ("soh2_battle_command", 4, 68, 152, 19), 32 ("soh2_party_stats", 4, 87, 152, 48), 33 ("soh2_room_label", 8, 4, 144, 20), 34 ] 35 """ 36 - room_label: The room name text on the right side of the MOVE|ROOM status bar. Used for location-based termination. 37 - game_viewport: The rendered scene area (top-left quadrant of the screen, below stats). 38 - status_bar: The full MOVE | ROOM NAME bar. 39 - command_area: The bottom command grid (directional pad + action commands). 40 - battle_full: Everything below the LV/HP/MP stats bar (enemy sprites + battle commands). Used for battle detection. 41 - battle_viewport: Full-width enemy sprite area (wider than game_viewport to include right side). 42 - battle_command: Battle command area including FIGHT/AUTO row + MAGIC/USE/ESCAPE + enemy list. 43 - status_command: Status bar + command area combined (y=72 to y=144). Useful for detecting interaction results where text changes span both areas. 44 - soh2_enemy_viewport: SoH2 only. Enemy sprite area; SoH2 has no top stats bar so the sprite extends to y=0. 45 - soh2_battle_command: SoH2 only. Horizontal command row (CLASH/FLEE/AUTO solo, MAGIC/ITEM/etc with party). 46 - soh2_party_stats: SoH2 only. Live LV/HP/MP table at the bottom. Values change per frame so this is mainly for VLM/OCR, not for .npy match termination. 47 - soh2_room_label: SoH2 only. The room name header at the TOP of the screen (SoH2 puts the room name above the viewport, not in the bottom MOVE bar like SoH1). 48 """ 49 50 COMMON_MULTI_TARGETS = {} 51 52 def __init__( 53 self, 54 pyboy, 55 parameters, 56 additional_multi_target_regions: List[Tuple[str, int, int, int, int]] = [], 57 override_multi_targets: Dict[str, List[str]] = {}, 58 ): 59 verify_parameters(parameters) 60 variant = self.VARIANT 61 if f"{variant}_rom_data_path" not in parameters: 62 log_error( 63 f"ROM data path not found for variant: {variant}. Add {variant}_rom_data_path to config files.", 64 parameters, 65 ) 66 self.rom_data_path = parameters[f"{variant}_rom_data_path"] 67 captures_dir = os.path.join(self.rom_data_path, "captures") 68 69 multi_target_regions = _get_proper_regions( 70 override_regions=additional_multi_target_regions, 71 base_regions=self.COMMON_MULTI_TARGET_REGIONS, 72 ) 73 74 multi_targets = {} 75 for key, val in self.COMMON_MULTI_TARGETS.items(): 76 multi_targets[key] = list(val) 77 for key in override_multi_targets: 78 if key in multi_targets: 79 multi_targets[key].extend(override_multi_targets[key]) 80 else: 81 multi_targets[key] = override_multi_targets[key] 82 83 named_screen_regions = [] 84 for region_name, x, y, w, h in multi_target_regions: 85 region_target_paths = {} 86 subdir = os.path.join(captures_dir, region_name) 87 for target_name in multi_targets.get(region_name, []): 88 region_target_paths[target_name] = os.path.join(subdir, target_name) 89 region = NamedScreenRegion( 90 region_name, 91 x, 92 y, 93 w, 94 h, 95 parameters=parameters, 96 multi_target_paths=region_target_paths, 97 ) 98 named_screen_regions.append(region) 99 100 super().__init__(pyboy, parameters, named_screen_regions) 101 102 def __repr__(self) -> str: 103 return f"{self.__class__.__name__}(variant={self.VARIANT})" 104 105 106class SwordOfHope1Parser(_BaseSwordOfHopeParser): 107 VARIANT = "sword_of_hope_1" 108 109 COMMON_MULTI_TARGETS = { 110 "room_label": [ 111 "mill_room", 112 "oldman_house", 113 "in_forest", 114 "shaman_house", 115 "shaman_room", 116 ], 117 "command_area": [ 118 "dialogue_active", 119 "dialogue_cleared", 120 "look_selected", 121 "exploration_menu", 122 "menu_open", 123 ], 124 "battle_command": [ 125 "battle_active", 126 "battle_won", 127 "battle_magic_menu", 128 "escape_confirmed", 129 "cursor_on_auto", 130 "cursor_on_firebal", 131 "cursor_on_firebal2", 132 "cursor_on_stripall", 133 ], 134 "status_command": [ 135 "look_target_options", 136 "item_found", 137 "shop_menu_open", 138 "purchase_confirmed", 139 "dialogue_visible", 140 "dialogue_initiated", 141 "dialogue_advanced", 142 "magic_menu_open", 143 "teleport_result", 144 "teleport_dest_cursor", 145 "teleport_landed", 146 "cursor_on_look", 147 "cursor_on_open", 148 "cursor_on_use", 149 "cursor_on_magic", 150 "cursor_on_hit", 151 "cursor_on_power", 152 "power_stats_first_page", 153 "cursor_on_teleport_oldman", 154 "cursor_on_herb_use", 155 "cursor_on_keym_use", 156 "power_stats_last_page", 157 ], 158 "battle_full": [ 159 "mistress_first_dialogue", 160 "mistress_second_dialogue", 161 "save_prompt_visible", 162 "save_confirmed", 163 "look_path_target", 164 "herb_received", 165 "treant_defeated", 166 "key_m_selected", 167 "gate_opened", 168 "in_backroom", 169 "scroll_received", 170 "grace_selected", 171 "charm_received", 172 ], 173 } 174 175 176class SwordOfHope2Parser(_BaseSwordOfHopeParser): 177 VARIANT = "sword_of_hope_2" 178 179 COMMON_MULTI_TARGETS = { 180 "status_command": [ 181 "dialogue_active", 182 "dialogue_cleared", 183 "dialogue_initiated", 184 "dialogue_advanced", 185 "dialogue_visible", 186 "battle_won", 187 "escape_confirmed", 188 "wheat_purchased", 189 "motion_result", 190 "battle_magic_menu", 191 "cpr_sword_purchased", 192 "wheat_from_tree", 193 "wheat_consumed", 194 "cursor_on_power", 195 "power_stats_first_page", 196 "power_stats_exp_page", 197 "cursor_on_look", 198 "cursor_on_item", 199 "cursor_on_open", 200 "cursor_on_magic", 201 "cursor_on_hit", 202 "cursor_on_motion", 203 "cursor_on_theo", 204 ], 205 "full_screen": [ 206 "shop_menu_open", 207 "magic_menu_open", 208 "weapons_shop_menu_open", 209 "look_shopkeeper", 210 "shop_buy_sell_menu", 211 "cursor_on_cpr_sword", 212 "hit_tree_target", 213 "item_menu_open", 214 "use_menu_open", 215 "cursor_on_wheat", 216 "cursor_on_shop_first_item", 217 "cursor_on_shop_third_item", 218 "cursor_on_first_weapon", 219 "cursor_on_second_weapon", 220 "cursor_on_third_weapon", 221 "look_tree_target", 222 ], 223 "command_area": [ 224 "exploration_menu", 225 "power_menu_last_slide", 226 ], 227 "soh2_room_label": [ 228 "kings_room", 229 "riccar_castle", 230 "temple_1f", 231 "outside_temple", 232 ], 233 "soh2_battle_command": [ 234 "battle_active", 235 "cursor_on_auto", 236 "cursor_on_clash", 237 ], 238 }
107class SwordOfHope1Parser(_BaseSwordOfHopeParser): 108 VARIANT = "sword_of_hope_1" 109 110 COMMON_MULTI_TARGETS = { 111 "room_label": [ 112 "mill_room", 113 "oldman_house", 114 "in_forest", 115 "shaman_house", 116 "shaman_room", 117 ], 118 "command_area": [ 119 "dialogue_active", 120 "dialogue_cleared", 121 "look_selected", 122 "exploration_menu", 123 "menu_open", 124 ], 125 "battle_command": [ 126 "battle_active", 127 "battle_won", 128 "battle_magic_menu", 129 "escape_confirmed", 130 "cursor_on_auto", 131 "cursor_on_firebal", 132 "cursor_on_firebal2", 133 "cursor_on_stripall", 134 ], 135 "status_command": [ 136 "look_target_options", 137 "item_found", 138 "shop_menu_open", 139 "purchase_confirmed", 140 "dialogue_visible", 141 "dialogue_initiated", 142 "dialogue_advanced", 143 "magic_menu_open", 144 "teleport_result", 145 "teleport_dest_cursor", 146 "teleport_landed", 147 "cursor_on_look", 148 "cursor_on_open", 149 "cursor_on_use", 150 "cursor_on_magic", 151 "cursor_on_hit", 152 "cursor_on_power", 153 "power_stats_first_page", 154 "cursor_on_teleport_oldman", 155 "cursor_on_herb_use", 156 "cursor_on_keym_use", 157 "power_stats_last_page", 158 ], 159 "battle_full": [ 160 "mistress_first_dialogue", 161 "mistress_second_dialogue", 162 "save_prompt_visible", 163 "save_confirmed", 164 "look_path_target", 165 "herb_received", 166 "treant_defeated", 167 "key_m_selected", 168 "gate_opened", 169 "in_backroom", 170 "scroll_received", 171 "grace_selected", 172 "charm_received", 173 ], 174 }
Base parser for Sword of Hope variants.
Defines common screen regions for the SoH UI layout:
- Top stats bar (LV, HP, MP)
- Game viewport + Gold/EX
- Status bar (MOVE | ROOM NAME)
- Command grid (Look/Use/Open/Magic/Hit/Power + directional pad)
COMMON_MULTI_TARGETS =
{'room_label': ['mill_room', 'oldman_house', 'in_forest', 'shaman_house', 'shaman_room'], 'command_area': ['dialogue_active', 'dialogue_cleared', 'look_selected', 'exploration_menu', 'menu_open'], 'battle_command': ['battle_active', 'battle_won', 'battle_magic_menu', 'escape_confirmed', 'cursor_on_auto', 'cursor_on_firebal', 'cursor_on_firebal2', 'cursor_on_stripall'], 'status_command': ['look_target_options', 'item_found', 'shop_menu_open', 'purchase_confirmed', 'dialogue_visible', 'dialogue_initiated', 'dialogue_advanced', 'magic_menu_open', 'teleport_result', 'teleport_dest_cursor', 'teleport_landed', 'cursor_on_look', 'cursor_on_open', 'cursor_on_use', 'cursor_on_magic', 'cursor_on_hit', 'cursor_on_power', 'power_stats_first_page', 'cursor_on_teleport_oldman', 'cursor_on_herb_use', 'cursor_on_keym_use', 'power_stats_last_page'], 'battle_full': ['mistress_first_dialogue', 'mistress_second_dialogue', 'save_prompt_visible', 'save_confirmed', 'look_path_target', 'herb_received', 'treant_defeated', 'key_m_selected', 'gate_opened', 'in_backroom', 'scroll_received', 'grace_selected', 'charm_received']}
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
177class SwordOfHope2Parser(_BaseSwordOfHopeParser): 178 VARIANT = "sword_of_hope_2" 179 180 COMMON_MULTI_TARGETS = { 181 "status_command": [ 182 "dialogue_active", 183 "dialogue_cleared", 184 "dialogue_initiated", 185 "dialogue_advanced", 186 "dialogue_visible", 187 "battle_won", 188 "escape_confirmed", 189 "wheat_purchased", 190 "motion_result", 191 "battle_magic_menu", 192 "cpr_sword_purchased", 193 "wheat_from_tree", 194 "wheat_consumed", 195 "cursor_on_power", 196 "power_stats_first_page", 197 "power_stats_exp_page", 198 "cursor_on_look", 199 "cursor_on_item", 200 "cursor_on_open", 201 "cursor_on_magic", 202 "cursor_on_hit", 203 "cursor_on_motion", 204 "cursor_on_theo", 205 ], 206 "full_screen": [ 207 "shop_menu_open", 208 "magic_menu_open", 209 "weapons_shop_menu_open", 210 "look_shopkeeper", 211 "shop_buy_sell_menu", 212 "cursor_on_cpr_sword", 213 "hit_tree_target", 214 "item_menu_open", 215 "use_menu_open", 216 "cursor_on_wheat", 217 "cursor_on_shop_first_item", 218 "cursor_on_shop_third_item", 219 "cursor_on_first_weapon", 220 "cursor_on_second_weapon", 221 "cursor_on_third_weapon", 222 "look_tree_target", 223 ], 224 "command_area": [ 225 "exploration_menu", 226 "power_menu_last_slide", 227 ], 228 "soh2_room_label": [ 229 "kings_room", 230 "riccar_castle", 231 "temple_1f", 232 "outside_temple", 233 ], 234 "soh2_battle_command": [ 235 "battle_active", 236 "cursor_on_auto", 237 "cursor_on_clash", 238 ], 239 }
Base parser for Sword of Hope variants.
Defines common screen regions for the SoH UI layout:
- Top stats bar (LV, HP, MP)
- Game viewport + Gold/EX
- Status bar (MOVE | ROOM NAME)
- Command grid (Look/Use/Open/Magic/Hit/Power + directional pad)
COMMON_MULTI_TARGETS =
{'status_command': ['dialogue_active', 'dialogue_cleared', 'dialogue_initiated', 'dialogue_advanced', 'dialogue_visible', 'battle_won', 'escape_confirmed', 'wheat_purchased', 'motion_result', 'battle_magic_menu', 'cpr_sword_purchased', 'wheat_from_tree', 'wheat_consumed', 'cursor_on_power', 'power_stats_first_page', 'power_stats_exp_page', 'cursor_on_look', 'cursor_on_item', 'cursor_on_open', 'cursor_on_magic', 'cursor_on_hit', 'cursor_on_motion', 'cursor_on_theo'], 'full_screen': ['shop_menu_open', 'magic_menu_open', 'weapons_shop_menu_open', 'look_shopkeeper', 'shop_buy_sell_menu', 'cursor_on_cpr_sword', 'hit_tree_target', 'item_menu_open', 'use_menu_open', 'cursor_on_wheat', 'cursor_on_shop_first_item', 'cursor_on_shop_third_item', 'cursor_on_first_weapon', 'cursor_on_second_weapon', 'cursor_on_third_weapon', 'look_tree_target'], 'command_area': ['exploration_menu', 'power_menu_last_slide'], 'soh2_room_label': ['kings_room', 'riccar_castle', 'temple_1f', 'outside_temple'], 'soh2_battle_command': ['battle_active', 'cursor_on_auto', 'cursor_on_clash']}
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