From 27ebd1b4dbe1d3b96d3f9d27e666a7244edb7bcf Mon Sep 17 00:00:00 2001 From: mgleed Date: Wed, 18 May 2022 01:35:27 -0400 Subject: [PATCH] Refactor: TemplateFinder() (#770) * init, wip * time to test * remove unused dataclass * tests passing, just need to run in-game * fix wait logic * add threading lock, delete excess funcs * start putting best_match back in to search_and_wait() * bugfixes, found a bad template * scope * fix npc detect --- config/game.ini | 2 +- src/bot.py | 4 +- src/char/barbarian.py | 2 +- src/char/basic.py | 2 +- src/char/basic_ranged.py | 4 +- src/char/bone_necro.py | 2 +- src/char/hammerdin.py | 2 +- src/char/i_char.py | 15 +- src/char/necro.py | 50 ++- src/char/poison_necro.py | 14 +- src/char/sorceress/blizz_sorc.py | 10 +- src/char/sorceress/sorceress.py | 6 +- src/char/trapsin.py | 2 +- src/chest.py | 14 +- src/inventory/belt.py | 4 +- src/inventory/common.py | 6 +- src/inventory/consumables.py | 9 +- src/inventory/personal.py | 8 +- src/inventory/stash.py | 2 +- src/inventory/vendor.py | 14 +- src/item/item_cropper.py | 4 +- src/item/item_finder.py | 2 +- src/item/pickit.py | 2 +- src/npc_manager.py | 152 +++++----- src/pather.py | 8 +- src/run/arcane.py | 8 +- src/run/diablo.py | 28 +- src/run/nihlathak.py | 10 +- src/run/pindle.py | 4 +- src/run/shenk_eld.py | 4 +- src/run/trav.py | 4 +- src/shop/anya.py | 44 ++- src/shop/drognan.py | 15 +- src/template_finder.py | 419 +++++++++++++------------- src/town/a1.py | 6 +- src/town/a2.py | 4 +- src/town/a3.py | 4 +- src/town/a4.py | 4 +- src/town/a5.py | 4 +- src/town/town_manager.py | 6 +- src/transmute/inventory_collection.py | 4 +- src/transmute/transmute.py | 6 +- src/ui/character_select.py | 9 +- src/ui/skills.py | 6 +- src/ui/view.py | 2 +- src/ui_manager.py | 30 +- src/utils/custom_mouse.py | 4 +- src/utils/graphic_debugger.py | 6 +- src/utils/misc.py | 9 +- src/utils/node_recorder.py | 12 +- src/utils/restart.py | 4 +- test/smoke_test.py | 2 +- test/template_finder_test.py | 17 +- test/utils/misc_test.py | 2 +- 54 files changed, 497 insertions(+), 520 deletions(-) diff --git a/config/game.ini b/config/game.ini index 2b10a49..df8abbc 100644 --- a/config/game.ini +++ b/config/game.ini @@ -101,7 +101,7 @@ reduce_to_center=120,60,1040,540 search_npcs=120,0,1040,620 merc_icon=0,0,100,100 loading_left_black=0,0,350,720 -death=513,193,253,50 +death=444,198,397,71 tp_search=353,120,547,400 repair_btn=318,473,90,80 left_inventory=35,92,378,378 diff --git a/src/bot.py b/src/bot.py index 8b90823..ba6b0c3 100644 --- a/src/bot.py +++ b/src/bot.py @@ -17,7 +17,7 @@ from game_stats import GameStats from logger import Logger from config import Config from screen import grab -from template_finder import TemplateFinder +import template_finder from char import IChar from item import ItemFinder from item.pickit import PickIt @@ -210,7 +210,7 @@ class Bot: "select_character": Bot._MAIN_MENU_MARKERS, "start_from_town": town_manager.TOWN_MARKERS, }) - match = TemplateFinder().search_and_wait(list(transition_to_screens.keys()), best_match=True) + match = template_finder.search_and_wait(list(transition_to_screens.keys()), best_match=True) self.trigger_or_stop(transition_to_screens[match.name]) def on_select_character(self): diff --git a/src/char/barbarian.py b/src/char/barbarian.py index 954d5b6..50f3f03 100644 --- a/src/char/barbarian.py +++ b/src/char/barbarian.py @@ -2,7 +2,7 @@ import keyboard from ui import skills from utils.custom_mouse import mouse from char import IChar, CharacterCapabilities -from template_finder import TemplateFinder +import template_finder from pather import Pather from logger import Logger from screen import convert_abs_to_monitor diff --git a/src/char/basic.py b/src/char/basic.py index 792d843..6703094 100644 --- a/src/char/basic.py +++ b/src/char/basic.py @@ -2,7 +2,7 @@ import keyboard from ui import skills from utils.custom_mouse import mouse from char import IChar,CharacterCapabilities -from template_finder import TemplateFinder +import template_finder from pather import Pather from logger import Logger from screen import convert_abs_to_monitor diff --git a/src/char/basic_ranged.py b/src/char/basic_ranged.py index 2f7bfae..50215ad 100644 --- a/src/char/basic_ranged.py +++ b/src/char/basic_ranged.py @@ -2,7 +2,7 @@ import keyboard from ui import skills from utils.custom_mouse import mouse from char import IChar -from template_finder import TemplateFinder +import template_finder from pather import Pather from logger import Logger from screen import convert_abs_to_monitor, convert_screen_to_abs, grab @@ -192,7 +192,7 @@ class Basic_Ranged(IChar): if __name__ == "__main__": import os import keyboard - from template_finder import TemplateFinder + import template_finder from pather import Pather keyboard.add_hotkey('f12', lambda: Logger.info('Force Exit (f12)') or os._exit(1)) keyboard.wait("f11") diff --git a/src/char/bone_necro.py b/src/char/bone_necro.py index 4586bc3..6186b1e 100644 --- a/src/char/bone_necro.py +++ b/src/char/bone_necro.py @@ -1,7 +1,7 @@ import keyboard from utils.custom_mouse import mouse from char import IChar -from template_finder import TemplateFinder +import template_finder from pather import Pather from logger import Logger from screen import grab, convert_abs_to_monitor, convert_screen_to_abs diff --git a/src/char/hammerdin.py b/src/char/hammerdin.py index 2e0c105..ee32fec 100644 --- a/src/char/hammerdin.py +++ b/src/char/hammerdin.py @@ -2,7 +2,7 @@ import keyboard from ui import skills from utils.custom_mouse import mouse from char import IChar, CharacterCapabilities -from template_finder import TemplateFinder +import template_finder from pather import Pather from logger import Logger from screen import convert_abs_to_monitor diff --git a/src/char/i_char.py b/src/char/i_char.py index 19c3e55..b9c286b 100644 --- a/src/char/i_char.py +++ b/src/char/i_char.py @@ -14,7 +14,7 @@ from utils.misc import wait, cut_roi, is_in_roi, color_filter, arc_spread from logger import Logger from config import Config from screen import grab, convert_monitor_to_screen, convert_screen_to_abs, convert_abs_to_monitor, convert_screen_to_monitor -from template_finder import TemplateFinder +import template_finder from ocr import Ocr from ui_manager import detect_screen_object, ScreenObjects @@ -88,10 +88,10 @@ class IChar: keyboard.send("esc") start = time.time() while timeout is None or (time.time() - start) < timeout: - template_match = TemplateFinder().search(template_type, grab(), threshold=threshold, normalize_monitor=True) + template_match = template_finder.search(template_type, grab(), threshold=threshold) if template_match.valid: Logger.debug(f"Select {template_match.name} ({template_match.score*100:.1f}% confidence)") - mouse.move(*template_match.center) + mouse.move(*template_match.center_monitor) wait(0.2, 0.3) mouse.click(button="left") # check the successfunction for 2 sec, if not found, try again @@ -129,10 +129,9 @@ class IChar: mouse.move(x + w/2, y + h / 2) mouse.click("left") wait(0.3) - match = TemplateFinder().search(skill_asset, grab(), threshold=0.84, roi=expanded_skill_roi) + match = template_finder.search(skill_asset, grab(), threshold=0.84, roi=expanded_skill_roi) if match.valid: - x, y = convert_screen_to_monitor(match.center) - mouse.move(x, y) + mouse.move(*match.center_monitor) wait(0.3) keyboard.send(hotkey) wait(0.3) @@ -222,7 +221,7 @@ class IChar: consumables.increment_need("tp", 1) wait(0.8, 1.3) # takes quite a while for tp to be visible if (template_match := detect_screen_object(ScreenObjects.TownPortal)).valid: - pos = template_match.center + pos = template_match.center_monitor pos = (pos[0], pos[1] + 30) # Note: Template is top of portal, thus move the y-position a bit to the bottom mouse.move(*pos, randomize=6, delay_factor=[0.9, 1.1]) @@ -357,7 +356,7 @@ if __name__ == "__main__": keyboard.wait("f11") from utils.misc import cut_roi from config import Config - from template_finder import TemplateFinder + import template_finder from ocr import Ocr from ui import skills diff --git a/src/char/necro.py b/src/char/necro.py index 7080f94..cb996c3 100644 --- a/src/char/necro.py +++ b/src/char/necro.py @@ -1,7 +1,8 @@ import keyboard from utils.custom_mouse import mouse from char import IChar -from template_finder import TemplateFinder +import template_finder +from template_finder import TemplateMatch from pather import Pather from logger import Logger from screen import grab, convert_abs_to_monitor, convert_screen_to_abs @@ -53,7 +54,7 @@ class Necro(IChar): roi = [640,0,640,720] img = grab() - template_match = TemplateFinder().search( + template_match = template_finder.search( ['SHENK_DEATH_1','SHENK_DEATH_2','SHENK_DEATH_3','SHENK_DEATH_4'], img, threshold=0.6, @@ -71,7 +72,7 @@ class Necro(IChar): img = grab() max_rev = 13 - template_match = TemplateFinder().search( + template_match = template_finder.search( ['REV_BASE'], img, threshold=0.6, @@ -85,7 +86,7 @@ class Necro(IChar): for count in range(1,max_rev): rev_num = "REV_"+str(count) - template_match = TemplateFinder().search( + template_match = template_finder.search( [rev_num], img, threshold=0.66, @@ -100,7 +101,7 @@ class Necro(IChar): img = grab() max_skeles = 13 - template_match = TemplateFinder().search( + template_match = template_finder.search( ['SKELE_BASE'], img, threshold=0.6, @@ -114,7 +115,7 @@ class Necro(IChar): for count in range(1,max_skeles): skele_num = "SKELE_"+str(count) - template_match = TemplateFinder().search( + template_match = template_finder.search( [skele_num], img, threshold=0.66, @@ -128,7 +129,7 @@ class Necro(IChar): roi = [15,14,400,45] img = grab() - template_match = TemplateFinder().search( + template_match = template_finder.search( ['CLAY'], img, threshold=0.6, @@ -583,15 +584,14 @@ class Necro(IChar): def stairs_S(self): roi = [0,0,1280,720] img = grab() - template_match = TemplateFinder().search( + template_match = template_finder.search( ["TRAV_S","TRAV_S_1"], img, threshold=0.4, - roi=roi, - normalize_monitor=True + roi=roi ) if template_match.valid: - pos = template_match.center + pos = template_match.center_monitor pos = (pos[0], pos[1] ) Logger.debug("mid point >> "+str(pos)) @@ -607,15 +607,14 @@ class Necro(IChar): def stairs_F(self): roi = [0,0,1280,720] img = grab() - template_match = TemplateFinder().search( + template_match = template_finder.search( ["TRAV_F"], img, threshold=0.4, - roi=roi, - normalize_monitor=True + roi=roi ) if template_match.valid: - pos = template_match.center + pos = template_match.center_monitor pos = (pos[0], pos[1] ) Logger.debug("mid point >> "+str(pos)) @@ -632,15 +631,14 @@ class Necro(IChar): def stairs_W(self): roi = [0,0,1280,720] img = grab() - template_match = TemplateFinder().search( + template_match = template_finder.search( ["TRAV_W","TRAV_W_1"], img, threshold=0.4, - roi=roi, - normalize_monitor=True + roi=roi ) if template_match.valid: - pos = template_match.center + pos = template_match.center_monitor pos = (pos[0], pos[1] ) Logger.debug("mid point >> "+str(pos)) @@ -661,15 +659,14 @@ class Necro(IChar): roi = [0,0,1280,720] img = grab() - template_match = TemplateFinder().search( + template_match = template_finder.search( ["TRAV_18"], img, threshold=0.3, - roi=roi, - normalize_monitor=True + roi=roi ) if template_match.valid: - pos = template_match.center + pos = template_match.center_monitor pos = (pos[0], pos[1] ) Logger.debug("DURANCE ENTRANCE >> "+str(pos)) # Note: Template is top of portal, thus move the y-position a bit to the bottom @@ -699,15 +696,14 @@ class Necro(IChar): mouse.move(*target, randomize=6, delay_factor=[0.9, 1.1]) img = grab() - template_match = TemplateFinder().search( + template_match = template_finder.search( ["TO_TRAV_0"], img, threshold=0.95, - roi=roi, - normalize_monitor=True + roi=roi ) if template_match.valid: - pos = template_match.center + pos = template_match.center_monitor pos = (pos[0], pos[1] ) Logger.debug("DURANCE EXIT >> "+str(pos)) # Note: Template is top of portal, thus move the y-position a bit to the bottom diff --git a/src/char/poison_necro.py b/src/char/poison_necro.py index 841d6c7..c92192a 100644 --- a/src/char/poison_necro.py +++ b/src/char/poison_necro.py @@ -1,7 +1,7 @@ import keyboard from utils.custom_mouse import mouse from char import IChar -from template_finder import TemplateFinder +import template_finder from pather import Pather from logger import Logger from screen import grab, convert_abs_to_monitor, convert_screen_to_abs @@ -49,7 +49,7 @@ class Poison_Necro(IChar): roi = [640,0,640,720] img = grab() - template_match = TemplateFinder().search( + template_match = template_finder.search( ['SHENK_DEATH_1','SHENK_DEATH_2','SHENK_DEATH_3','SHENK_DEATH_4'], img, threshold=0.6, @@ -67,7 +67,7 @@ class Poison_Necro(IChar): img = grab() max_rev = 13 - template_match = TemplateFinder().search( + template_match = template_finder.search( ['REV_BASE'], img, threshold=0.6, @@ -81,7 +81,7 @@ class Poison_Necro(IChar): for count in range(1,max_rev): rev_num = "REV_"+str(count) - template_match = TemplateFinder().search( + template_match = template_finder.search( [rev_num], img, threshold=0.66, @@ -109,7 +109,7 @@ class Poison_Necro(IChar): img = grab() max_skeles = 13 - template_match = TemplateFinder().search( + template_match = template_finder.search( ['SKELE_BASE'], img, threshold=0.6, @@ -123,7 +123,7 @@ class Poison_Necro(IChar): for count in range(1,max_skeles): skele_num = "SKELE_"+str(count) - template_match = TemplateFinder().search( + template_match = template_finder.search( [skele_num], img, threshold=0.66, @@ -137,7 +137,7 @@ class Poison_Necro(IChar): roi = [15,14,400,45] img = grab() - template_match = TemplateFinder().search( + template_match = template_finder.search( ['CLAY'], img, threshold=0.6, diff --git a/src/char/sorceress/blizz_sorc.py b/src/char/sorceress/blizz_sorc.py index 460ca47..39cc7bd 100644 --- a/src/char/sorceress/blizz_sorc.py +++ b/src/char/sorceress/blizz_sorc.py @@ -8,7 +8,7 @@ from pather import Location import numpy as np from screen import convert_abs_to_monitor, grab, convert_screen_to_abs from config import Config -from template_finder import TemplateFinder +import template_finder class BlizzSorc(Sorceress): def __init__(self, *args, **kwargs): @@ -73,7 +73,7 @@ class BlizzSorc(Sorceress): self.move(pos_m, force_move=True) self._blizzard((-50, -50), spray=10) self._cast_static() - wait(0.75) + wait(0.75) #move down pos_m = convert_abs_to_monitor((0, 85)) self.pre_move() @@ -228,8 +228,8 @@ class BlizzSorc(Sorceress): wait(0.5) self._blizzard(cast_pos_abs, spray=0) wait(0.2) - is_nihl = TemplateFinder().search(["NIHL_BAR"], grab(), threshold=0.8, roi=Config().ui_roi["enemy_info"]).valid - nihl_immune = TemplateFinder().search(["COLD_IMMUNE","COLD_IMMUNES"], grab(), threshold=0.8, roi=Config().ui_roi["enemy_info"]).valid + is_nihl = template_finder.search(["NIHL_BAR"], grab(), threshold=0.8, roi=Config().ui_roi["enemy_info"]).valid + nihl_immune = template_finder.search(["COLD_IMMUNE","COLD_IMMUNES"], grab(), threshold=0.8, roi=Config().ui_roi["enemy_info"]).valid if is_nihl: Logger.info("Found him!") if nihl_immune: @@ -257,7 +257,7 @@ class BlizzSorc(Sorceress): if __name__ == "__main__": import os import keyboard - from template_finder import TemplateFinder + import template_finder from pather import Pather keyboard.add_hotkey('f12', lambda: Logger.info('Force Exit (f12)') or os._exit(1)) keyboard.wait("f11") diff --git a/src/char/sorceress/sorceress.py b/src/char/sorceress/sorceress.py index 7993e89..abedb97 100644 --- a/src/char/sorceress/sorceress.py +++ b/src/char/sorceress/sorceress.py @@ -2,7 +2,7 @@ import keyboard from typing import Tuple, Union, List, Callable from utils.custom_mouse import mouse from char import IChar -from template_finder import TemplateFinder +import template_finder from pather import Pather from screen import grab from utils.misc import wait @@ -51,11 +51,11 @@ class Sorceress(IChar): keyboard.send("esc") start = time.time() while timeout is None or (time.time() - start) < timeout: - template_match = TemplateFinder().search(template_type, grab(), threshold=threshold, normalize_monitor=True) + template_match = template_finder.search(template_type, grab(), threshold=threshold) if template_match.valid: keyboard.send(self._skill_hotkeys["telekinesis"]) wait(0.1, 0.2) - mouse.move(*template_match.center) + mouse.move(*template_match.center_monitor) wait(0.2, 0.3) mouse.click(button="right") # check the successfunction for 2 sec, if not found, try again diff --git a/src/char/trapsin.py b/src/char/trapsin.py index 2fa1fe4..288d575 100644 --- a/src/char/trapsin.py +++ b/src/char/trapsin.py @@ -1,7 +1,7 @@ import keyboard from utils.custom_mouse import mouse from char import IChar -from template_finder import TemplateFinder +import template_finder from pather import Pather from logger import Logger from screen import convert_abs_to_monitor, convert_screen_to_abs, grab diff --git a/src/chest.py b/src/chest.py index c54cc69..f1a5e2b 100644 --- a/src/chest.py +++ b/src/chest.py @@ -2,7 +2,7 @@ import time import os from logger import Logger -from template_finder import TemplateFinder +import template_finder from screen import grab from char import IChar from config import Config @@ -29,7 +29,7 @@ class Chest: found_chest = True start = time.time() while time.time() - start < timeout: - template_match = TemplateFinder().search(templates, grab(), roi=Config().ui_roi["reduce_to_center"], threshold=threshold, use_grayscale=True, best_match=True, normalize_monitor=True) + template_match = template_finder.search(templates, grab(), roi=Config().ui_roi["reduce_to_center"], threshold=threshold, use_grayscale=True, best_match=True) # search for at least 1.5 second, if no chest found, break if not template_match.valid: if time.time() - start > 1.5: @@ -37,19 +37,19 @@ class Chest: else: found_chest = True # move mouse and check for label - mouse.move(*template_match.center, delay_factor=[0.4, 0.6]) + mouse.move(*template_match.center_monitor, delay_factor=[0.4, 0.6]) wait(0.13, 0.16) chest_label_img = grab() - chest_label = TemplateFinder().search("CHEST_LABEL", chest_label_img, threshold=0.85) - is_locked = TemplateFinder().search("LOCKED", chest_label_img, threshold=0.85).valid + chest_label = template_finder.search("CHEST_LABEL", chest_label_img, threshold=0.85) + is_locked = template_finder.search("LOCKED", chest_label_img, threshold=0.85).valid if chest_label.valid: if is_locked: consumables.increment_need("key", 1) Logger.debug(f"Opening {template_match.name} ({template_match.score*100:.1f}% confidence)") # TODO: Act as picking up a potion to support telekinesis. This workaround needs a proper solution. - self._char.pick_up_item(template_match.center, 'potion') + self._char.pick_up_item(template_match.center_monitor, 'potion') wait(0.13, 0.16) - if TemplateFinder().search("LOCKED", grab(), threshold=0.85).valid: + if template_finder.search("LOCKED", grab(), threshold=0.85).valid: templates.remove(template_match.name) Logger.debug("No more keys, removing locked chest template") continue diff --git a/src/inventory/belt.py b/src/inventory/belt.py index 6c9f8b7..4957a33 100644 --- a/src/inventory/belt.py +++ b/src/inventory/belt.py @@ -2,7 +2,7 @@ import itertools from logger import Logger from typing import List import numpy as np -from template_finder import TemplateFinder +import template_finder from inventory import common, consumables, personal from ui import view from ui_manager import is_visible, wait_until_visible, ScreenObjects, wait_until_hidden @@ -151,7 +151,7 @@ def fill_up_belt_from_inventory(num_loot_columns: int): pot_positions = [] for column, row in itertools.product(range(num_loot_columns), range(4)): center_pos, slot_img = common.get_slot_pos_and_img(img, column, row) - found = TemplateFinder().search(["GREATER_HEALING_POTION", "GREATER_MANA_POTION", "SUPER_HEALING_POTION", "SUPER_MANA_POTION", "FULL_REJUV_POTION", "REJUV_POTION"], slot_img, threshold=0.9).valid + found = template_finder.search(["GREATER_HEALING_POTION", "GREATER_MANA_POTION", "SUPER_HEALING_POTION", "SUPER_MANA_POTION", "FULL_REJUV_POTION", "REJUV_POTION"], slot_img, threshold=0.9).valid if found: pot_positions.append(center_pos) keyboard.press("shift") diff --git a/src/inventory/common.py b/src/inventory/common.py index 93fbeff..265b792 100644 --- a/src/inventory/common.py +++ b/src/inventory/common.py @@ -4,7 +4,7 @@ import numpy as np import keyboard import time from utils.custom_mouse import mouse -from template_finder import TemplateFinder +import template_finder from ui_manager import detect_screen_object, ScreenObjects, is_visible, wait_until_hidden from utils.misc import wait, trim_black, color_filter, cut_roi from inventory import consumables @@ -80,12 +80,12 @@ def calc_item_roi(img_pre, img_post): def tome_state(img: np.ndarray = None, tome_type: str = "tp", roi: list = None): img = img if img is not None else grab() - if (tome_found := TemplateFinder().search([f"{tome_type.upper()}_TOME", f"{tome_type.upper()}_TOME_RED"], img, roi = roi, threshold = 0.8, best_match = True, normalize_monitor = True)).valid: + if (tome_found := template_finder.search([f"{tome_type.upper()}_TOME", f"{tome_type.upper()}_TOME_RED"], img, roi = roi, threshold = 0.8, best_match = True)).valid: if tome_found.name == f"{tome_type.upper()}_TOME": state = "ok" else: state = "empty" - position = tome_found.center + position = tome_found.center_monitor else: state = position = None return state, position diff --git a/src/inventory/consumables.py b/src/inventory/consumables.py index 731659f..5e3b84d 100644 --- a/src/inventory/consumables.py +++ b/src/inventory/consumables.py @@ -4,7 +4,7 @@ import numpy as np import time import parse from utils.custom_mouse import mouse -from template_finder import TemplateFinder +import template_finder from inventory import personal from utils.misc import wait from screen import grab @@ -106,12 +106,11 @@ def should_buy(item_name: str = None, min_remaining: int = None, min_needed: int def update_tome_key_needs(img: np.ndarray = None, item_type: str = "tp") -> bool: img = personal.open(img) if item_type.lower() in ["tp", "id"]: - match = TemplateFinder().search( + match = template_finder.search( [f"{item_type.upper()}_TOME", f"{item_type.upper()}_TOME_RED"], img, roi = Config().ui_roi["restricted_inventory_area"], best_match = True, - normalize_monitor=True ) if match.valid: if match.name == f"{item_type.upper()}_TOME_RED": @@ -122,13 +121,13 @@ def update_tome_key_needs(img: np.ndarray = None, item_type: str = "tp") -> bool Logger.debug(f"update_tome_key_needs: could not find {item_type}") return False elif item_type.lower() in ["key"]: - match = TemplateFinder().search("INV_KEY", img, roi = Config().ui_roi["restricted_inventory_area"], normalize_monitor = True) + match = template_finder.search("INV_KEY", img, roi = Config().ui_roi["restricted_inventory_area"]) if not match.valid: return False else: Logger.error(f"update_tome_key_needs failed, item_type: {item_type} not supported") return False - mouse.move(*match.center, randomize=4, delay_factor=[0.5, 0.7]) + mouse.move(*match.center_monitor, randomize=4, delay_factor=[0.5, 0.7]) wait(0.2, 0.2) hovered_item = grab() # get the item description box diff --git a/src/inventory/personal.py b/src/inventory/personal.py index 204bce8..222ab38 100644 --- a/src/inventory/personal.py +++ b/src/inventory/personal.py @@ -10,7 +10,7 @@ import time import numpy as np from dataclasses import dataclass -from template_finder import TemplateFinder +import template_finder from config import Config from utils.misc import wait, is_in_roi, mask_by_roi from utils.custom_mouse import mouse @@ -194,7 +194,7 @@ def keep_item(item_box: ItemText = None, found_item: Item = None, do_logging: bo found_subprops=[] for subprop in prop: try: - template_match = TemplateFinder().search(subprop, img, threshold=0.95) + template_match = template_finder.search(subprop, img, threshold=0.95) except: Logger.error(f"{found_item.name}: can't find template file for required {prop}, ignore just in case") template_match = lambda: None; template_match.valid = True @@ -212,7 +212,7 @@ def keep_item(item_box: ItemText = None, found_item: Item = None, do_logging: bo break else: try: - template_match = TemplateFinder().search(prop, img, threshold=0.95) + template_match = template_finder.search(prop, img, threshold=0.95) except: Logger.error(f"{found_item.name}: can't find template file for required {prop}, ignore just in case") template_match = lambda: None; template_match.valid = True @@ -236,7 +236,7 @@ def keep_item(item_box: ItemText = None, found_item: Item = None, do_logging: bo found_props = [] for prop in exclude_props: try: - template_match = TemplateFinder().search(prop, img, threshold=0.97) + template_match = template_finder.search(prop, img, threshold=0.97) except: Logger.error(f"{found_item.name}: can't find template file for exclusion {prop}, ignore just in case") template_match = lambda: None; template_match.valid = False diff --git a/src/inventory/stash.py b/src/inventory/stash.py index 69b77c1..f0247d1 100644 --- a/src/inventory/stash.py +++ b/src/inventory/stash.py @@ -1,4 +1,4 @@ -from template_finder import TemplateFinder +import template_finder from screen import grab, convert_screen_to_monitor from config import Config import os diff --git a/src/inventory/vendor.py b/src/inventory/vendor.py index 257bf0b..0f92c13 100644 --- a/src/inventory/vendor.py +++ b/src/inventory/vendor.py @@ -1,6 +1,6 @@ from math import floor import keyboard -from template_finder import TemplateFinder +import template_finder from config import Config import numpy as np from utils.misc import wait @@ -49,20 +49,20 @@ def repair() -> bool: return True def gamble(): - if (refresh_btn := TemplateFinder().search_and_wait("REFRESH", threshold=0.79, timeout=4, normalize_monitor=True)).valid: + if (refresh_btn := template_finder.search_and_wait("REFRESH", threshold=0.79, timeout=4)).valid: #Gambling window is open. Starting to spent some coins max_gamble_count = floor(2000000/188000) # leave about 500k gold and assume buying coronets at ~188k while get_gamble_status() and get_gamble_count() < max_gamble_count: img=grab() for item in Config().char["gamble_items"]: # while desired gamble item is not on screen, refresh - while not (desired_item := TemplateFinder().search (item.upper(), grab(), roi=Config().ui_roi["left_inventory"], normalize_monitor=True)).valid: - mouse.move(*refresh_btn.center, randomize=12, delay_factor=[1.0, 1.5]) + while not (desired_item := template_finder.search (item.upper(), grab(), roi=Config().ui_roi["left_inventory"])).valid: + mouse.move(*refresh_btn.center_monitor, randomize=12, delay_factor=[1.0, 1.5]) wait(0.1, 0.15) mouse.click(button="left") wait(0.1, 0.15) # desired item found, purchase it - mouse.move(*desired_item.center, randomize=12, delay_factor=[1.0, 1.5]) + mouse.move(*desired_item.center_monitor, randomize=12, delay_factor=[1.0, 1.5]) wait(0.1, 0.15) mouse.click(button="right") wait(0.4, 0.6) @@ -109,8 +109,8 @@ def buy_item(template_name: str, quantity: int = 1, img: np.ndarray = None, shif """ if img is None: img = grab() - if (desired_item := TemplateFinder().search(template_name, inp_img=img, roi=Config().ui_roi["left_inventory"], normalize_monitor=True)).valid: - mouse.move(*desired_item.center, randomize=8, delay_factor=[1.0, 1.5]) + if (desired_item := template_finder.search(template_name, inp_img=img, roi=Config().ui_roi["left_inventory"])).valid: + mouse.move(*desired_item.center_monitor, randomize=8, delay_factor=[1.0, 1.5]) if shift_click: keyboard.send('shift', do_release=False) wait(0.5, 0.8) diff --git a/src/item/item_cropper.py b/src/item/item_cropper.py index 909dd0a..f2f9cf1 100644 --- a/src/item/item_cropper.py +++ b/src/item/item_cropper.py @@ -4,7 +4,7 @@ from dataclasses import dataclass import time from utils.misc import color_filter, erode_to_black -from template_finder import TemplateFinder +import template_finder from ocr import Ocr, OcrResult from config import Config from logger import Logger @@ -126,7 +126,7 @@ class ItemCropper: overlaps_inventory = False if (x+w 720 else 35 - found_footer = TemplateFinder().search(["TO_TOOLTIP"], inp_img, threshold=0.8, roi=[x, y+h, w, footer_height_max]).valid + found_footer = template_finder.search(["TO_TOOLTIP"], inp_img, threshold=0.8, roi=[x, y+h, w, footer_height_max]).valid if found_footer: ocr_result = self._ocr.image_to_text(cropped_item, psm=6, model=model)[0] result.color = "black" diff --git a/src/item/item_finder.py b/src/item/item_finder.py index 91362c1..ff3fbe5 100644 --- a/src/item/item_finder.py +++ b/src/item/item_finder.py @@ -9,7 +9,7 @@ import math from config import Config from utils.misc import color_filter, cut_roi from item import ItemCropper -from template_finder import TemplateFinder +import template_finder from ocr import OcrResult, Ocr diff --git a/src/item/pickit.py b/src/item/pickit.py index def3281..7c4cf86 100644 --- a/src/item/pickit.py +++ b/src/item/pickit.py @@ -179,7 +179,7 @@ if __name__ == "__main__": from config import Config from char.sorceress import LightSorc from char.hammerdin import Hammerdin - from template_finder import TemplateFinder + import template_finder from pather import Pather import keyboard diff --git a/src/npc_manager.py b/src/npc_manager.py index 783d81d..02bc612 100644 --- a/src/npc_manager.py +++ b/src/npc_manager.py @@ -2,7 +2,7 @@ import time import os import numpy as np import keyboard -from template_finder import TemplateFinder +import template_finder from config import Config from screen import grab from ui_manager import ScreenObjects, center_mouse, is_visible, wait_until_hidden @@ -35,12 +35,12 @@ class Npc: npcs = { Npc.QUAL_KEHK: { - "name_tag_white": color_filter(TemplateFinder().get_template("QUAL_NAME_TAG_WHITE"), Config().colors["white"])[1], - "name_tag_gold": color_filter(TemplateFinder().get_template("QUAL_NAME_TAG_GOLD"), Config().colors["gold"])[1], + "name_tag_white": color_filter(template_finder.get_template("QUAL_NAME_TAG_WHITE"), Config().colors["white"])[1], + "name_tag_gold": color_filter(template_finder.get_template("QUAL_NAME_TAG_GOLD"), Config().colors["gold"])[1], "action_btns": { "resurrect": { - "white": color_filter(TemplateFinder().get_template("RESURRECT"), Config().colors["white"])[1], - "blue": color_filter(TemplateFinder().get_template("RESURRECT_BLUE"), Config().colors["blue"])[1], + "white": color_filter(template_finder.get_template("RESURRECT"), Config().colors["white"])[1], + "blue": color_filter(template_finder.get_template("RESURRECT_BLUE"), Config().colors["blue"])[1], } }, "template_group": ["QUAL_0", "QUAL_45", "QUAL_45_B", "QUAL_90", "QUAL_135", "QUAL_135_B", "QUAL_135_C", "QUAL_180", "QUAL_180_B", "QUAL_225", "QUAL_225_B", "QUAL_270", "QUAL_315"], @@ -48,12 +48,12 @@ npcs = { "poses": [[350, 140], [310, 268], [385, 341], [481, 196], [502, 212], [771, 254]] }, Npc.MALAH: { - "name_tag_white": color_filter(TemplateFinder().get_template("MALAH_NAME_TAG_WHITE"), Config().colors["white"])[1], - "name_tag_gold": color_filter(TemplateFinder().get_template("MALAH_NAME_TAG_GOLD"), Config().colors["gold"])[1], + "name_tag_white": color_filter(template_finder.get_template("MALAH_NAME_TAG_WHITE"), Config().colors["white"])[1], + "name_tag_gold": color_filter(template_finder.get_template("MALAH_NAME_TAG_GOLD"), Config().colors["gold"])[1], "action_btns": { "trade": { - "white": color_filter(TemplateFinder().get_template("TRADE"), Config().colors["white"])[1], - "blue": color_filter(TemplateFinder().get_template("TRADE_BLUE"), Config().colors["blue"])[1], + "white": color_filter(template_finder.get_template("TRADE"), Config().colors["white"])[1], + "blue": color_filter(template_finder.get_template("TRADE_BLUE"), Config().colors["blue"])[1], } }, "template_group": ["MALAH_FRONT", "MALAH_BACK", "MALAH_45", "MALAH_SIDE", "MALAH_SIDE_2"], @@ -61,12 +61,12 @@ npcs = { "poses": [[445, 485], [526, 473], [602, 381], [623, 368], [641, 323], [605, 300], [622, 272], [638, 284], [677, 308], [710, 288]] }, Npc.LARZUK: { - "name_tag_white": color_filter(TemplateFinder().get_template("LARZUK_NAME_TAG_WHITE"), Config().colors["white"])[1], - "name_tag_gold": color_filter(TemplateFinder().get_template("LARZUK_NAME_TAG_GOLD"), Config().colors["gold"])[1], + "name_tag_white": color_filter(template_finder.get_template("LARZUK_NAME_TAG_WHITE"), Config().colors["white"])[1], + "name_tag_gold": color_filter(template_finder.get_template("LARZUK_NAME_TAG_GOLD"), Config().colors["gold"])[1], "action_btns": { "trade_repair": { - "white": color_filter(TemplateFinder().get_template("TRADE_REPAIR"), Config().colors["white"])[1], - "blue": color_filter(TemplateFinder().get_template("TRADE_REPAIR_BLUE"), Config().colors["blue"])[1], + "white": color_filter(template_finder.get_template("TRADE_REPAIR"), Config().colors["white"])[1], + "blue": color_filter(template_finder.get_template("TRADE_REPAIR_BLUE"), Config().colors["blue"])[1], } }, "roi": [570, 70, (1038-570), (290-70)], @@ -74,35 +74,35 @@ npcs = { "poses": [[733, 192], [911, 143]] }, Npc.ANYA: { - "name_tag_white": color_filter(TemplateFinder().get_template("ANYA_NAME_TAG_WHITE"), Config().colors["white"])[1], - "name_tag_gold": color_filter(TemplateFinder().get_template("ANYA_NAME_TAG_GOLD"), Config().colors["gold"])[1], + "name_tag_white": color_filter(template_finder.get_template("ANYA_NAME_TAG_WHITE"), Config().colors["white"])[1], + "name_tag_gold": color_filter(template_finder.get_template("ANYA_NAME_TAG_GOLD"), Config().colors["gold"])[1], "action_btns": { "trade": { - "white": color_filter(TemplateFinder().get_template("TRADE"), Config().colors["white"])[1], - "blue": color_filter(TemplateFinder().get_template("TRADE_BLUE"), Config().colors["blue"])[1], + "white": color_filter(template_finder.get_template("TRADE"), Config().colors["white"])[1], + "blue": color_filter(template_finder.get_template("TRADE_BLUE"), Config().colors["blue"])[1], } }, "template_group": ["ANYA_FRONT", "ANYA_BACK", "ANYA_SIDE"] }, Npc.TYRAEL: { - "name_tag_white": color_filter(TemplateFinder().get_template("TYRAEL_NAME_TAG_WHITE"), Config().colors["white"])[1], - "name_tag_gold": color_filter(TemplateFinder().get_template("TYRAEL_NAME_TAG_GOLD"), Config().colors["gold"])[1], + "name_tag_white": color_filter(template_finder.get_template("TYRAEL_NAME_TAG_WHITE"), Config().colors["white"])[1], + "name_tag_gold": color_filter(template_finder.get_template("TYRAEL_NAME_TAG_GOLD"), Config().colors["gold"])[1], "action_btns": { "resurrect": { - "white": color_filter(TemplateFinder().get_template("RESURRECT"), Config().colors["white"])[1], - "blue": color_filter(TemplateFinder().get_template("RESURRECT_BLUE"), Config().colors["blue"])[1], + "white": color_filter(template_finder.get_template("RESURRECT"), Config().colors["white"])[1], + "blue": color_filter(template_finder.get_template("RESURRECT_BLUE"), Config().colors["blue"])[1], } }, "roi": [569, 86, (852-569), (357-86)], "template_group": ["TYRAEL_1", "TYRAEL_2"] }, Npc.ORMUS: { - "name_tag_white": color_filter(TemplateFinder().get_template("ORMUS_NAME_TAG_WHITE"), Config().colors["white"])[1], - "name_tag_gold": color_filter(TemplateFinder().get_template("ORMUS_NAME_TAG_GOLD"), Config().colors["gold"])[1], + "name_tag_white": color_filter(template_finder.get_template("ORMUS_NAME_TAG_WHITE"), Config().colors["white"])[1], + "name_tag_gold": color_filter(template_finder.get_template("ORMUS_NAME_TAG_GOLD"), Config().colors["gold"])[1], "action_btns": { "trade": { - "white": color_filter(TemplateFinder().get_template("TRADE"), Config().colors["white"])[1], - "blue": color_filter(TemplateFinder().get_template("TRADE_BLUE"), Config().colors["blue"])[1], + "white": color_filter(template_finder.get_template("TRADE"), Config().colors["white"])[1], + "blue": color_filter(template_finder.get_template("TRADE_BLUE"), Config().colors["blue"])[1], } }, "roi": [444, 13, (816-444), (331-13)], @@ -110,82 +110,82 @@ npcs = { "template_group": ["ORMUS_0", "ORMUS_1", "ORMUS_2", "ORMUS_3", "ORMUS_4", "ORMUS_5"] }, Npc.FARA: { - "name_tag_white": color_filter(TemplateFinder().get_template("FARA_NAME_TAG_WHITE"), Config().colors["white"])[1], - "name_tag_gold": color_filter(TemplateFinder().get_template("FARA_NAME_TAG_GOLD"), Config().colors["gold"])[1], + "name_tag_white": color_filter(template_finder.get_template("FARA_NAME_TAG_WHITE"), Config().colors["white"])[1], + "name_tag_gold": color_filter(template_finder.get_template("FARA_NAME_TAG_GOLD"), Config().colors["gold"])[1], "action_btns": { "trade_repair": { - "white": color_filter(TemplateFinder().get_template("TRADE_REPAIR"), Config().colors["white"])[1], - "blue": color_filter(TemplateFinder().get_template("TRADE_REPAIR_BLUE"), Config().colors["blue"])[1], + "white": color_filter(template_finder.get_template("TRADE_REPAIR"), Config().colors["white"])[1], + "blue": color_filter(template_finder.get_template("TRADE_REPAIR_BLUE"), Config().colors["blue"])[1], } }, "template_group": ["FARA_LIGHT_1", "FARA_LIGHT_2", "FARA_LIGHT_3", "FARA_LIGHT_4", "FARA_LIGHT_5", "FARA_LIGHT_6", "FARA_LIGHT_7", "FARA_LIGHT_8", "FARA_LIGHT_9", "FARA_MEDIUM_1", "FARA_MEDIUM_2", "FARA_MEDIUM_3", "FARA_MEDIUM_4", "FARA_MEDIUM_5", "FARA_MEDIUM_6", "FARA_MEDIUM_7", "FARA_DARK_1", "FARA_DARK_2", "FARA_DARK_3", "FARA_DARK_4", "FARA_DARK_5", "FARA_DARK_6", "FARA_DARK_7"] }, Npc.DROGNAN: { - "name_tag_white": color_filter(TemplateFinder().get_template("DROGNAN_NAME_TAG_WHITE"), Config().colors["white"])[1], - "name_tag_gold": color_filter(TemplateFinder().get_template("DROGNAN_NAME_TAG_GOLD"), Config().colors["gold"])[1], + "name_tag_white": color_filter(template_finder.get_template("DROGNAN_NAME_TAG_WHITE"), Config().colors["white"])[1], + "name_tag_gold": color_filter(template_finder.get_template("DROGNAN_NAME_TAG_GOLD"), Config().colors["gold"])[1], "action_btns": { "trade": { - "white": color_filter(TemplateFinder().get_template("TRADE"), Config().colors["white"])[1], - "blue": color_filter(TemplateFinder().get_template("TRADE_BLUE"), Config().colors["blue"])[1], + "white": color_filter(template_finder.get_template("TRADE"), Config().colors["white"])[1], + "blue": color_filter(template_finder.get_template("TRADE_BLUE"), Config().colors["blue"])[1], } }, "template_group": ["DROGNAN_FRONT", "DROGNAN_LEFT", "DROGNAN_RIGHT_SIDE"] }, Npc.LYSANDER: { - "name_tag_white": color_filter(TemplateFinder().get_template("LYSANDER_NAME_TAG_WHITE"), Config().colors["white"])[1], - "name_tag_gold": color_filter(TemplateFinder().get_template("LYSANDER_NAME_TAG_GOLD"), Config().colors["gold"])[1], + "name_tag_white": color_filter(template_finder.get_template("LYSANDER_NAME_TAG_WHITE"), Config().colors["white"])[1], + "name_tag_gold": color_filter(template_finder.get_template("LYSANDER_NAME_TAG_GOLD"), Config().colors["gold"])[1], "action_btns": { "trade": { - "white": color_filter(TemplateFinder().get_template("TRADE"), Config().colors["white"])[1], - "blue": color_filter(TemplateFinder().get_template("TRADE_BLUE"), Config().colors["blue"])[1], + "white": color_filter(template_finder.get_template("TRADE"), Config().colors["white"])[1], + "blue": color_filter(template_finder.get_template("TRADE_BLUE"), Config().colors["blue"])[1], } }, "template_group": ["LYSANDER_FRONT", "LYSANDER_BACK", "LYSANDER_SIDE", "LYSANDER_SIDE_2"] }, Npc.CAIN: { - "name_tag_white": color_filter(TemplateFinder().get_template("CAIN_NAME_TAG_WHITE"), Config().colors["white"])[1], - "name_tag_gold": color_filter(TemplateFinder().get_template("CAIN_NAME_TAG_GOLD"), Config().colors["gold"])[1], + "name_tag_white": color_filter(template_finder.get_template("CAIN_NAME_TAG_WHITE"), Config().colors["white"])[1], + "name_tag_gold": color_filter(template_finder.get_template("CAIN_NAME_TAG_GOLD"), Config().colors["gold"])[1], "action_btns": { "identify": { - "white": color_filter(TemplateFinder().get_template("IDENTIFY"), Config().colors["white"])[1], - "blue": color_filter(TemplateFinder().get_template("IDENTIFY_BLUE"), Config().colors["blue"])[1], + "white": color_filter(template_finder.get_template("IDENTIFY"), Config().colors["white"])[1], + "blue": color_filter(template_finder.get_template("IDENTIFY_BLUE"), Config().colors["blue"])[1], } }, "template_group": ["CAIN_0", "CAIN_1", "CAIN_2", "CAIN_3"] }, Npc.JAMELLA: { - "name_tag_white": color_filter(TemplateFinder().get_template("JAMELLA_NAME_TAG_WHITE"), Config().colors["white"])[1], - "name_tag_gold": color_filter(TemplateFinder().get_template("JAMELLA_NAME_TAG_GOLD"), Config().colors["gold"])[1], + "name_tag_white": color_filter(template_finder.get_template("JAMELLA_NAME_TAG_WHITE"), Config().colors["white"])[1], + "name_tag_gold": color_filter(template_finder.get_template("JAMELLA_NAME_TAG_GOLD"), Config().colors["gold"])[1], "action_btns": { "trade": { - "white": color_filter(TemplateFinder().get_template("TRADE"), Config().colors["white"])[1], - "blue": color_filter(TemplateFinder().get_template("TRADE_BLUE"), Config().colors["blue"])[1], + "white": color_filter(template_finder.get_template("TRADE"), Config().colors["white"])[1], + "blue": color_filter(template_finder.get_template("TRADE_BLUE"), Config().colors["blue"])[1], }, "gamble": { - "white": color_filter(TemplateFinder().get_template("GAMBLE"), Config().colors["white"])[1], - "blue": color_filter(TemplateFinder().get_template("GAMBLE_BLUE"), Config().colors["blue"])[1], + "white": color_filter(template_finder.get_template("GAMBLE"), Config().colors["white"])[1], + "blue": color_filter(template_finder.get_template("GAMBLE_BLUE"), Config().colors["blue"])[1], } }, "template_group": ["JAMELLA_FRONT", "JAMELLA_BACK", "JAMELLA_SIDE", "JAMELLA_SIDE_2", "JAMELLA_SIDE_3", "JAMELLA_DRAWING"] }, Npc.HALBU: { - "name_tag_white": color_filter(TemplateFinder().get_template("HALBU_NAME_TAG_WHITE"), Config().colors["white"])[1], - "name_tag_gold": color_filter(TemplateFinder().get_template("HALBU_NAME_TAG_GOLD"), Config().colors["gold"])[1], + "name_tag_white": color_filter(template_finder.get_template("HALBU_NAME_TAG_WHITE"), Config().colors["white"])[1], + "name_tag_gold": color_filter(template_finder.get_template("HALBU_NAME_TAG_GOLD"), Config().colors["gold"])[1], "action_btns": { "trade_repair": { - "white": color_filter(TemplateFinder().get_template("TRADE_REPAIR"), Config().colors["white"])[1], - "blue": color_filter(TemplateFinder().get_template("TRADE_REPAIR_BLUE"), Config().colors["blue"])[1], + "white": color_filter(template_finder.get_template("TRADE_REPAIR"), Config().colors["white"])[1], + "blue": color_filter(template_finder.get_template("TRADE_REPAIR_BLUE"), Config().colors["blue"])[1], } }, "template_group": ["HALBU_FRONT", "HALBU_BACK", "HALBU_SIDE", "HALBU_SIDE_2"] }, Npc.AKARA: { - "name_tag_white": color_filter(TemplateFinder().get_template("AKARA_NAME_TAG_WHITE"), Config().colors["white"])[1], - "name_tag_gold": color_filter(TemplateFinder().get_template("AKARA_NAME_TAG_GOLD"), Config().colors["gold"])[1], + "name_tag_white": color_filter(template_finder.get_template("AKARA_NAME_TAG_WHITE"), Config().colors["white"])[1], + "name_tag_gold": color_filter(template_finder.get_template("AKARA_NAME_TAG_GOLD"), Config().colors["gold"])[1], "action_btns": { "trade": { - "white": color_filter(TemplateFinder().get_template("TRADE"), Config().colors["white"])[1], - "blue": color_filter(TemplateFinder().get_template("TRADE_BLUE"), Config().colors["blue"])[1], + "white": color_filter(template_finder.get_template("TRADE"), Config().colors["white"])[1], + "blue": color_filter(template_finder.get_template("TRADE_BLUE"), Config().colors["blue"])[1], } }, "roi": [603, 176, (1002-603), (478-176)], @@ -193,12 +193,12 @@ npcs = { "template_group": ["AKARA_FRONT", "AKARA_BACK", "AKARA_SIDE", "AKARA_SIDE_2"] }, Npc.CHARSI: { - "name_tag_white": color_filter(TemplateFinder().get_template("CHARSI_NAME_TAG_WHITE"), Config().colors["white"])[1], - "name_tag_gold": color_filter(TemplateFinder().get_template("CHARSI_NAME_TAG_GOLD"), Config().colors["gold"])[1], + "name_tag_white": color_filter(template_finder.get_template("CHARSI_NAME_TAG_WHITE"), Config().colors["white"])[1], + "name_tag_gold": color_filter(template_finder.get_template("CHARSI_NAME_TAG_GOLD"), Config().colors["gold"])[1], "action_btns": { "trade_repair": { - "white": color_filter(TemplateFinder().get_template("TRADE_REPAIR"), Config().colors["white"])[1], - "blue": color_filter(TemplateFinder().get_template("TRADE_REPAIR_BLUE"), Config().colors["blue"])[1], + "white": color_filter(template_finder.get_template("TRADE_REPAIR"), Config().colors["white"])[1], + "blue": color_filter(template_finder.get_template("TRADE_REPAIR_BLUE"), Config().colors["blue"])[1], } }, "roi": [249, 76, (543-249), (363-76)], @@ -206,12 +206,12 @@ npcs = { "template_group": ["CHARSI_FRONT", "CHARSI_BACK", "CHARSI_SIDE", "CHARSI_SIDE_2", "CHARSI_SIDE_3"] }, Npc.KASHYA: { - "name_tag_white": color_filter(TemplateFinder().get_template("KASHYA_NAME_TAG_WHITE"), Config().colors["white"])[1], - "name_tag_gold": color_filter(TemplateFinder().get_template("KASHYA_NAME_TAG_GOLD"), Config().colors["gold"])[1], + "name_tag_white": color_filter(template_finder.get_template("KASHYA_NAME_TAG_WHITE"), Config().colors["white"])[1], + "name_tag_gold": color_filter(template_finder.get_template("KASHYA_NAME_TAG_GOLD"), Config().colors["gold"])[1], "action_btns": { "resurrect": { - "white": color_filter(TemplateFinder().get_template("RESURRECT"), Config().colors["white"])[1], - "blue": color_filter(TemplateFinder().get_template("RESURRECT_BLUE"), Config().colors["blue"])[1], + "white": color_filter(template_finder.get_template("RESURRECT"), Config().colors["white"])[1], + "blue": color_filter(template_finder.get_template("RESURRECT_BLUE"), Config().colors["blue"])[1], } }, "template_group": ["KASHYA_FRONT", "KASHYA_BACK", "KASHYA_SIDE", "KASHYA_SIDE_2"] @@ -242,11 +242,11 @@ def open_npc_menu(npc_key: Npc) -> bool: roi_npc = npcs[npc_key]["roi"] else: roi_npc = roi_npc_search - res = TemplateFinder().search(key, img, threshold=0.35, roi=roi_npc, normalize_monitor=True) + res = template_finder.search(key, img, threshold=0.35, roi=roi_npc) if res.valid: is_unique = True for r in results: - if (abs(r["pos"][0] - res.center[0]) + abs(r["pos"][1] - res.center[1])) < 22: + if (abs(r["pos"][0] - res.center_monitor[0]) + abs(r["pos"][1] - res.center_monitor[1])) < 22: is_unique = False break if is_unique: @@ -254,9 +254,9 @@ def open_npc_menu(npc_key: Npc) -> bool: if attempts == 0 and "poses" in npcs[npc_key]: # find distance between template match and nearest pose (([x2] - x1)**2 + (y2 - y1)**2) for pose in npcs[npc_key]["poses"]: - dist = sqrt((res.center[0] - pose[0])**2 + (res.center[1] - pose[1])**2) + dist = sqrt((res.center_monitor[0] - pose[0])**2 + (res.center_monitor[1] - pose[1])**2) min_dist = dist if dist < min_dist else min_dist - results.append({"pos": res.center, "score": res.score, "combo": min_dist / (res.score**2)}) + results.append({"pos": res.center_monitor, "score": res.score, "combo": min_dist / (res.score**2)}) # sort by composite of template match score and distance to NPC pose results = sorted(results, key=lambda r: r["combo"]) for result in results: @@ -266,14 +266,14 @@ def open_npc_menu(npc_key: Npc) -> bool: img = escape_dialogue(img) _, filtered_inp_w = color_filter(img, Config().colors["white"]) _, filtered_inp_g = color_filter(img, Config().colors["gold"]) - res_w = TemplateFinder().search(npcs[npc_key]["name_tag_white"], filtered_inp_w, 0.9, roi=roi).valid - res_g = TemplateFinder().search(npcs[npc_key]["name_tag_gold"], filtered_inp_g, 0.9, roi=roi).valid + res_w = template_finder.search(npcs[npc_key]["name_tag_white"], filtered_inp_w, 0.9, roi=roi).valid + res_g = template_finder.search(npcs[npc_key]["name_tag_gold"], filtered_inp_g, 0.9, roi=roi).valid if res_w: mouse.click(button="left") attempts += 1 wait(0.7, 1.0) _, filtered_inp = color_filter(grab(), Config().colors["gold"]) - res = TemplateFinder().search(npcs[npc_key]["name_tag_gold"], filtered_inp, 0.9, roi=roi).valid + res = template_finder.search(npcs[npc_key]["name_tag_gold"], filtered_inp, 0.9, roi=roi).valid if res: return True elif res_g: @@ -285,21 +285,19 @@ def press_npc_btn(npc_key: Npc, action_btn_key: str): img = grab() img = escape_dialogue(img) _, filtered_inp_w = color_filter(img, Config().colors["white"]) - res = TemplateFinder().search( + res = template_finder.search( npcs[npc_key]["action_btns"][action_btn_key]["white"], - filtered_inp_w, 0.85, roi=Config().ui_roi["cut_skill_bar"], - normalize_monitor=True + filtered_inp_w, 0.85, roi=Config().ui_roi["cut_skill_bar"] ) if not res.valid and "blue" in npcs[npc_key]["action_btns"][action_btn_key]: # search for highlighted / blue action btn _, filtered_inp_b = color_filter(img, Config().colors["blue"]) - res = TemplateFinder().search( + res = template_finder.search( npcs[npc_key]["action_btns"][action_btn_key]["blue"], - filtered_inp_b, 0.85, roi=Config().ui_roi["cut_skill_bar"], - normalize_monitor=True + filtered_inp_b, 0.85, roi=Config().ui_roi["cut_skill_bar"] ) if res.valid: - mouse.move(*res.center, randomize=3, delay_factor=[1.0, 1.5]) + mouse.move(*res.center_monitor, randomize=3, delay_factor=[1.0, 1.5]) wait(0.2, 0.4) mouse.click(button="left") center_mouse() diff --git a/src/pather.py b/src/pather.py index 0179505..4eeaf82 100644 --- a/src/pather.py +++ b/src/pather.py @@ -12,7 +12,7 @@ from utils.misc import is_in_roi from config import Config from logger import Logger from screen import convert_screen_to_monitor, convert_abs_to_screen, convert_abs_to_monitor, convert_screen_to_abs, grab, stop_detecting_window -from template_finder import TemplateFinder +import template_finder from char import IChar from ui_manager import detect_screen_object, ScreenObjects, is_visible, select_screen_object_match @@ -571,7 +571,7 @@ class Pather: def find_abs_node_pos(self, node_idx: int, img: np.ndarray, threshold: float = 0.68) -> Tuple[float, float]: node = self._nodes[node_idx] - template_match = TemplateFinder().search( + template_match = template_finder.search( [*node], img, best_match=False, @@ -706,9 +706,9 @@ if __name__ == "__main__": display_img = img.copy() template_map = {} template_scores = {} - for template_type in TemplateFinder()._templates: + for template_type in template_finder._templates: if filter is None or filter in template_type: - template_match = TemplateFinder().search(template_type, img, use_grayscale=True, threshold=0.78) + template_match = template_finder.search(template_type, img, use_grayscale=True, threshold=0.78) if template_match.valid: template_map[template_type] = template_match.center template_scores[template_type] = template_match.score diff --git a/src/run/arcane.py b/src/run/arcane.py index 7ca6023..767a3dc 100644 --- a/src/run/arcane.py +++ b/src/run/arcane.py @@ -4,7 +4,7 @@ from logger import Logger from pather import Location, Pather from typing import Union from item.pickit import PickIt -from template_finder import TemplateFinder +import template_finder from town.town_manager import TownManager from utils.misc import wait from dataclasses import dataclass @@ -42,13 +42,13 @@ class Arcane: # Check if we arrived at platform templates_platform = ["ARC_PLATFORM_1", "ARC_PLATFORM_2", "ARC_PLATFORM_3", "ARC_CENTER"] tempaltes_summoner = ["ARC_ALTAR", "ARC_ALTAR3", "ARC_END_STAIRS", "ARC_END_STAIRS_2"] - match_platform = TemplateFinder().search_and_wait(templates_platform, threshold=0.55, timeout=0.5, use_grayscale=True, take_ss=False) - match_summoner = TemplateFinder().search_and_wait(tempaltes_summoner, threshold=0.79, timeout=0.5, use_grayscale=True, take_ss=False) + match_platform = template_finder.search_and_wait(templates_platform, threshold=0.55, timeout=0.5, use_grayscale=True) + match_summoner = template_finder.search_and_wait(tempaltes_summoner, threshold=0.79, timeout=0.5, use_grayscale=True) if not match_platform.valid and not match_summoner.valid: # We might have arrived at summoner, move up stairs with static traverse self._pather.traverse_nodes_fixed(traverse_to_summoner, self._char) # try to match summoner again - match_summoner = TemplateFinder().search_and_wait(tempaltes_summoner, threshold=0.79, timeout=1.0, use_grayscale=True, take_ss=False) + match_summoner = template_finder.search_and_wait(tempaltes_summoner, threshold=0.79, timeout=1.0, use_grayscale=True) if match_summoner.valid: if self._pather.traverse_nodes([461], self._char, timeout=2.2, force_tp=True): return True diff --git a/src/run/diablo.py b/src/run/diablo.py index c1014e8..2e99c9c 100644 --- a/src/run/diablo.py +++ b/src/run/diablo.py @@ -6,7 +6,7 @@ from logger import Logger from pather import Location, Pather from typing import Union from item.pickit import PickIt -from template_finder import TemplateFinder +import template_finder from town.town_manager import TownManager, A4 from utils.misc import wait from utils.custom_mouse import mouse @@ -82,7 +82,7 @@ class Diablo: if not self._pather.traverse_nodes([164, 163], self._char, timeout=2): return False wait(0.22, 0.28) if (template_match := detect_screen_object(ScreenObjects.TownPortalReduced)).valid: - pos = template_match.center + pos = template_match.center_monitor pos = (pos[0], pos[1] + 30) Logger.debug(location + ": Going through portal...") # Note: Template is top of portal, thus move the y-position a bit to the bottom @@ -113,7 +113,7 @@ class Diablo: Logger.debug(seal_layout + ": trying to open (try #" + str(i+1)+")") self._char.select_by_template(seal_closedtemplates, threshold=0.5, timeout=0.1, telekinesis=True) wait(i*0.5) - found = TemplateFinder().search_and_wait(seal_opentemplates, threshold=0.75, timeout=0.1, best_match=True, take_ss=False).valid + found = template_finder.search_and_wait(seal_opentemplates, threshold=0.75, timeout=0.1).valid if found: Logger.info(seal_layout +": is open - "+'\033[92m'+" open"+'\033[0m') break @@ -143,7 +143,7 @@ class Diablo: templates = ["DIA_NEW_PENT_TP", "DIA_NEW_PENT_0", "DIA_NEW_PENT_1", "DIA_NEW_PENT_2"] start_time = time.time() while not found and time.time() - start_time < 15: - found = TemplateFinder().search_and_wait(templates, threshold=0.83, timeout=0.1, best_match=True, take_ss=False, suppress_debug=True).valid + found = template_finder.search_and_wait(templates, threshold=0.83, timeout=0.1, suppress_debug=True).valid if not found: self._pather.traverse_nodes_fixed(path, self._char) if not found: if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_failed_loop_pentagram_" + path + "_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab()) @@ -161,10 +161,10 @@ class Diablo: if not self._pather.traverse_nodes([605], self._char): return False templates = ["DIABLO_ENTRANCE_53", "DIABLO_ENTRANCE_51","DIABLO_ENTRANCE_50", "DIABLO_ENTRANCE_52", "DIABLO_ENTRANCE_54", "DIABLO_ENTRANCE_55"] - if TemplateFinder().search_and_wait(templates, threshold=0.8, timeout=0.1, best_match=False, take_ss=False).valid: + if template_finder.search_and_wait(templates, threshold=0.8, timeout=0.1).valid: Logger.debug("CS Trash (A): Layout_check step 1/2: Layout A templates found") templates = ["DIABLO_ENTRANCE2_55", "DIABLO_ENTRANCE2_50", "DIABLO_ENTRANCE2_51", "DIABLO_ENTRANCE2_52","DIABLO_ENTRANCE2_53","DIABLO_ENTRANCE2_54","DIABLO_ENTRANCE2_15","DIABLO_ENTRANCE2_56"] - if not TemplateFinder().search_and_wait(templates, threshold=0.8, timeout=0.5, best_match=True, take_ss=False).valid: + if not template_finder.search_and_wait(templates, threshold=0.8, timeout=0.5).valid: Logger.debug("CS Trash (A): Layout_check step 2/2: Layout B templates NOT found - "+'\033[95m'+"all fine, proceeding with Layout A"+'\033[0m') entrance1_layout = "CS Trash (A):" #if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_" + entrance1_layout + "_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab()) @@ -185,7 +185,7 @@ class Diablo: else: Logger.debug("CS Trash (B): Layout_check step 1/2: Layout A templates NOT found") templates = ["DIABLO_ENTRANCE2_55", "DIABLO_ENTRANCE2_50", "DIABLO_ENTRANCE2_51", "DIABLO_ENTRANCE2_52","DIABLO_ENTRANCE2_53","DIABLO_ENTRANCE2_54","DIABLO_ENTRANCE2_15","DIABLO_ENTRANCE2_56"] - if TemplateFinder().search_and_wait(templates, threshold=0.8, timeout=0.1, best_match=False, take_ss=False).valid: + if template_finder.search_and_wait(templates, threshold=0.8, timeout=0.1).valid: Logger.debug("CS Trash (B): Layout_check step 2/2: Layout B templates found - "+'\033[96m'+"all fine, proceeding with Layout B"+'\033[0m') entrance2_layout = "CS Trash (B):" #if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_" + entrance2_layout + "_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab()) @@ -214,7 +214,7 @@ class Diablo: templates = ["DIA_NEW_PENT_0", "DIA_NEW_PENT_1", "DIA_NEW_PENT_2"] start_time = time.time() while not found and time.time() - start_time < 10: - found = TemplateFinder().search_and_wait(templates, threshold=0.8, timeout=0.1, best_match=True, take_ss=False, suppress_debug=True).valid + found = template_finder.search_and_wait(templates, threshold=0.8, timeout=0.1, suppress_debug=True).valid if not found: self._pather.traverse_nodes_fixed("diablo_wp_pentagram_loop", self._char) if not found: @@ -235,7 +235,7 @@ class Diablo: templates = ["DIABLO_CS_ENTRANCE_0", "DIABLO_CS_ENTRANCE_2", "DIABLO_CS_ENTRANCE_3"] start_time = time.time() while not found and time.time() - start_time < 10: - found = TemplateFinder().search_and_wait(templates, threshold=0.8, timeout=0.1, best_match=True, take_ss=False, suppress_debug=True).valid + found = template_finder.search_and_wait(templates, threshold=0.8, timeout=0.1, suppress_debug=True).valid if not found: self._pather.traverse_nodes_fixed("diablo_wp_entrance_loop", self._char) if not found: @@ -251,7 +251,7 @@ class Diablo: templates = ["DIA_NEW_PENT_TP", "DIA_NEW_PENT_0", "DIA_NEW_PENT_1", "DIA_NEW_PENT_2"] start_time = time.time() while not found and time.time() - start_time < 15: - found = TemplateFinder().search_and_wait(templates, threshold=0.83, timeout=0.1, best_match=True, take_ss=False, suppress_debug=True).valid + found = template_finder.search_and_wait(templates, threshold=0.83, timeout=0.1, suppress_debug=True).valid if not found: self._pather.traverse_nodes_fixed("diablo_wp_pentagram_loop", self._char) if not found: if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_failed_loop_pentagram_diablo_wp_pentagram_loop_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab()) @@ -288,7 +288,7 @@ class Diablo: templates = ["DIA_NEW_PENT_TP", "DIA_NEW_PENT_0", "DIA_NEW_PENT_1", "DIA_NEW_PENT_2"] start_time = time.time() while not found and time.time() - start_time < 15: - found = TemplateFinder().search_and_wait(templates, threshold=0.83, timeout=0.1, best_match=True, take_ss=False).valid + found = template_finder.search_and_wait(templates, threshold=0.83, timeout=0.1).valid if not found: self._pather.traverse_nodes_fixed(loop_path, self._char) if not found: if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_failed_loop_pentagram_" + path + "_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab()) @@ -370,12 +370,12 @@ class Diablo: if not self._pather.traverse_nodes(calibration_node, self._char, threshold=calibration_threshold,): return False #if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_LC_" + sealname + "_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab()) #check1 using primary templates - if not TemplateFinder().search_and_wait(templates_primary, threshold =threshold_primary, timeout=0.1, best_match=True, take_ss=False).valid: + if not template_finder.search_and_wait(templates_primary, threshold =threshold_primary, timeout=0.1).valid: Logger.debug(f"{seal_layout1}: Layout_check step 1/2 - templates NOT found for "f"{seal_layout2}") #cross-check for confirmation if not confirmation_node == None: if not self._pather.traverse_nodes(confirmation_node, self._char, threshold=calibration_threshold,): return False - if not TemplateFinder().search_and_wait(templates_confirmation, threshold=threshold_confirmation, timeout=0.1, best_match=True, take_ss=False).valid: + if not template_finder.search_and_wait(templates_confirmation, threshold=threshold_confirmation, timeout=0.1).valid: Logger.warning(f"{seal_layout2}: Layout_check failure - could not determine the seal Layout at" f"{sealname} ("f"{boss}) - "+'\033[91m'+"aborting run"+'\033[0m') if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_" + seal_layout1 + "_LC_fail" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab()) return False @@ -387,7 +387,7 @@ class Diablo: #cross-check for confirmation if not confirmation_node2 == None: if not self._pather.traverse_nodes(confirmation_node2, self._char, threshold=calibration_threshold,): return False - if not TemplateFinder().search_and_wait(templates_confirmation, threshold=threshold_confirmation2, timeout=0.1, best_match=True, take_ss=False).valid: + if not template_finder.search_and_wait(templates_confirmation, threshold=threshold_confirmation2, timeout=0.1).valid: Logger.info(f"{seal_layout2}: Layout_check step 2/2 - templates NOT found for "f"{seal_layout1} - "+'\033[96m'+"all fine, proceeding with "f"{seal_layout2}"+'\033[0m') if not self._seal(*params_seal2): return False else: diff --git a/src/run/nihlathak.py b/src/run/nihlathak.py index f6b9025..5e8df19 100644 --- a/src/run/nihlathak.py +++ b/src/run/nihlathak.py @@ -4,7 +4,7 @@ from logger import Logger from pather import Location, Pather from typing import Union from item.pickit import PickIt -from template_finder import TemplateFinder +import template_finder from town.town_manager import TownManager from utils.misc import wait from dataclasses import dataclass @@ -40,7 +40,7 @@ class Nihlathak: def battle(self, do_pre_buff: bool) -> Union[bool, tuple[Location, bool]]: # TODO: We might need a second template for each option as merc might run into the template and we dont find it then # Let's check which layout ("NI1_A = bottom exit" , "NI1_B = large room", "NI1_C = small room") - template_match = TemplateFinder().search_and_wait(["NI1_A", "NI1_B", "NI1_C"], threshold=0.65, timeout=20) + template_match = template_finder.search_and_wait(["NI1_A", "NI1_B", "NI1_C"], threshold=0.65, timeout=20) if not template_match.valid: return False if do_pre_buff: @@ -50,7 +50,7 @@ class Nihlathak: # Its xpects that the static routes defined in game.ini are named: "ni1_a", "ni1_b", "ni1_c" self._pather.traverse_nodes_fixed(template_match.name.lower(), self._char) found_loading_screen_func = lambda: loading.wait_for_loading_screen(2.0) or \ - TemplateFinder().search_and_wait(["NI2_SEARCH_0", "NI2_SEARCH_1"], threshold=0.8, timeout=0.5).valid + template_finder.search_and_wait(["NI2_SEARCH_0", "NI2_SEARCH_1"], threshold=0.8, timeout=0.5).valid # look for stairs if not self._char.select_by_template(["NI1_STAIRS", "NI1_STAIRS_2", "NI1_STAIRS_3", "NI1_STAIRS_4"], found_loading_screen_func, threshold=0.63, timeout=4): # do a random tele jump and try again @@ -59,7 +59,7 @@ class Nihlathak: if not self._char.select_by_template(["NI1_STAIRS", "NI1_STAIRS_2", "NI1_STAIRS_3", "NI1_STAIRS_4"], found_loading_screen_func, threshold=0.63, timeout=4): return False # Wait until templates in lvl 2 entrance are found - if not TemplateFinder().search_and_wait(["NI2_SEARCH_0", "NI2_SEARCH_1", "NI2_SEARCH_2"], threshold=0.8, timeout=20).valid: + if not template_finder.search_and_wait(["NI2_SEARCH_0", "NI2_SEARCH_1", "NI2_SEARCH_2"], threshold=0.8, timeout=20).valid: return False wait(1.0) # wait to make sure the red writing is gone once we check for the eye @dataclass @@ -82,7 +82,7 @@ class Nihlathak: # Move to spot where eye would be visible self._pather.traverse_nodes_fixed(data.circle_static_path_key, self._char) # Search for eye - template_match = TemplateFinder().search_and_wait(data.template_name, threshold=0.7, best_match=True, timeout=3) + template_match = template_finder.search_and_wait(data.template_name, threshold=0.7, best_match=True, timeout=3) # If it is found, move down that hallway if template_match.valid and template_match.name.endswith("_SAFE_DIST"): self._pather.traverse_nodes_fixed(data.destination_static_path_key, self._char) diff --git a/src/run/pindle.py b/src/run/pindle.py index 1ec7b36..d517e05 100644 --- a/src/run/pindle.py +++ b/src/run/pindle.py @@ -4,7 +4,7 @@ from logger import Logger from pather import Location, Pather from typing import Union from item.pickit import PickIt -from template_finder import TemplateFinder +import template_finder from town.town_manager import TownManager from utils.misc import wait from ui import loading @@ -38,7 +38,7 @@ class Pindle: def battle(self, do_pre_buff: bool) -> Union[bool, tuple[Location, bool]]: # Kill Pindle - if not TemplateFinder().search_and_wait(["PINDLE_0", "PINDLE_1"], threshold=0.65, timeout=20).valid: + if not template_finder.search_and_wait(["PINDLE_0", "PINDLE_1"], threshold=0.65, timeout=20).valid: return False if do_pre_buff: self._char.pre_buff() diff --git a/src/run/shenk_eld.py b/src/run/shenk_eld.py index 08c540a..16fccb4 100644 --- a/src/run/shenk_eld.py +++ b/src/run/shenk_eld.py @@ -4,7 +4,7 @@ from logger import Logger from pather import Location, Pather from typing import Union from item.pickit import PickIt -from template_finder import TemplateFinder +import template_finder from town.town_manager import TownManager from utils.misc import wait from ui import waypoint @@ -35,7 +35,7 @@ class ShenkEld: def battle(self, do_shenk: bool, do_pre_buff: bool, game_stats) -> Union[bool, tuple[Location, bool]]: # Eldritch game_stats.update_location("Eld") - if not TemplateFinder().search_and_wait(["ELDRITCH_0", "ELDRITCH_0_V2", "ELDRITCH_0_V3", "ELDRITCH_START", "ELDRITCH_START_V2"], threshold=0.65, timeout=20).valid: + if not template_finder.search_and_wait(["ELDRITCH_0", "ELDRITCH_0_V2", "ELDRITCH_0_V3", "ELDRITCH_START", "ELDRITCH_START_V2"], threshold=0.65, timeout=20).valid: return False if do_pre_buff: self._char.pre_buff() diff --git a/src/run/trav.py b/src/run/trav.py index a44dc12..e25fa8f 100644 --- a/src/run/trav.py +++ b/src/run/trav.py @@ -4,7 +4,7 @@ from logger import Logger from pather import Location, Pather from typing import Union from item.pickit import PickIt -from template_finder import TemplateFinder +import template_finder from town.town_manager import TownManager from utils.misc import wait @@ -35,7 +35,7 @@ class Trav: def battle(self, do_pre_buff: bool) -> Union[bool, tuple[Location, bool]]: # Kill Council - if not TemplateFinder().search_and_wait(["TRAV_0", "TRAV_1", "TRAV_20"], threshold=0.65, timeout=20).valid: + if not template_finder.search_and_wait(["TRAV_0", "TRAV_1", "TRAV_20"], threshold=0.65, timeout=20).valid: return False if do_pre_buff: self._char.pre_buff() diff --git a/src/shop/anya.py b/src/shop/anya.py index 130933f..aada375 100644 --- a/src/shop/anya.py +++ b/src/shop/anya.py @@ -10,7 +10,7 @@ from screen import grab, convert_screen_to_monitor from config import Config from logger import Logger from npc_manager import Npc, open_npc_menu, press_npc_btn -from template_finder import TemplateFinder +import template_finder from utils.custom_mouse import mouse from utils.misc import wait, load_template @@ -93,23 +93,22 @@ class AnyaShopper: img = grab() # 20 IAS gloves have a unique color so we can skip all others - ias_glove = TemplateFinder(True).search( - ref=load_template(asset_folder + "ias_gloves.png", 1.0), + ias_glove = template_finder.search( + ref=load_template(asset_folder + "ias_gloves.png"), inp_img=img, threshold=0.96, - roi=Config().ui_roi["left_inventory"], - normalize_monitor=True, + roi=Config().ui_roi["left_inventory"] ) if ias_glove.valid: self.ias_gloves_seen += 1 - mouse.move(*ias_glove.center) + mouse.move(*ias_glove.center_monitor) time.sleep(0.1) img = grab() if self.look_for_plus_3_gloves is True: - gg_gloves = TemplateFinder(True).search( + gg_gloves = template_finder.search( ref=load_template( - asset_folder + "gg_gloves.png", 1.0 # assets for javazon gloves are mixed up, this one need +3 as in the 1080p version + asset_folder + "gg_gloves.png" # assets for javazon gloves are mixed up, this one need +3 as in the 1080p version ), inp_img=img, threshold=0.80 @@ -124,9 +123,9 @@ class AnyaShopper: else: if self.look_for_plus_2_gloves is True: - g_gloves = TemplateFinder(True).search( + g_gloves = template_finder.search( ref=load_template( - asset_folder + "g_gloves.png", 1.0 + asset_folder + "g_gloves.png" ), inp_img=img, threshold=0.80 @@ -149,34 +148,33 @@ class AnyaShopper: img = grab().copy() claw_keys = ["CLAW1", "CLAW2", "CLAW3"] for ck in claw_keys: - template_match = TemplateFinder(True).search(ck, img, roi=self.roi_vendor) + template_match = template_finder.search(ck, img, roi=self.roi_vendor) if template_match.valid: - claw_pos.append(template_match.center) + claw_pos.append(template_match.center_monitor) # check out each claw for pos in claw_pos: # cv2.circle(img, pos, 3, (0, 255, 0), 2) - x_m, y_m = convert_screen_to_monitor(pos) - mouse.move(x_m, y_m, randomize=3, delay_factor=[0.5, 0.6]) + mouse.move(*pos, randomize=3, delay_factor=[0.5, 0.6]) wait(0.5, 0.6) img_stats = grab() trap_score = 0 melee_score = 0 - if TemplateFinder(True).search("3_TO_TRAPS", img_stats, roi=self.roi_claw_stats, threshold=0.94).valid: + if template_finder.search("3_TO_TRAPS", img_stats, roi=self.roi_claw_stats, threshold=0.94).valid: trap_score += 12 - elif TemplateFinder(True).search("TO_TRAPS", img_stats, roi=self.roi_claw_stats, threshold=0.9).valid: + elif template_finder.search("TO_TRAPS", img_stats, roi=self.roi_claw_stats, threshold=0.9).valid: trap_score += 8 - if TemplateFinder(True).search("2_TO_ASSA", img_stats, roi=self.roi_claw_stats, threshold=0.9).valid: + if template_finder.search("2_TO_ASSA", img_stats, roi=self.roi_claw_stats, threshold=0.9).valid: trap_score += 10 melee_score += 10 - if TemplateFinder(True).search("TO_VENOM", img_stats, roi=self.roi_claw_stats, threshold=0.9).valid: + if template_finder.search("TO_VENOM", img_stats, roi=self.roi_claw_stats, threshold=0.9).valid: melee_score += 6 - if TemplateFinder(True).search("TO_LIGHT", img_stats, roi=self.roi_claw_stats, threshold=0.9).valid: + if template_finder.search("TO_LIGHT", img_stats, roi=self.roi_claw_stats, threshold=0.9).valid: trap_score += 6 - if TemplateFinder(True).search("TO_WB", img_stats, roi=self.roi_claw_stats, threshold=0.9).valid: + if template_finder.search("TO_WB", img_stats, roi=self.roi_claw_stats, threshold=0.9).valid: melee_score += 2 trap_score += 1 - if TemplateFinder(True).search("TO_DS", img_stats, roi=self.roi_claw_stats, threshold=0.9).valid: + if template_finder.search("TO_DS", img_stats, roi=self.roi_claw_stats, threshold=0.9).valid: trap_score += 4 self.claws_evaluated += 1 @@ -221,9 +219,9 @@ class AnyaShopper: def select_by_template(self, template_type: str) -> bool: Logger.debug(f"Select {template_type}") - template_match = TemplateFinder(True).search_and_wait(template_type, timeout=10, normalize_monitor=True) + template_match = template_finder.search_and_wait(template_type, timeout=10) if template_match.valid: - mouse.move(*template_match.center) + mouse.move(*template_match.center_monitor) wait(0.1, 0.2) mouse.click(button="left") return True diff --git a/src/shop/drognan.py b/src/shop/drognan.py index f9ba3ac..dca868d 100644 --- a/src/shop/drognan.py +++ b/src/shop/drognan.py @@ -12,7 +12,7 @@ from screen import convert_screen_to_monitor, grab, convert_abs_to_monitor, conv from config import Config from logger import Logger from npc_manager import Npc, open_npc_menu, press_npc_btn -from template_finder import TemplateFinder +import template_finder from utils.custom_mouse import mouse from utils.misc import wait @@ -91,23 +91,22 @@ class DrognanShopper: img = grab().copy() item_keys = ["SCEPTER1", "SCEPTER2", "SCEPTER3", "SCEPTER4", "SCEPTER5"] for ck in item_keys: - template_match = TemplateFinder(True).search(ck, img, roi=self.roi_vendor) + template_match = template_finder.search(ck, img, roi=self.roi_vendor) if template_match.valid: - item_pos.append(template_match.center) + item_pos.append(template_match.center_monitor) # check out each item for pos in item_pos: - x_m, y_m = convert_screen_to_monitor(pos) - mouse.move(x_m, y_m, randomize=3, delay_factor=[0.5, 0.6]) + mouse.move(*pos, randomize=3, delay_factor=[0.5, 0.6]) wait(0.5, 0.6) img_stats = grab() # First check for +2 Paladin Skills. This weeds out most scepters right away. - if TemplateFinder(True).search("2_TO_PALADIN_SKILLS", img_stats, roi=self.roi_shop_item_stats, threshold=0.94).valid: + if template_finder.search("2_TO_PALADIN_SKILLS", img_stats, roi=self.roi_shop_item_stats, threshold=0.94).valid: # Has 2 Pally skills, check blessed hammers next - if TemplateFinder(True).search("TO_BLESSED_HAMMERS", img_stats, roi=self.roi_shop_item_stats, threshold=0.9).valid: + if template_finder.search("TO_BLESSED_HAMMERS", img_stats, roi=self.roi_shop_item_stats, threshold=0.9).valid: # Has 2 Pally skills AND Blessed Hammers, check Concentration next - if TemplateFinder(True).search("TO_CONCENTRATION", img_stats, roi=self.roi_shop_item_stats, threshold=0.9).valid: + if template_finder.search("TO_CONCENTRATION", img_stats, roi=self.roi_shop_item_stats, threshold=0.9).valid: # Has 2 Pally skills AND Blessed Hammers AND Concentration. We're good! Buy it! mouse.click(button="right") Logger.info(f"Item bought!") diff --git a/src/template_finder.py b/src/template_finder.py index 6dfc96b..5d8244f 100644 --- a/src/template_finder.py +++ b/src/template_finder.py @@ -9,235 +9,220 @@ from logger import Logger import time import os from config import Config -from utils.misc import cut_roi, load_template, list_files_in_folder, alpha_to_mask, roi_center, color_filter +from utils.misc import cut_roi, load_template, list_files_in_folder, alpha_to_mask, roi_center, color_filter, mask_by_roi +from functools import cache -template_finder_lock = threading.Lock() +templates_lock = threading.Lock() + +@dataclass +class Template: + name: str = None + img_bgra: np.ndarray = None + img_bgr: np.ndarray = None + img_gray: np.ndarray = None + alpha_mask: np.ndarray = None @dataclass class TemplateMatch: name: str = None score: float = -1.0 - center: tuple[float, float] = None + center: tuple[int, int] = None + center_monitor: tuple[int, int] = None region: list[float] = None + region_monitor: list[float] = None valid: bool = False -class TemplateFinder: + +TEMPLATE_PATHS = [ + "assets\\templates", + "assets\\npc", + "assets\\shop", + "assets\\item_properties", + "assets\\chests", + "assets\\gamble", +] + +@cache +def _templates() -> dict[Template]: + paths = [] + templates = {} + for path in TEMPLATE_PATHS: + paths += list_files_in_folder(path) + for file_path in paths: + file_name: str = os.path.basename(file_path) + if file_name.lower().endswith('.png'): + key = file_name[:-4].upper() + template_img = load_template(file_path) + templates[key] = Template( + name = key, + img_bgra = template_img, + img_bgr = cv2.cvtColor(template_img, cv2.COLOR_BGRA2BGR), + img_gray = cv2.cvtColor(template_img, cv2.COLOR_BGRA2GRAY), + alpha_mask = alpha_to_mask(template_img) + ) + return templates + +def get_template(key): + with templates_lock: + return _templates()[key].img_bgr + +def _process_template_refs(ref: Union[str, np.ndarray, list[str]]) -> list[Template]: + templates = [] + if type(ref) != list: + ref = [ref] + for i in ref: + # if the reference is a string, then it's a reference to a named template asset + if type(i) == str: + templates.append(_templates()[i.upper()]) + # if the reference is an image, append new Template class object + elif type(i) == np.ndarray: + templates.append(Template( + img_bgr = i, + img_gray = cv2.cvtColor(i, cv2.COLOR_BGR2GRAY), + alpha_mask = alpha_to_mask(i) + )) + return templates + +def _single_template_match(template: Template, inp_img: np.ndarray = None, roi: list = None, color_match: list = None, use_grayscale: bool = False) -> TemplateMatch: + inp_img = inp_img if inp_img is not None else grab() + template_match = TemplateMatch() + + # crop image to roi + if roi is None: + # if no roi is provided roi = full inp_img + roi = [0, 0, inp_img.shape[1], inp_img.shape[0]] + rx, ry, rw, rh = roi + img = inp_img[ry:ry + rh, rx:rx + rw] + + # filter for desired color or make grayscale + if color_match: + template_img, = color_filter(template.img_bgr, color_match)[1], + img = color_filter(img, color_match)[1] + elif use_grayscale: + template_img = template.img_gray + img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) + else: + template_img = template.img_bgr + + if not (img.shape[0] > template_img.shape[0] and img.shape[1] > template_img.shape[1]): + Logger.error(f"Image shape and template shape are incompatible: {template.name}. Image: {img.shape}, Template: {template_img.shape}") + else: + res = cv2.matchTemplate(img, template_img, cv2.TM_CCOEFF_NORMED, mask = template.alpha_mask) + np.nan_to_num(res, copy=False, nan=0.0, posinf=0.0, neginf=0.0) + _, max_val, _, max_pos = cv2.minMaxLoc(res) + + # save rectangle corresponding to matched region + rec_x = int((max_pos[0] + rx)) + rec_y = int((max_pos[1] + ry)) + rec_w = int(template_img.shape[1]) + rec_h = int(template_img.shape[0]) + template_match.region = [rec_x, rec_y, rec_w, rec_h] + template_match.region_monitor = [*convert_screen_to_monitor((rec_x, rec_y)), rec_w, rec_h] + template_match.center = roi_center(template_match.region) + template_match.center_monitor = convert_screen_to_monitor(template_match.center) + template_match.name = template.name + template_match.score = max_val + template_match.valid = True + + return template_match + + +def search( + ref: Union[str, np.ndarray, list[str]], + inp_img: np.ndarray, + threshold: float = 0.68, + roi: list[float] = None, + use_grayscale: bool = False, + color_match: list = False, + best_match: bool = False +) -> TemplateMatch: """ - Loads images from assets/templates and assets/npc and provides search functions - to find these assets within another image - IMPORTANT: This method must be thread safe! + Search for a template in an image + :param ref: Either key of a already loaded template, list of such keys, or a image which is used as template + :param inp_img: Image in which the template will be searched + :param threshold: Threshold which determines if a template is found or not + :param roi: Region of Interest of the inp_img to restrict search area. Format [left, top, width, height] + :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"]) + :param best_match: If list input, will search for list of templates by best match. Default behavior is first match. + :return: Returns a TemplateMatch object with a valid flag """ - TEMPLATE_PATHS = [ - "assets\\templates", - "assets\\npc", - "assets\\shop", - "assets\\item_properties", - "assets\\chests", - "assets\\gamble", - ] - _instance = None - - def __new__(cls, save_last_res=False): - if cls._instance is None: - cls._instance = super(TemplateFinder, cls).__new__(cls) - cls._instance._save_last_res = save_last_res - if cls._instance._save_last_res: - # do not use this when running botty as it is used accross multiple threads! Just used in shopper as a workaround for now - cls._instance.last_res = None - # load templates with their filename as key in the dict - pathes = [] - for path in TemplateFinder.TEMPLATE_PATHS: - pathes += list_files_in_folder(path) - cls._instance._templates = {} - for file_path in pathes: - file_name: str = os.path.basename(file_path) - if file_name.lower().endswith('.png'): - key = file_name[:-4].upper() - template_img = load_template(file_path, 1.0, True) - mask = alpha_to_mask(template_img) - cls._instance._templates[key] = [ - cv2.cvtColor(template_img, cv2.COLOR_BGRA2BGR), - cv2.cvtColor(template_img, cv2.COLOR_BGRA2GRAY), - 1.0, - mask - ] - return cls._instance - - def get_template(self, key): - return cv2.cvtColor(self._templates[key][0], cv2.COLOR_BGRA2BGR) - - def search( - self, - ref: Union[str, np.ndarray, list[str]], - inp_img: np.ndarray, - threshold: float = 0.68, - roi: list[float] = None, - normalize_monitor: bool = False, - best_match: bool = False, - use_grayscale: bool = False, - color_match: list = False, - ) -> TemplateMatch: - """ - Search for a template in an image - :param ref: Either key of a already loaded template, list of such keys, or a image which is used as template - :param inp_img: Image in which the template will be searched - :param threshold: Threshold which determines if a template is found or not - :param roi: Region of Interest of the inp_img to restrict search area. Format [left, top, width, height] - :param normalize_monitor: If True will return positions in monitor coordinates. Otherwise in coordinates of the input image. - :param best_match: If list input, will search for list of templates by best match. Default behavior is first match. - :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"]) - :return: Returns a TemplateMatch object with a valid flag - """ - if roi is None: - # if no roi is provided roi = full inp_img - roi = [0, 0, inp_img.shape[1], inp_img.shape[0]] - rx, ry, rw, rh = roi - inp_img = inp_img[ry:ry + rh, rx:rx + rw] - - if type(ref) == str: - if not color_match: - templates = [self._templates[ref][use_grayscale]] + templates = _process_template_refs(ref) + matches = [] + for template in templates: + match = _single_template_match(template, inp_img, roi, color_match, use_grayscale) + if match.score >= threshold: + if not best_match: + return match else: - templates = [color_filter(self._templates[ref][0], color_match)[1]] - if use_grayscale: - templates = [cv2.cvtColor(templates[0], cv2.COLOR_BGR2GRAY)] - scales = [self._templates[ref][2]] - masks = [self._templates[ref][3]] - names = [ref] - best_match = False - elif type(ref) == list: - if type(ref[0]) == str: - if not color_match: - templates = [self._templates[i][use_grayscale] for i in ref] - else: - templates = [color_filter(self._templates[i][0], color_match)[1] for i in ref] - if use_grayscale: - templates = [cv2.cvtColor(i, cv2.COLOR_BGR2GRAY) for i in templates] - scales = [self._templates[i][2] for i in ref] - masks = [self._templates[i][3] for i in ref] - names = ref - else: - if not color_match: - templates = ref - else: - templates = [color_filter(i, color_match)[1] for i in ref] - if use_grayscale: - templates = [cv2.cvtColor(i, cv2.COLOR_BGR2GRAY) for i in templates] - scales = [1.0] * len(ref) - masks = [None] * len(ref) - else: - if not color_match: - templates = [ref] - else: - templates = [color_filter(ref, color_match)[1]] - if use_grayscale: - templates = [cv2.cvtColor(i, cv2.COLOR_BGR2GRAY) for i in templates] - scales = [1.0] - masks = [None] - best_match = False + matches.append(match) + if matches: + matches = sorted(matches, key=lambda obj: obj.score, reverse=True) + return matches[0] + return TemplateMatch() - scores = [0] * len(templates) - ref_points = [(0, 0)] * len(templates) - recs = [[0, 0, 0, 0]] * len(templates) - if color_match: - inp_img = color_filter(inp_img, color_match)[1] +def search_and_wait( + ref: Union[str, list[str]], + roi: list[float] = None, + timeout: float = 30, + threshold: float = 0.68, + use_grayscale: bool = False, + color_match: list = False, + best_match: bool = False, + suppress_debug: bool = False, +) -> TemplateMatch: + """ + Helper function that will loop and keep searching for a template + :param timeout: After this amount of time the search will stop and it will return [False, None] + :Other params are the same as for TemplateFinder.search() + :returns a TemplateMatch object + """ + if not suppress_debug: + Logger.debug(f"Waiting for templates: {ref}") + start = time.time() + template_match = TemplateMatch() + while (time_remains := time.time() - start < timeout): + img = grab() + is_loading_black_roi = np.average(img[:, 0:Config().ui_roi["loading_left_black"][2]]) < 1.0 + if not is_loading_black_roi or "LOADING" in ref: + template_match = search(ref, img, roi=roi, threshold=threshold, use_grayscale=use_grayscale, color_match=color_match, best_match=best_match) + if template_match.valid: + break + if not time_remains: + Logger.debug(f"Could not find desired templates") + else: + Logger.debug(f"Found match: {template_match.name} ({template_match.score*100:.1f}% confidence)") + return template_match - for count, template in enumerate(templates): - template_match = TemplateMatch() - scale = scales[count] - mask = masks[count] - if scale != 1: - img: np.ndarray = cv2.resize(inp_img, None, fx=scale, fy=scale, interpolation=cv2.INTER_NEAREST) - rx *= scale - ry *= scale - rw *= scale - rh *= scale - else: - img: np.ndarray = inp_img - - if img.shape[0] > template.shape[0] and img.shape[1] > template.shape[1]: - if use_grayscale: - img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) - res = cv2.matchTemplate(img, template, cv2.TM_CCOEFF_NORMED, mask=mask) - np.nan_to_num(res, copy=False, nan=0.0, posinf=0.0, neginf=0.0) - _, max_val, _, max_pos = cv2.minMaxLoc(res) - if self._save_last_res: - with template_finder_lock: - self.last_res = deepcopy(res) - if max_val > threshold: - rec = [int((max_pos[0] + rx) // scale), int((max_pos[1] +ry) // scale), int(template.shape[1] // scale), int(template.shape[0] // scale)] - ref_point = roi_center(rec) - - if normalize_monitor: - ref_point = convert_screen_to_monitor(ref_point) - rec[0], rec[1] = convert_screen_to_monitor((rec[0], rec[1])) - if best_match: - scores[count] = max_val - ref_points[count] = ref_point - recs[count] = rec - else: - try: template_match.name = names[count] - except: pass - template_match.center = ref_point - template_match.score = max_val - template_match.region = rec - template_match.valid = True - return template_match - - if len(scores) > 0 and max(scores) > 0: - idx=scores.index(max(scores)) - try: template_match.name = names[idx] - except: pass - template_match.center = ref_points[idx] - template_match.score = scores[idx] - template_match.region = recs[idx] - template_match.valid = True - else: - template_match = TemplateMatch() - - return template_match - - def search_and_wait( - self, - ref: Union[str, list[str]], - roi: list[float] = None, - timeout: float = None, - threshold: float = 0.68, - normalize_monitor: bool = False, - best_match: bool = False, - take_ss: bool = True, - use_grayscale: bool = False, - suppress_debug: bool = False, - color_match: list = False, - ) -> TemplateMatch: - """ - Helper function that will loop and keep searching for a template - :param timeout: After this amount of time the search will stop and it will return [False, None] - :param take_ss: Bool value to take screenshot on timeout or not (flag must still be set in params!) - Other params are the same as for TemplateFinder.search() - """ - if type(ref) is str: - ref = [ref] - if not suppress_debug: - Logger.debug(f"Waiting for templates: {ref}") - start = time.time() - while 1: - img = grab() - template_match = self.search(ref, img, roi=roi, threshold=threshold, best_match=best_match, use_grayscale=use_grayscale, normalize_monitor=normalize_monitor, color_match=color_match) - is_loading_black_roi = np.average(img[:, 0:Config().ui_roi["loading_left_black"][2]]) < 1.0 - if not is_loading_black_roi or "LOADING" in ref: - if template_match.valid: - Logger.debug(f"Found Match: {template_match.name} ({template_match.score*100:.1f}% confidence)") - return template_match - if timeout is not None and (time.time() - start) > timeout: - if Config().general["info_screenshots"] and take_ss: - cv2.imwrite(f"./info_screenshots/info_wait_for_{ref}_timeout_" + time.strftime("%Y%m%d_%H%M%S") + ".png", img) - if take_ss: - Logger.debug(f"Could not find any of the above templates") - return template_match +def search_all( + ref: Union[str, np.ndarray, list[str]], + inp_img: np.ndarray, + threshold: float = 0.68, + roi: list[float] = None, + use_grayscale: bool = False, + color_match: list = False, +) -> list[TemplateMatch]: + """ + Returns a list of all templates scoring above set threshold on the screen + :Other params are the same as for TemplateFinder.search() + :return: Returns a list of TemplateMatch objects + """ + templates = _process_template_refs(ref) + matches = [] + img = inp_img + while True: + any_found = False + for template in templates: + match = _single_template_match(template, img, roi, color_match, use_grayscale) + if (ind_found := match.score >= threshold): + matches.append(match) + img = mask_by_roi(img, match.region, "inverse") + any_found |= ind_found + if not any_found: + break + return matches # Testing: Have whatever you want to find on the screen @@ -246,7 +231,7 @@ if __name__ == "__main__": import os from screen import start_detecting_window, stop_detecting_window from utils.misc import wait - from template_finder import TemplateFinder + import template_finder start_detecting_window() wait(0.1) @@ -279,7 +264,7 @@ if __name__ == "__main__": cv2.imwrite(match['filename'], match['img']) Logger.info(f"{match['filename']} saved") - def _toggle_all_templates(): + def _toggle_templates(): global _current_template_idx _current_template_idx = -1 if _current_template_idx != -1 else _last_stored_idx if _current_template_idx == -1: @@ -310,7 +295,7 @@ if __name__ == "__main__": keyboard.add_hotkey('up', lambda: _incr_threshold(0.05)) keyboard.add_hotkey('left', lambda: _incr_template_idx(-1)) keyboard.add_hotkey('right', lambda: _incr_template_idx(1)) - keyboard.add_hotkey('f9', lambda: _toggle_all_templates()) + keyboard.add_hotkey('f9', lambda: _toggle_templates()) keyboard.add_hotkey('f10', lambda: _save_visible_templates()) while 1: @@ -322,7 +307,7 @@ if __name__ == "__main__": else: templates = [_template_list[_current_template_idx]] for key in templates: - template_match = TemplateFinder().search(key, img, threshold=_current_threshold) + template_match = template_finder.search(key, img, threshold=_current_threshold) if template_match.valid: x, y = template_match.center cv2.putText(display_img, str(template_match.name), template_match.center, cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA) diff --git a/src/town/a1.py b/src/town/a1.py index f032f5f..6e539ba 100644 --- a/src/town/a1.py +++ b/src/town/a1.py @@ -5,7 +5,7 @@ from config import Config from npc_manager import Npc, open_npc_menu, press_npc_btn from pather import Pather, Location from typing import Union -from template_finder import TemplateFinder +import template_finder from ui_manager import ScreenObjects, is_visible from utils.misc import wait @@ -34,7 +34,7 @@ class A1(IAct): def open_wp(self, curr_loc: Location) -> bool: if not self._pather.traverse_nodes((curr_loc, Location.A1_WP_SOUTH), self._char, force_move=True): return False wait(0.5, 0.7) - if not TemplateFinder().search("A1_WP", grab()).valid: + if not template_finder.search("A1_WP", grab()).valid: curr_loc = Location.A1_WP_SOUTH if not self._pather.traverse_nodes((curr_loc, Location.A1_WP_NORTH), self._char, force_move=True): return False wait(0.5, 0.7) @@ -43,7 +43,7 @@ class A1(IAct): return self._char.select_by_template(["A1_WP"], found_wp_func, threshold=0.62) def wait_for_tp(self) -> Union[Location, bool]: - success = TemplateFinder().search_and_wait(["A1_TOWN_7", "A1_TOWN_9"], timeout=20).valid + success = template_finder.search_and_wait(["A1_TOWN_7", "A1_TOWN_9"], timeout=20).valid if not self._pather.traverse_nodes([Location.A1_TOWN_TP, Location.A1_KASHYA_CAIN], self._char, force_move=True): return False if success: return Location.A1_KASHYA_CAIN diff --git a/src/town/a2.py b/src/town/a2.py index 8cf1d2a..612e477 100644 --- a/src/town/a2.py +++ b/src/town/a2.py @@ -4,7 +4,7 @@ from screen import grab from npc_manager import Npc, open_npc_menu, press_npc_btn from pather import Pather, Location from typing import Union -from template_finder import TemplateFinder +import template_finder from utils.misc import wait from ui_manager import ScreenObjects, is_visible @@ -67,7 +67,7 @@ class A2(IAct): return self._char.select_by_template(["A2_WP_LIGHT", "A2_WP_DARK"], found_wp_func, telekinesis=True) def wait_for_tp(self) -> Union[Location, bool]: - template_match = TemplateFinder().search_and_wait(["A2_TOWN_21", "A2_TOWN_22", "A2_TOWN_20", "A2_TOWN_19"], timeout=20) + template_match = template_finder.search_and_wait(["A2_TOWN_21", "A2_TOWN_22", "A2_TOWN_20", "A2_TOWN_19"], timeout=20) if template_match.valid: self._pather.traverse_nodes((Location.A2_TP, Location.A2_FARA_STASH), self._char, force_move=True) return Location.A2_FARA_STASH diff --git a/src/town/a3.py b/src/town/a3.py index 2d361ea..b7386d0 100644 --- a/src/town/a3.py +++ b/src/town/a3.py @@ -4,7 +4,7 @@ from screen import grab from npc_manager import Npc, open_npc_menu, press_npc_btn from pather import Pather, Location from typing import Union -from template_finder import TemplateFinder +import template_finder from utils.misc import wait from ui_manager import ScreenObjects, is_visible @@ -53,7 +53,7 @@ class A3(IAct): return self._char.select_by_template("A3_WP", found_wp_func, telekinesis=True) def wait_for_tp(self) -> Union[Location, bool]: - template_match = TemplateFinder().search_and_wait("A3_TOWN_10", timeout=20) + template_match = template_finder.search_and_wait("A3_TOWN_10", timeout=20) if template_match.valid: self._pather.traverse_nodes((Location.A3_STASH_WP, Location.A3_STASH_WP), self._char, force_move=True) return Location.A3_STASH_WP diff --git a/src/town/a4.py b/src/town/a4.py index fc791e3..6eb495a 100644 --- a/src/town/a4.py +++ b/src/town/a4.py @@ -4,7 +4,7 @@ from screen import grab from npc_manager import Npc, open_npc_menu, press_npc_btn from pather import Pather, Location from typing import Union -from template_finder import TemplateFinder +import template_finder from utils.misc import wait from ui_manager import ScreenObjects, is_visible @@ -39,7 +39,7 @@ class A4(IAct): return self._char.select_by_template(["A4_WP", "A4_WP_2"], found_wp_func, threshold=0.62, telekinesis=False) def wait_for_tp(self) -> Union[Location, bool]: - success = TemplateFinder().search_and_wait(["A4_TOWN_4", "A4_TOWN_5", "A4_TOWN_6"], timeout=20).valid + success = template_finder.search_and_wait(["A4_TOWN_4", "A4_TOWN_5", "A4_TOWN_6"], timeout=20).valid if success: return Location.A4_TOWN_START return False diff --git a/src/town/a5.py b/src/town/a5.py index 279990f..1d5fc6f 100644 --- a/src/town/a5.py +++ b/src/town/a5.py @@ -4,7 +4,7 @@ from screen import grab from npc_manager import Npc, open_npc_menu, press_npc_btn from pather import Pather, Location from typing import Union -from template_finder import TemplateFinder +import template_finder from utils.misc import wait from ui_manager import ScreenObjects, is_visible @@ -75,7 +75,7 @@ class A5(IAct): return self._char.select_by_template("A5_WP", found_wp_func, telekinesis=True) def wait_for_tp(self) -> Union[Location, bool]: - success = TemplateFinder().search_and_wait(["A5_TOWN_1", "A5_TOWN_0"], timeout=20).valid + success = template_finder.search_and_wait(["A5_TOWN_1", "A5_TOWN_0"], timeout=20).valid if success: return Location.A5_TOWN_START return False diff --git a/src/town/town_manager.py b/src/town/town_manager.py index 7466272..cba91a9 100644 --- a/src/town/town_manager.py +++ b/src/town/town_manager.py @@ -1,5 +1,5 @@ from typing import Union -from template_finder import TemplateFinder +import template_finder from config import Config from pather import Location from logger import Logger @@ -43,12 +43,12 @@ class TownManager: location = Location.A1_TOWN_START return location - def wait_for_town_spawn(self, timeout: float = None) -> Location: + def wait_for_town_spawn(self, timeout: float = 30) -> Location: """Wait for the char to spawn in town after starting a new game :param timeout: Optional float value for time out in seconds, defaults to None :return: Location of the town (e.g. Location.A4_TOWN_START) or None if nothing was found within timeout time """ - template_match = TemplateFinder().search_and_wait(TOWN_MARKERS, best_match=True, timeout=timeout) + template_match = template_finder.search_and_wait(TOWN_MARKERS, best_match=True, timeout=timeout) if template_match.valid: return TownManager.get_act_from_location(template_match.name) return None diff --git a/src/transmute/inventory_collection.py b/src/transmute/inventory_collection.py index 9d80254..46fd239 100644 --- a/src/transmute/inventory_collection.py +++ b/src/transmute/inventory_collection.py @@ -8,7 +8,7 @@ from config import Config from screen import grab import cv2 import numpy as np -from template_finder import TemplateFinder +import template_finder def _is_slot_empty(img, treshold=16.0): @@ -66,7 +66,7 @@ def inspect_area( result.set_empty((column, row)) if len(known_items) > 0: - match = TemplateFinder().search( + match = template_finder.search( known_items, slot_img, threshold=0.91, best_match=True ) diff --git a/src/transmute/transmute.py b/src/transmute/transmute.py index 28c9b8f..f9aaf9b 100644 --- a/src/transmute/transmute.py +++ b/src/transmute/transmute.py @@ -11,7 +11,7 @@ from utils.misc import wait from version import __version__ from logger import Logger from game_stats import GameStats -from template_finder import TemplateFinder +import template_finder import numpy as np import keyboard import cv2 @@ -67,7 +67,7 @@ class Transmute: def open_cube(self): common.select_tab(0) if (match := detect_screen_object(ScreenObjects.CubeInventory)).valid: - mouse.move(*match.center) + mouse.move(*match.center_monitor) self._wait() mouse.click("right") self._wait() @@ -107,7 +107,7 @@ class Transmute: slot_img = img[y_start:y_end, x_start:x_end] if not self._is_slot_empty(slot_img[+4:-4, +4:-4], treshold=36): result.set_empty((column, row)) - match = TemplateFinder().search( + match = template_finder.search( known_items, slot_img, threshold=0.91, best_match=True) if match.valid: diff --git a/src/ui/character_select.py b/src/ui/character_select.py index a083a5c..cdc5307 100644 --- a/src/ui/character_select.py +++ b/src/ui/character_select.py @@ -2,7 +2,7 @@ from utils.custom_mouse import mouse from utils.misc import cut_roi, roi_center, wait, is_in_roi from config import Config from screen import convert_screen_to_monitor, grab -from template_finder import TemplateFinder +import template_finder from utils.misc import wait from logger import Logger from ocr import Ocr @@ -91,16 +91,15 @@ def select_char(): if scrolls_attempts > 0: img = grab() # TODO: can cleanup logic here, can we utilize a generic ScreenObject or use custom locator? - desired_char = TemplateFinder().search(last_char_template, img, roi = Config().ui_roi["character_select"], threshold = 0.8, normalize_monitor = False) + desired_char = template_finder.search(last_char_template, img, roi = Config().ui_roi["character_select"], threshold = 0.8) if desired_char.valid: - print(f"{match.region} {desired_char.center}") + #print(f"{match.region} {desired_char.center}") if is_in_roi(match.region, desired_char.center) and scrolls_attempts == 0: Logger.debug("Saved character template found and already highlighted, continue") return else: Logger.debug("Selecting saved character") - pos = convert_screen_to_monitor(desired_char.center) - mouse.move(*pos) + mouse.move(*desired_char.center_monitor) wait(0.4, 0.6) mouse.click(button="left") wait(0.4), 0.6 diff --git a/src/ui/skills.py b/src/ui/skills.py index 60dc4ad..c307d75 100644 --- a/src/ui/skills.py +++ b/src/ui/skills.py @@ -6,7 +6,7 @@ import numpy as np from utils.misc import cut_roi, color_filter, wait from screen import grab from config import Config -from template_finder import TemplateFinder +import template_finder from ui_manager import wait_until_visible, ScreenObjects def is_left_skill_selected(template_list: list[str]) -> bool: @@ -15,7 +15,7 @@ def is_left_skill_selected(template_list: list[str]) -> bool: """ skill_left_ui_roi = Config().ui_roi["skill_left"] for template in template_list: - if TemplateFinder().search(template, grab(), threshold=0.84, roi=skill_left_ui_roi).valid: + if template_finder.search(template, grab(), threshold=0.84, roi=skill_left_ui_roi).valid: return True return False @@ -60,7 +60,7 @@ def is_right_skill_selected(template_list: list[str]) -> bool: """ skill_right_ui_roi = Config().ui_roi["skill_right"] for template in template_list: - if TemplateFinder().search(template, grab(), threshold=0.84, roi=skill_right_ui_roi).valid: + if template_finder.search(template, grab(), threshold=0.84, roi=skill_right_ui_roi).valid: return True return False diff --git a/src/ui/view.py b/src/ui/view.py index 20a2fed..642e537 100644 --- a/src/ui/view.py +++ b/src/ui/view.py @@ -111,6 +111,6 @@ if __name__ == "__main__": print("Go to D2R window and press f11 to start game") keyboard.wait("f11") from config import Config - from template_finder import TemplateFinder + import template_finder return_to_play() diff --git a/src/ui_manager.py b/src/ui_manager.py index d31a1d5..b199801 100644 --- a/src/ui_manager.py +++ b/src/ui_manager.py @@ -8,8 +8,9 @@ from utils.custom_mouse import mouse from utils.misc import wait, cut_roi, image_is_equal from logger import Logger from config import Config -from screen import grab, convert_screen_to_monitor, convert_abs_to_monitor -from template_finder import TemplateFinder, TemplateMatch +from screen import grab, convert_abs_to_monitor +import template_finder +from template_finder import TemplateMatch from dataclasses import dataclass from messages import Messenger from game_stats import GameStats @@ -24,9 +25,7 @@ class ScreenObject: ref: list[str] inp_img: np.ndarray = None roi: list[float] = None - timeout: float = 30 threshold: float = 0.68 - normalize_monitor: bool = True best_match: bool = False use_grayscale: bool = False color_match: list[np.array] = None @@ -97,13 +96,11 @@ class ScreenObjects: ref=["CHARACTER_STATE_ONLINE", "CHARACTER_STATE_OFFLINE"], roi="character_online_status", best_match=True, - normalize_monitor=False ) SelectedCharacter=ScreenObject( ref=["CHARACTER_ACTIVE"], roi="character_select", threshold=0.8, - normalize_monitor=False ) ServerError=ScreenObject( ref=["SERVER_ISSUES"] @@ -253,7 +250,6 @@ class ScreenObjects: TabIndicator=ScreenObject( ref="TAB_INDICATOR", roi="tab_indicator", - normalize_monitor=False ) DepositBtn=ScreenObject( ref=["DEPOSIT_BTN", "DEPOSIT_BTN_BRIGHT"], @@ -264,19 +260,17 @@ class ScreenObjects: def detect_screen_object(screen_object: ScreenObject, img: np.ndarray = None) -> TemplateMatch: roi = Config().ui_roi[screen_object.roi] if screen_object.roi else None img = grab() if img is None else img - return TemplateFinder().search( + return template_finder.search( ref = screen_object.ref, inp_img = img, threshold = screen_object.threshold, roi = roi, best_match = screen_object.best_match, use_grayscale = screen_object.use_grayscale, - normalize_monitor = screen_object.normalize_monitor ) -def select_screen_object_match(match: TemplateMatch, delay_factor: tuple[float, float] = (0.9, 1.1), normalize_monitor: bool = False) -> None: - pos = match.center if not normalize_monitor else convert_screen_to_monitor(match.center) - mouse.move(*pos, delay_factor=delay_factor) +def select_screen_object_match(match: TemplateMatch, delay_factor: tuple[float, float] = (0.9, 1.1)) -> None: + mouse.move(*match.center_monitor, delay_factor=delay_factor) wait(0.05, 0.09) mouse.click("left") wait(0.05, 0.09) @@ -285,22 +279,22 @@ def is_visible(screen_object: ScreenObject, img: np.ndarray = None) -> bool: return detect_screen_object(screen_object, img).valid def wait_until_visible(screen_object: ScreenObject, timeout: float = 30) -> TemplateMatch: - if not (match := wait_until(lambda: detect_screen_object(screen_object), lambda match: match.valid, timeout)[0]).valid: + if not (match := _wait_until(lambda: detect_screen_object(screen_object), lambda match: match.valid, timeout)[0]).valid: Logger.debug(f"{screen_object.ref} not found after {timeout} seconds") return match def wait_until_hidden(screen_object: ScreenObject, timeout: float = 3) -> bool: - if not (hidden := wait_until(lambda: detect_screen_object(screen_object).valid, lambda res: not res, timeout)[1]): + if not (hidden := _wait_until(lambda: detect_screen_object(screen_object).valid, lambda res: not res, timeout)[1]): Logger.debug(f"{screen_object.ref} still found after {timeout} seconds") return hidden def wait_for_update(img: np.ndarray, roi: list[int] = None, timeout: float = 3) -> bool: roi = roi if roi is not None else [0, 0, img.shape[0]-1, img.shape[1] -1] - if not (change := wait_until(lambda: cut_roi(grab(), roi), lambda res: not image_is_equal(cut_roi(img, roi), res), timeout)[1]): + if not (change := _wait_until(lambda: cut_roi(grab(), roi), lambda res: not image_is_equal(cut_roi(img, roi), res), timeout)[1]): Logger.debug(f"ROI: '{roi}' unchanged after {timeout} seconds") return change -def wait_until(func: Callable[[], T], is_success: Callable[[T], bool], timeout = None) -> Union[T, None]: +def _wait_until(func: Callable[[], T], is_success: Callable[[T], bool], timeout = None) -> Union[T, None]: start = time.time() while (time.time() - start) < timeout: res = func() @@ -309,8 +303,8 @@ def wait_until(func: Callable[[], T], is_success: Callable[[T], bool], timeout = wait(0.05) return res, success -def hover_over_screen_object_match(match) -> None: - mouse.move(*convert_screen_to_monitor(match.center)) +def hover_over_screen_object_match(match : TemplateMatch) -> None: + mouse.move(*match.center_monitor) wait(0.2, 0.4) def list_visible_objects(img: np.ndarray = None) -> list: diff --git a/src/utils/custom_mouse.py b/src/utils/custom_mouse.py index 3d285f9..1e9dcab 100644 --- a/src/utils/custom_mouse.py +++ b/src/utils/custom_mouse.py @@ -11,7 +11,7 @@ import screen from config import Config from utils.misc import is_in_roi from logger import Logger -from template_finder import TemplateFinder +import template_finder def isNumeric(val): return isinstance(val, (float, int, np.int32, np.int64, np.float32, np.float64)) @@ -262,7 +262,7 @@ class mouse: def _is_clicking_safe(): # Because of reports that botty lost equiped items, let's check if the inventory is open, and if it is, restrict the mouse move mouse_pos = screen.convert_monitor_to_screen(_mouse.get_position()) - is_inventory_open = TemplateFinder().search( + is_inventory_open = template_finder.search( "INVENTORY_GOLD_BTN", screen.grab(), threshold=0.8, diff --git a/src/utils/graphic_debugger.py b/src/utils/graphic_debugger.py index a1b25a7..e8007cc 100644 --- a/src/utils/graphic_debugger.py +++ b/src/utils/graphic_debugger.py @@ -9,7 +9,7 @@ from screen import grab from item import ItemFinder from config import Config import tkinter as tk -from template_finder import TemplateFinder +import template_finder from PIL import ImageTk, Image import re @@ -298,7 +298,7 @@ class GraphicDebuggerController: # Show Town A5 template matches scores = {} for template_name in search_templates: - template_match = TemplateFinder().search(template_name, img, threshold=0.65) + template_match = template_finder.search(template_name, img, threshold=0.65) if template_match.valid: scores[template_match.name] = template_match.score cv2.putText(combined_img, str(template_name), template_match.center, cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA) @@ -329,7 +329,7 @@ class GraphicDebuggerController: # Show Town A5 template matches scores = {} for template_name in search_templates: - template_match = TemplateFinder().search(template_name, img, threshold=0.65) + template_match = template_finder.search(template_name, img, threshold=0.65) if template_match.valid: scores[template_match.name] = template_match.score cv2.putText(combined_img, str(template_name), template_match.center, cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA) diff --git a/src/utils/misc.py b/src/utils/misc.py index 881e2ee..ed3a95c 100644 --- a/src/utils/misc.py +++ b/src/utils/misc.py @@ -180,15 +180,16 @@ def hms(seconds: int): s = seconds % 3600 % 60 return '{:02d}:{:02d}:{:02d}'.format(h, m, s) -def load_template(path, scale_factor: float = 1.0, alpha: bool = False): +def load_template(path): if os.path.isfile(path): try: - template_img = cv2.imread(path, cv2.IMREAD_UNCHANGED) if alpha else cv2.imread(path) - template_img = cv2.resize(template_img, None, fx=scale_factor, fy=scale_factor, interpolation=cv2.INTER_NEAREST) + template_img = cv2.imread(path, cv2.IMREAD_UNCHANGED) return template_img except Exception as e: print(e) raise ValueError(f"Could not load template: {path}") + else: + Logger.error(f"Template does not exist: {path}") return None def alpha_to_mask(img: np.ndarray): @@ -220,7 +221,7 @@ def image_is_equal(img1: np.ndarray, img2: np.ndarray) -> bool: Logger.debug("image_is_equal: Image shape is not equal") return False return not(np.bitwise_xor(img1, img2).any()) - + def arc_spread(cast_dir: Tuple[float,float], spread_deg: float=10, radius_spread: Tuple[float, float] = [.95, 1.05]): """ Given an x,y vec (target), generate a new target that is the same vector but rotated by +/- spread_deg/2 diff --git a/src/utils/node_recorder.py b/src/utils/node_recorder.py index e8e7a79..539b683 100644 --- a/src/utils/node_recorder.py +++ b/src/utils/node_recorder.py @@ -1,7 +1,7 @@ from screen import grab, convert_monitor_to_screen, convert_abs_to_screen import cv2 from config import Config -from template_finder import TemplateFinder +import template_finder from utils.misc import load_template, cut_roi import mouse import keyboard @@ -30,7 +30,7 @@ class NodeRecorder: self._half_height = Config().ui_pos["screen_height"] // 2 self._curr_state = 0 self._upper_left = None - TemplateFinder()._templates = {} + template_finder._templates = {} self._pather_code_file = "generated/pather_generated.py" self.ref_points = {} self.nodes = {} @@ -44,8 +44,8 @@ class NodeRecorder: def find_templates(self, img): ref_points = {} - for key in TemplateFinder()._templates: - found = TemplateFinder().search(key, img, use_grayscale=False, threshold=0.77) + for key in template_finder._templates: + found = template_finder.search(key, img, use_grayscale=False, threshold=0.77) if found.valid: ref_points[key] = found.center return ref_points @@ -76,8 +76,8 @@ class NodeRecorder: template_path = f"generated/templates/{self._run_name}/{ref_point_name}.png" cv2.imwrite(template_path, template_img) self._upper_left = None - template_img = load_template(template_path, 1.0, False) - TemplateFinder()._templates[ref_point_name] = [template_img, cv2.cvtColor(template_img, cv2.COLOR_BGRA2GRAY), 1.0, None] + template_img = load_template(template_path) + template_finder._templates[ref_point_name] = [template_img, cv2.cvtColor(template_img, cv2.COLOR_BGRA2GRAY), 1.0, None] elif e.name == "f7": self.ref_points = {} else: diff --git a/src/utils/restart.py b/src/utils/restart.py index 7374bae..2bf4404 100644 --- a/src/utils/restart.py +++ b/src/utils/restart.py @@ -2,7 +2,7 @@ import subprocess import os, sys import keyboard from bot import Bot -from template_finder import TemplateFinder +import template_finder from utils.misc import wait, set_d2r_always_on_top from screen import get_offset_state, grab from config import Config @@ -40,7 +40,7 @@ def restart_game(d2_path): wait(0.5, 1.0) - while not TemplateFinder().search(Bot._MAIN_MENU_MARKERS, grab(), best_match=True).valid: + while not template_finder.search(Bot._MAIN_MENU_MARKERS, grab(), best_match=True).valid: keyboard.send("space") wait(2.0, 4.0) attempts += 1 diff --git a/test/smoke_test.py b/test/smoke_test.py index 23a81ed..5d40c5a 100644 --- a/test/smoke_test.py +++ b/test/smoke_test.py @@ -2,7 +2,7 @@ from version import __version__ from logger import Logger from game_stats import GameStats from bot import Bot -from template_finder import TemplateFinder +import template_finder from mocks.screen_mock import ScreenMock diff --git a/test/template_finder_test.py b/test/template_finder_test.py index 6cacd3c..d7b1352 100644 --- a/test/template_finder_test.py +++ b/test/template_finder_test.py @@ -1,7 +1,8 @@ import cv2 import pytest -from template_finder import TemplateFinder +import template_finder from utils.misc import is_in_roi +import screen @pytest.mark.parametrize("template1_path, template2_path, template3_path, screen_path, expected_roi", [( "test/assets/stash_slot_empty.png", # empty stash slot @@ -11,6 +12,7 @@ from utils.misc import is_in_roi [38, 0, 38, 38]) # region of slash ]) def test_match_behavior(template1_path, template2_path, template3_path, screen_path, expected_roi): + screen.set_window_position(0, 0) image = cv2.imread(screen_path) empty = cv2.imread(template1_path) slash = cv2.imread(template2_path) @@ -22,7 +24,7 @@ def test_match_behavior(template1_path, template2_path, template3_path, screen_p - if cross matches above threshold as expected, then it won't bother to search for slash, which has a perfect match on the image - test passes if the template match score is not perfect """ - match = TemplateFinder().search([cross, slash], image, threshold) + match = template_finder.search([cross, slash], image, threshold) assert threshold <= match.score < 1 """ Test best match @@ -30,5 +32,12 @@ def test_match_behavior(template1_path, template2_path, template3_path, screen_p - searches next for slash, which perfectly matches on image - test passes if the center of the template match lies within the expected region of the slash """ - match = TemplateFinder().search([cross, slash], image, threshold=0.6, best_match=True) - assert is_in_roi(expected_roi, match.center) \ No newline at end of file + match = template_finder.search([cross, slash], image, threshold=0.6, best_match=True) + assert is_in_roi(expected_roi, match.center) + """ + Test all matches + - searches for empty slots with high threshold + - test passes if 3 matches result + """ + matches = template_finder.search_all(empty, image, threshold=0.98) + assert len(matches) == 3 \ No newline at end of file diff --git a/test/utils/misc_test.py b/test/utils/misc_test.py index e5c025b..2fe15b5 100644 --- a/test/utils/misc_test.py +++ b/test/utils/misc_test.py @@ -13,6 +13,6 @@ class TestUtilsMisc: ("some/random/path/that/not/a/file.png", False), ]) def test_load_template(self, path: str, should_be_success: bool): - template_img = load_template(path, 1.0, alpha=True) + template_img = load_template(path) success = template_img is not None assert(success == should_be_success)