Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisoro authored Aug 19, 2024
2 parents 87991b7 + 1ff904e commit d459cad
Show file tree
Hide file tree
Showing 33 changed files with 197 additions and 84 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
2 changes: 2 additions & 0 deletions assets/lang/enUS/corrections.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@
"druid",
"dungeon affixe",
"imprinted",
"monster level",
"necromancer",
"not useable",
"only)",
"operties lost",
"properties lost",
"requires world tier 3",
"requires world tier 4",
"revives allowed",
"rogue",
"sorcerer",
"sorceress"
Expand Down
4 changes: 2 additions & 2 deletions assets/lang/enUS/uniques.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

TP = concurrent.futures.ThreadPoolExecutor()

__version__ = "5.7.5"
__version__ = "5.7.6"
2 changes: 1 addition & 1 deletion src/config/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
3 changes: 1 addition & 2 deletions src/dataloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
2 changes: 1 addition & 1 deletion src/item/descr/find_affixes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/item/descr/find_aspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
90 changes: 60 additions & 30 deletions src/item/descr/read_descr.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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(
Expand Down
22 changes: 21 additions & 1 deletion src/item/descr/texture.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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"]
Expand Down
4 changes: 2 additions & 2 deletions src/item/find_descr.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)


Expand Down Expand Up @@ -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 = [
Expand Down
2 changes: 1 addition & 1 deletion src/template_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/tools/data/custom_uniques_enUS.json
Original file line number Diff line number Diff line change
@@ -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.",
Expand Down
2 changes: 0 additions & 2 deletions src/tools/gen_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion tests/config/ui_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ def test_colors():


def test_templates():
assert len(ResManager().templates) == 46
assert len(ResManager().templates) == 50
2 changes: 1 addition & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion tests/gui/importer/test_d4builds.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
2 changes: 1 addition & 1 deletion tests/gui/importer/test_diablo_trade.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
2 changes: 1 addition & 1 deletion tests/gui/importer/test_maxroll.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
2 changes: 1 addition & 1 deletion tests/gui/importer/test_mobalytics.py
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
Loading

0 comments on commit d459cad

Please sign in to comment.