diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f228d41d..396aad46 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: - id: trailing-whitespace - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.5.7 + rev: v0.6.1 hooks: - id: ruff args: [--fix] diff --git a/assets/lang/enUS/corrections.json b/assets/lang/enUS/corrections.json index b8a259a7..b51e147a 100644 --- a/assets/lang/enUS/corrections.json +++ b/assets/lang/enUS/corrections.json @@ -34,6 +34,7 @@ "druid", "dungeon affixe", "imprinted", + "monster level", "necromancer", "not useable", "only)", @@ -41,6 +42,7 @@ "properties lost", "requires world tier 3", "requires world tier 4", + "revives allowed", "rogue", "sorcerer", "sorceress" diff --git a/assets/lang/enUS/uniques.json b/assets/lang/enUS/uniques.json index f673b0ab..5ba89326 100644 --- a/assets/lang/enUS/uniques.json +++ b/assets/lang/enUS/uniques.json @@ -60,9 +60,9 @@ ] }, "azurewrath": { - "desc": "lucky hit your skills have up to a chance to freeze enemies for seconds and deal cold damage to them.", + "desc": "lucky hit up to a chance to freeze enemies for seconds and deal cold damage to them", "num_idx": [ - 1 + 2 ] }, "banished_lords_talisman": { diff --git a/assets/templates/item_descr/item_seperator_long_legendary.png b/assets/templates/item_descr/item_seperator_long_legendary.png new file mode 100644 index 00000000..528f6680 Binary files /dev/null and b/assets/templates/item_descr/item_seperator_long_legendary.png differ diff --git a/assets/templates/item_descr/item_seperator_long_mythic.png b/assets/templates/item_descr/item_seperator_long_mythic.png new file mode 100644 index 00000000..e4a61be5 Binary files /dev/null and b/assets/templates/item_descr/item_seperator_long_mythic.png differ diff --git a/assets/templates/item_descr/legendary_bullet_point.png b/assets/templates/item_descr/legendary_bullet_point.png new file mode 100644 index 00000000..900e6825 Binary files /dev/null and b/assets/templates/item_descr/legendary_bullet_point.png differ diff --git a/assets/templates/item_descr/legendary_bullet_point_medium.png b/assets/templates/item_descr/legendary_bullet_point_medium.png new file mode 100644 index 00000000..7032827a Binary files /dev/null and b/assets/templates/item_descr/legendary_bullet_point_medium.png differ diff --git a/src/__init__.py b/src/__init__.py index 9fda0f13..d2334145 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -2,4 +2,4 @@ TP = concurrent.futures.ThreadPoolExecutor() -__version__ = "5.7.5" +__version__ = "5.7.6" diff --git a/src/config/ui.py b/src/config/ui.py index 26f7e979..a43d75ae 100644 --- a/src/config/ui.py +++ b/src/config/ui.py @@ -128,5 +128,5 @@ def set_resolution(self, res: str): if res == self._current_resolution: return self._current_resolution = res - LOGGER.debug(f"Setting ui resolution to {res}") + LOGGER.info(f"Setting ui resolution to {res}") self._offsets, self._pos, self._roi, self._templates = _ResTransformer(resolution=res).fromUHD() diff --git a/src/dataloader.py b/src/dataloader.py index d42a710e..df1aeb75 100644 --- a/src/dataloader.py +++ b/src/dataloader.py @@ -67,6 +67,5 @@ def load_data(self): with open(BASE_DIR / f"assets/lang/{IniConfigLoader().general.language}/uniques.json", encoding="utf-8") as f: data = json.load(f) for key, d in data.items(): - # Note: If you adjust the :45, also adjust it in find_aspect.py - self.aspect_unique_dict[key] = d["desc"][:45] + self.aspect_unique_dict[key] = d["desc"][:60] self.aspect_unique_num_idx[key] = d["num_idx"] diff --git a/src/item/descr/find_affixes.py b/src/item/descr/find_affixes.py index de832ffd..db270e55 100644 --- a/src/item/descr/find_affixes.py +++ b/src/item/descr/find_affixes.py @@ -78,7 +78,7 @@ def find_affixes( # Calc full region of all affixes affix_width = img_width - affix_top_left[0] - affix_height = bottom_limit - affix_top_left[1] - int(line_height * 0.75) + affix_height = bottom_limit - affix_top_left[1] full_affix_region = [*affix_top_left, affix_width, affix_height] crop_full_affix = crop(img_item_descr, full_affix_region) do_pre_proc = not (is_sigil or not do_pre_proc_flag) diff --git a/src/item/descr/find_aspect.py b/src/item/descr/find_aspect.py index afda18b2..7b49e8db 100644 --- a/src/item/descr/find_aspect.py +++ b/src/item/descr/find_aspect.py @@ -23,7 +23,7 @@ def find_aspect(img_item_descr: np.ndarray, aspect_bullet: TemplateMatch, do_pre cleaned_str = clean_str(concatenated_str) # Note: If you adjust the [:45] it also needs to be adapted in the dataloader - cleaned_str = cleaned_str[:45] + cleaned_str = cleaned_str[:60] found_key = closest_match(cleaned_str, Dataloader().aspect_unique_dict) num_idx = Dataloader().aspect_unique_num_idx diff --git a/src/item/descr/read_descr.py b/src/item/descr/read_descr.py index e09ab306..da2659ed 100644 --- a/src/item/descr/read_descr.py +++ b/src/item/descr/read_descr.py @@ -9,7 +9,13 @@ from src.item.descr.find_affixes import find_affixes from src.item.descr.find_aspect import find_aspect from src.item.descr.item_type import read_item_type -from src.item.descr.texture import find_affix_bullets, find_aspect_bullet, find_codex_upgrade_icon, find_seperator_short +from src.item.descr.texture import ( + find_affix_bullets, + find_aspect_bullet, + find_codex_upgrade_icon, + find_seperator_short, + find_seperators_long, +) from src.item.models import Item from src.utils.window import screenshot @@ -19,7 +25,6 @@ def read_descr(rarity: ItemRarity, img_item_descr: np.ndarray, show_warnings: bool = True) -> Item | None: futures = {} base_item = Item(rarity=rarity) - # Find textures for seperator short on top # ========================= sep_short_match = find_seperator_short(img_item_descr) @@ -29,6 +34,7 @@ def read_descr(rarity: ItemRarity, img_item_descr: np.ndarray, show_warnings: bo screenshot("failed_seperator_short", img=img_item_descr) return None + futures["sep_long"] = TP.submit(find_seperators_long, img_item_descr, sep_short_match) # Find item type and item power / tier list # ========================= item, item_type_str = read_item_type(base_item, img_item_descr, sep_short_match, do_pre_proc=False) @@ -46,38 +52,60 @@ def read_descr(rarity: ItemRarity, img_item_descr: np.ndarray, show_warnings: bo ): return item - # Find textures for bullets - # ========================= + is_sigil = item.item_type == ItemType.Sigil + affix_bullets = find_affix_bullets(img_item_descr, sep_short_match) futures["aspect_bullet"] = ( - TP.submit(find_aspect_bullet, img_item_descr, sep_short_match) if rarity in [ItemRarity.Unique, ItemRarity.Mythic] else None + TP.submit(find_aspect_bullet, img_item_descr, sep_short_match) + if rarity in [ItemRarity.Legendary, ItemRarity.Unique, ItemRarity.Mythic] + else None ) - # Split affix bullets into inherent and others - # ========================= - if item.rarity == ItemRarity.Mythic: # TODO should refactor so we either apply some logic OR we look for separator - # Just pick the last 4 matches as affixes - inherent_affix_bullets = affix_bullets[:-4] - affix_bullets = affix_bullets[-4:] - elif item.item_type in [ItemType.ChestArmor, ItemType.Helm, ItemType.Gloves, ItemType.Legs]: - inherent_affix_bullets = [] - elif item.item_type in [ItemType.Ring]: - inherent_affix_bullets = affix_bullets[:2] - affix_bullets = affix_bullets[2:] - elif item.item_type in [ItemType.Sigil]: - inherent_affix_bullets = affix_bullets[:3] - affix_bullets = affix_bullets[3:] - elif item.item_type in [ItemType.Shield]: - inherent_affix_bullets = affix_bullets[:4] - affix_bullets = affix_bullets[4:] - else: - # default for: Amulets, Boots, All Weapons + sep_long_bottom = None + sep_long_match = futures["sep_long"].result() if futures["sep_long"] is not None else None + if is_sigil: inherent_affix_bullets = affix_bullets[:1] affix_bullets = affix_bullets[1:] - - # Find inherent affixes - # ========================= - is_sigil = item.item_type == ItemType.Sigil + else: + if sep_long_match is None: + if show_warnings: + LOGGER.debug("Could not detect item_seperator_long. Fallback to old logic") + # Split affix bullets into inherent and others + # ========================= + if item.rarity == ItemRarity.Mythic: # TODO should refactor so we either apply some logic OR we look for separator + # Just pick the last 4 matches as affixes + inherent_affix_bullets = affix_bullets[:-4] + affix_bullets = affix_bullets[-4:] + elif item.item_type in [ItemType.ChestArmor, ItemType.Helm, ItemType.Gloves, ItemType.Legs]: + inherent_affix_bullets = [] + elif item.item_type in [ItemType.Ring]: + inherent_affix_bullets = affix_bullets[:2] + affix_bullets = affix_bullets[2:] + elif item.item_type in [ItemType.Sigil]: + inherent_affix_bullets = affix_bullets[:3] + affix_bullets = affix_bullets[3:] + elif item.item_type in [ItemType.Shield]: + inherent_affix_bullets = affix_bullets[:4] + affix_bullets = affix_bullets[4:] + else: + # default for: Amulets, Boots, All Weapons + inherent_affix_bullets = affix_bullets[:1] + affix_bullets = affix_bullets[1:] + else: + # check how many affix bullets are below the long separator. if there are more below, then the long separator is the inherent separator. + inherent_sep = (None, 0) + for sep in sep_long_match: + candidate = (sep, len([True for bullet in affix_bullets if bullet.center[1] > sep.center[1]])) + if candidate[1] > inherent_sep[1]: + inherent_sep = candidate + if inherent_sep[0] is None: + number_inherents = 0 + else: + number_inherents = len([True for bullet in affix_bullets if bullet.center[1] < inherent_sep[0].center[1]]) + inherent_affix_bullets = affix_bullets[:number_inherents] + affix_bullets = affix_bullets[number_inherents:] + if len(sep_long_match) == 2: + sep_long_bottom = sep_long_match[1] def _get_inherent(): line_height = ResManager().offsets.item_descr_line_height @@ -109,8 +137,10 @@ def _get_inherent(): # Find normal affixes # ========================= def _get_affixes(): - if affix_bullets: - bottom_limit = affix_bullets[-1].region[1] + affix_bullets[-1].region[3] + ResManager().offsets.item_descr_line_height + if aspect_bullet is not None: + bottom_limit = aspect_bullet.center[1] - int(ResManager().offsets.item_descr_line_height / 2) + elif sep_long_bottom is not None: + bottom_limit = sep_long_bottom.center[1] - ResManager().offsets.item_descr_line_height else: bottom_limit = img_item_descr.shape[0] i_affixes, debug_str = find_affixes( diff --git a/src/item/descr/texture.py b/src/item/descr/texture.py index e927a026..204ad9c1 100644 --- a/src/item/descr/texture.py +++ b/src/item/descr/texture.py @@ -8,6 +8,26 @@ from src.utils.image_operations import color_filter, crop +def find_seperators_long(img_item_descr: np.ndarray, sep_short_match: TemplateMatch) -> list[TemplateMatch]: + refs = ["item_seperator_long_legendary", "item_seperator_long_mythic"] + roi = [0, sep_short_match.center[1], img_item_descr.shape[1], img_item_descr.shape[0] - sep_short_match.center[1]] + if not (sep_long := search(refs, img_item_descr, 0.80, roi, True, mode="all", do_multi_process=False)).success: + return None + matches_dict = {} + for match in sep_long.matches: + match_exists = False + for center in matches_dict: + if math.sqrt((center[0] - match.center[0]) ** 2 + (center[1] - match.center[1]) ** 2) <= 10: + if match.score > matches_dict[center].score: + matches_dict[center] = match + match_exists = True + break + if not match_exists: + matches_dict[match.center] = match + filtered_matches = list(matches_dict.values()) + return sorted(filtered_matches, key=lambda match: match.center[1]) + + def find_seperator_short(img_item_descr: np.ndarray) -> TemplateMatch: refs = ["item_seperator_short_rare", "item_seperator_short_legendary", "item_seperator_short_mythic"] roi = [0, 0, img_item_descr.shape[1], ResManager().offsets.find_seperator_short_offset_top] @@ -76,7 +96,7 @@ def find_affix_bullets(img_item_descr: np.ndarray, sep_short_match: TemplateMatc def find_aspect_bullet(img_item_descr: np.ndarray, sep_short_match: TemplateMatch) -> TemplateMatch | None: - template_list = ["unique_bullet_point", "mythic_bullet_point"] + template_list = ["legendary_bullet_point", "unique_bullet_point", "mythic_bullet_point"] all_templates = [f"{x}_medium" for x in template_list] + template_list if ResManager().resolution == "1920x1080": all_templates += ["mythic_bullet_point_1080p_special", "mythic_bullet_point_medium_1080p_special"] diff --git a/src/item/find_descr.py b/src/item/find_descr.py index 041fe4c1..45e59d01 100644 --- a/src/item/find_descr.py +++ b/src/item/find_descr.py @@ -33,7 +33,7 @@ def _template_search(img: np.ndarray, anchor: int, roi: np.ndarray): roi_copy[0] += anchor ok, roi_left = fit_roi_to_window_size(roi_copy, ResManager().pos.window_dimensions) if ok: - return search(ref=list(map_template_rarity.keys()), inp_img=img, roi=roi_left, threshold=0.8, mode="best") + return search(ref=list(map_template_rarity.keys()), inp_img=img, roi=roi_left, threshold=0.8, mode="all") return SearchResult(success=False) @@ -64,7 +64,7 @@ def find_descr(img: np.ndarray, anchor: tuple[int, int]) -> tuple[bool, ItemRari off_bottom_of_descr = ResManager().offsets.item_descr_off_bottom_edge roi_height = ResManager().pos.window_dimensions[1] - (2 * off_bottom_of_descr) - match.region[1] if ( - res_bottom := search(ref=["item_bottom_edge"], inp_img=img, roi=roi, threshold=0.54, use_grayscale=True, mode="best") + res_bottom := search(ref=["item_bottom_edge"], inp_img=img, roi=roi, threshold=0.54, use_grayscale=True, mode="all") ).success: roi_height = res_bottom.matches[0].center[1] - off_bottom_of_descr - match.region[1] crop_roi = [ diff --git a/src/template_finder.py b/src/template_finder.py index 8bf65db2..bffb25f5 100644 --- a/src/template_finder.py +++ b/src/template_finder.py @@ -180,7 +180,7 @@ def search( :param roi: Region of Interest of the inp_img to restrict search area. Format [left, top, width, height] or string corresponding to a key in Config().ui_roi :param use_grayscale: Use grayscale template matching for speed up :param color_match: Pass a color to be used by misc.color_filter to filter both image of interest and template image (format Config().colors["color"]) or string corresponding to a key in Config().colors - :param mode: search "first" match, "best" match, or "all" matches + :param mode: search "first" match or "all" matches :param timeout: wait for the specified number of seconds before stopping search :param do_multi_process: flag if multi process should be used in case there are multiple refs :return: SearchResult object containing success and matches diff --git a/src/tools/data/custom_uniques_enUS.json b/src/tools/data/custom_uniques_enUS.json index d30bffbe..4eb9f00e 100644 --- a/src/tools/data/custom_uniques_enUS.json +++ b/src/tools/data/custom_uniques_enUS.json @@ -1,7 +1,7 @@ { "azurewrath": { - "desc": "lucky hit your skills have up to a chance to freeze enemies for seconds and deal cold damage to them.", - "num_idx": [1] + "desc": "lucky hit up to a chance to freeze enemies for seconds and deal cold damage to them", + "num_idx": [2] }, "crown_of_lucion": { "desc": "each time you use a skill with a resource Cost, gain increased damage and resource cost is increased by for seconds, stacking up to times.", diff --git a/src/tools/gen_data.py b/src/tools/gen_data.py index 6c880de4..38cfe6d3 100644 --- a/src/tools/gen_data.py +++ b/src/tools/gen_data.py @@ -213,8 +213,6 @@ def main(d4data_dir: Path, companion_app_dir: Path): json_file.write("\n") # Create Affixes - # Its quite the hustle to reconstruct the displayed affixes. So using the companion app parsed json. Generated from here: - # https://github.com/josdemmers/D4DataParser/blob/main/D4DataParser/Parsers/AffixParser.cs print(f"Gen Affixes for {language}") affix_dict = {} with open(companion_app_dir / f"D4Companion/Data/Affixes.Full.{language}.json", encoding="utf-8") as file: diff --git a/tests/assets/item/season5/1080p_small_read_descr_5.png b/tests/assets/item/season5/1080p_small_read_descr_5.png new file mode 100644 index 00000000..517b41ab Binary files /dev/null and b/tests/assets/item/season5/1080p_small_read_descr_5.png differ diff --git a/tests/assets/item/season5/1440p_small_read_descr_2.png b/tests/assets/item/season5/1440p_small_read_descr_2.png new file mode 100644 index 00000000..55f965a5 Binary files /dev/null and b/tests/assets/item/season5/1440p_small_read_descr_2.png differ diff --git a/tests/assets/item/season5/1440p_small_sigil_1.png b/tests/assets/item/season5/1440p_small_sigil_1.png new file mode 100644 index 00000000..b675baae Binary files /dev/null and b/tests/assets/item/season5/1440p_small_sigil_1.png differ diff --git a/tests/assets/item/season5/2160p_small_read_descr_12.png b/tests/assets/item/season5/2160p_small_read_descr_12.png new file mode 100644 index 00000000..a430d3bc Binary files /dev/null and b/tests/assets/item/season5/2160p_small_read_descr_12.png differ diff --git a/tests/assets/item/season5/2160p_small_read_descr_13.png b/tests/assets/item/season5/2160p_small_read_descr_13.png new file mode 100644 index 00000000..9befa512 Binary files /dev/null and b/tests/assets/item/season5/2160p_small_read_descr_13.png differ diff --git a/tests/config/ui_test.py b/tests/config/ui_test.py index 4094dfd0..6a620e07 100644 --- a/tests/config/ui_test.py +++ b/tests/config/ui_test.py @@ -41,4 +41,4 @@ def test_colors(): def test_templates(): - assert len(ResManager().templates) == 46 + assert len(ResManager().templates) == 50 diff --git a/tests/conftest.py b/tests/conftest.py index 83d0c00f..bce1975b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,7 +5,7 @@ from src.config.models import BrowserType -@pytest.fixture() +@pytest.fixture def mock_ini_loader(mocker: MockerFixture): general_mock = mocker.patch.object(IniConfigLoader(), "_general") general_mock.language = "enUS" diff --git a/tests/gui/importer/test_d4builds.py b/tests/gui/importer/test_d4builds.py index fdf28bb9..a4987c45 100644 --- a/tests/gui/importer/test_d4builds.py +++ b/tests/gui/importer/test_d4builds.py @@ -18,7 +18,7 @@ @pytest.mark.parametrize("url", URLS) -@pytest.mark.selenium() +@pytest.mark.selenium def test_import_d4builds(url: str, mock_ini_loader: MockerFixture, mocker: MockerFixture): Dataloader() # need to load data first or the mock will make it impossible mocker.patch("builtins.open", new=mocker.mock_open()) diff --git a/tests/gui/importer/test_diablo_trade.py b/tests/gui/importer/test_diablo_trade.py index a897ce9e..62dd44d5 100644 --- a/tests/gui/importer/test_diablo_trade.py +++ b/tests/gui/importer/test_diablo_trade.py @@ -10,7 +10,7 @@ @pytest.mark.parametrize("url", URLS) -@pytest.mark.requests() +@pytest.mark.requests def test_import_diablo_trade(url: str, mock_ini_loader: MockerFixture, mocker: MockerFixture): Dataloader() # need to load data first or the mock will make it impossible mocker.patch("builtins.open", new=mocker.mock_open()) diff --git a/tests/gui/importer/test_maxroll.py b/tests/gui/importer/test_maxroll.py index 32f4fe95..0c5f0417 100644 --- a/tests/gui/importer/test_maxroll.py +++ b/tests/gui/importer/test_maxroll.py @@ -21,7 +21,7 @@ @pytest.mark.parametrize("url", URLS) -@pytest.mark.requests() +@pytest.mark.requests def test_import_maxroll(url: str, mock_ini_loader: MockerFixture, mocker: MockerFixture): Dataloader() # need to load data first or the mock will make it impossible mocker.patch("builtins.open", new=mocker.mock_open()) diff --git a/tests/gui/importer/test_mobalytics.py b/tests/gui/importer/test_mobalytics.py index dfb65944..65426f22 100644 --- a/tests/gui/importer/test_mobalytics.py +++ b/tests/gui/importer/test_mobalytics.py @@ -24,7 +24,7 @@ @pytest.mark.parametrize("url", URLS) -@pytest.mark.requests() +@pytest.mark.requests def test_import_mobalytics(url: str, mock_ini_loader: MockerFixture, mocker: MockerFixture): Dataloader() # need to load data first or the mock will make it impossible mocker.patch("builtins.open", new=mocker.mock_open()) diff --git a/tests/item/read_descr_season5_test.py b/tests/item/read_descr_season5_test.py index e8061100..ac8a6858 100644 --- a/tests/item/read_descr_season5_test.py +++ b/tests/item/read_descr_season5_test.py @@ -213,6 +213,40 @@ ), ), ((3840, 2160), f"{BASE_PATH}/2160p_small_read_descr_11.png", item11), + ( + (3840, 2160), + f"{BASE_PATH}/2160p_small_read_descr_12.png", + Item( + affixes=[ + Affix(name="all_stats", value=98, type=AffixType.greater), + Affix(name="attack_speed", value=8.5), + Affix(name="cold_resistance", value=55.5), + Affix(name="lucky_hit_up_to_a_chance_to_deal_cold_damage", value=40, type=AffixType.greater), + ], + aspect=Aspect(name="azurewrath", value=23825), + inherent=[Affix(name="cold_damage", value=85, type=AffixType.inherent)], + item_type=ItemType.Sword, + power=925, + rarity=ItemRarity.Unique, + ), + ), + ( + (3840, 2160), + f"{BASE_PATH}/2160p_small_read_descr_13.png", + Item( + affixes=[ + Affix(name="attack_speed", value=11), + Affix(name="fire_and_cold_damage", value=156), + Affix(name="lucky_hit_up_to_a_chance_to_deal_fire_damage", value=40), + Affix(name="lucky_hit_up_to_a_chance_to_deal_cold_damage", value=40, type=AffixType.greater), + ], + aspect=Aspect(name="frostburn", value=58), + inherent=[Affix(name="lucky_hit_chance", value=8, type=AffixType.inherent)], + item_type=ItemType.Gloves, + power=925, + rarity=ItemRarity.Unique, + ), + ), ( (2160, 1440), f"{BASE_PATH}/1440p_small_read_descr_1.png", @@ -230,6 +264,20 @@ rarity=ItemRarity.Unique, ), ), + ( + (3840, 1440), + f"{BASE_PATH}/1440p_small_read_descr_2.png", + Item( + affixes=[ + Affix(name="intelligence", value=43), + Affix(name="maximum_resource", value=7), + Affix(name="lucky_hit_chance", value=4), + ], + item_type=ItemType.Helm, + power=590, + rarity=ItemRarity.Legendary, + ), + ), ( (1920, 1080), f"{BASE_PATH}/1080p_small_read_descr_1.png", @@ -289,7 +337,7 @@ affixes=[ Affix(name="maximum_life", value=1342), Affix(name="life_on_hit", value=39), - Affix(name="lucky_hit_up_to_a_chance_to_restore_primary_resource", value=None), + Affix(name="lucky_hit_up_to_a_chance_to_restore_primary_resource", value=23), ], inherent=[Affix(name="damage_over_time", value=40, type=AffixType.inherent)], item_type=ItemType.Staff, @@ -299,19 +347,18 @@ ), ( (1920, 1080), - f"{BASE_PATH}/1080p_medium_read_descr_2.png", + f"{BASE_PATH}/1080p_small_read_descr_5.png", Item( affixes=[ - Affix(name="movement_speed", value=21.4), - Affix(name="maximum_resistance_to_all_elements", value=9.8), - Affix(name="resistance_to_all_elements", value=78), - Affix(name="damage_reduction", value=36), + Affix(name="intelligence", value=149, type=AffixType.greater), + Affix(name="maximum_life", value=1441, type=AffixType.greater), + Affix(name="to_flame_shield", value=3, type=AffixType.rerolled), + Affix(name="flame_shield_duration", value=17.2, type=AffixType.tempered), + Affix(name="frost_nova_size", value=47.6, type=AffixType.tempered), ], - aspect=Aspect(name="tyraels_might", value=5213), - inherent=[Affix(name="ignore_durability_loss", value=None, type=AffixType.inherent)], item_type=ItemType.ChestArmor, power=925, - rarity=ItemRarity.Mythic, + rarity=ItemRarity.Legendary, ), ), ( @@ -328,6 +375,37 @@ rarity=ItemRarity.Legendary, ), ), + ( + (1920, 1080), + f"{BASE_PATH}/1080p_medium_read_descr_2.png", + Item( + affixes=[ + Affix(name="movement_speed", value=21.4), + Affix(name="maximum_resistance_to_all_elements", value=9.8), + Affix(name="resistance_to_all_elements", value=78), + Affix(name="damage_reduction", value=36), + ], + aspect=Aspect(name="tyraels_might", value=5213), + inherent=[Affix(name="ignore_durability_loss", value=None, type=AffixType.inherent)], + item_type=ItemType.ChestArmor, + power=925, + rarity=ItemRarity.Mythic, + ), + ), +] + +sigil = [ + ( + (2560, 1440), + f"{BASE_PATH}/1440p_small_sigil_1.png", + Item( + affixes=[Affix(name="quick_killer", value=2), Affix(name="empowered_elites_poison_enchanted")], + inherent=[Affix(name="sanguine_chapel", type=AffixType.inherent)], + item_type=ItemType.Sigil, + power=41, + rarity=ItemRarity.Common, + ), + ), ] @@ -341,3 +419,8 @@ def _run_test_helper(img_res: tuple[int, int], input_img: str, expected_item: It @pytest.mark.parametrize(("img_res", "input_img", "expected_item"), items) def test_item(img_res: tuple[int, int], input_img: str, expected_item: Item): _run_test_helper(img_res=img_res, input_img=input_img, expected_item=expected_item) + + +@pytest.mark.parametrize(("img_res", "input_img", "expected_item"), sigil) +def test_sigil(img_res: tuple[int, int], input_img: str, expected_item: Item): + _run_test_helper(img_res=img_res, input_img=input_img, expected_item=expected_item) diff --git a/tests/item/read_descr_unknown_test.py b/tests/item/read_descr_unknown_test.py index 0b3cc693..d12c5e89 100644 --- a/tests/item/read_descr_unknown_test.py +++ b/tests/item/read_descr_unknown_test.py @@ -192,20 +192,6 @@ ), ] -sigil = [ - ( - (2560, 1440), - f"{BASE_PATH}/unknown/read_descr_sigil_1440p_2.png", - Item( - affixes=[Affix(name="extra_shrines"), Affix(name="avenger")], - inherent=[Affix(name="serpents_lair", type=AffixType.inherent)], - item_type=ItemType.Sigil, - power=76, - rarity=ItemRarity.Common, - ), - ), -] - def _run_test_helper(img_res: tuple[int, int], input_img: str, expected_item: Item): Cam().update_window_pos(0, 0, *img_res) @@ -222,8 +208,3 @@ def test_legendary(img_res: tuple[int, int], input_img: str, expected_item: Item @pytest.mark.parametrize(("img_res", "input_img", "expected_item"), material) def test_material(img_res: tuple[int, int], input_img: str, expected_item: Item): _run_test_helper(img_res=img_res, input_img=input_img, expected_item=expected_item) - - -@pytest.mark.parametrize(("img_res", "input_img", "expected_item"), sigil) -def test_sigil(img_res: tuple[int, int], input_img: str, expected_item: Item): - _run_test_helper(img_res=img_res, input_img=input_img, expected_item=expected_item) diff --git a/tests/template_finder_test.py b/tests/template_finder_test.py index 25627ecb..0c1a6dec 100644 --- a/tests/template_finder_test.py +++ b/tests/template_finder_test.py @@ -31,7 +31,7 @@ def test_search_best_match(): slash = cv2.imread("tests/assets/template_finder/stash_slot_slash.png") cross = cv2.imread("tests/assets/template_finder/stash_slot_cross.png") slash_expected_roi = [38, 0, 38, 38] - result = src.template_finder.search([cross, slash], image, threshold=0.6, mode="best") + result = src.template_finder.search([cross, slash], image, threshold=0.6, mode="all") match = result.matches[0] assert is_in_roi(slash_expected_roi, match.center) diff --git a/tests/utils/image_operations_test.py b/tests/utils/image_operations_test.py index 5d8ab413..c81f9fdc 100644 --- a/tests/utils/image_operations_test.py +++ b/tests/utils/image_operations_test.py @@ -102,12 +102,12 @@ def test_create_mask(): assert np.count_nonzero(mask) == 100 - 36 -@pytest.fixture() +@pytest.fixture def filter_img(): return np.random.randint(0, 255, (100, 100, 3), dtype=np.uint8) -@pytest.fixture() +@pytest.fixture def color_range(): return [np.array([0, 0, 0]), np.array([180, 255, 255])]