A2 Pathing + Arcane run + Static for Sorc + Static Tele robustification + Chest class (#348)

This commit is contained in:
aeon0
2022-01-01 14:15:13 +01:00
committed by GitHub
parent 0fe0b55661
commit d8942f92b4
108 changed files with 492 additions and 52 deletions
+9 -2
View File
@@ -23,7 +23,7 @@ Open up D2R and wait till you are at the hero selection screen. Make sure the ch
### 4) Start Botty
You can either run from python. Follow [development.md](development.md) for that. Or you download the a prebuilt release [here](https://github.com/aeon0/botty/releases) (the .rar file!). Start `main.exe` in the botty folder. Focus your D2R window and press the start key (default f11). You can always force stop botty with f12.
You can either run from python. Follow [development.md](development.md) for that. Or you download the a prebuilt release [here](https://github.com/aeon0/botty/releases) (the .rar file!). Start `main.exe` in the botty folder. Focus your D2R window and press the start key (default f11). You can always force stop botty with f12. Note: Botty will use the /nopickup command in the first game to avoid pickup up trash while traversing. This command will only allow item pickup when "show items" is active.
## Graphic Debugger
@@ -70,6 +70,7 @@ run_shenk=0
| run_eldritch | Run Eldritch in each new game. Select "1" to run it "0" to leave it out. |
| run_shenk | Run shenk in each new game. Select "1" to run it "0" to leave it out. |
| run_nihlatak | Run Nihlatak in each new game. Select "1" to run it "0" to leave it out. (Teleport required) |
| run_arcane | Run Arcane Sanctuary in each new game. Select "1" to run it "0" to leave it out. (Teleport required) |
| [char] | Descriptions |
| ------------------ | -------------------------------------------------------------------------------------------------|
@@ -94,6 +95,7 @@ run_shenk=0
| stash_gold | Bool value to stash gold each time when stashing items |
| gold_trav_only | Hacky config that will restrict gold pickup to trav only. misc_gold must be set to 1 for this to have any effect |
| use_merc | Set to 1 for using merc. Set to 0 for not using merc (will not revive merc when dead), default = 1 |
| atk_len_arc | Attack length for hdin/sorc fighting arcane |
| atk_len_trav | Attack length for hdin fighting trav (note this atk length will be applied in 4 different spots each) |
| atk_len_pindle | Attack length for hdin or number of attack sequences for sorc when fighting pindle |
| atk_len_eldritch | Attack length for hdin or number of attack sequences for sorc when fighting eldritch |
@@ -112,7 +114,8 @@ run_shenk=0
| belt_mp_columns | Number of belt columns for mana potions |
| pre_buff_every_run | 0: Will only prebuff on first run, 1: Will prebuff after each run/boss |
| always_repair | 0: Will only repair when needed, 1: Will repair at the start of each run (EXPENSIVE FOR HIGH RUNEWORDS) |
| id_items | will identify items at cain before stashing them. Cain must be recused for this to work.|
| id_items | Will identify items at cain before stashing them. Cain must be rescued for this to work.|
| open_chests | Open up chests in some places. E.g. on dead ends of arcane. Note: currently bad runtime. |
### Builds
| [sorceress] | Descriptions |
@@ -132,6 +135,10 @@ run_shenk=0
| ice_blast | Optional Hotkey for ice_blast (must be bound to left skill) |
| blizzard | Required Hotkey for Blizzard (must be bound to right skill) |
| [nova_sorc] | Descriptions |
| ------------- | ----------------------------------------------------------------------------- |
| nova | Required Hotkey for Nova (must be bound to right skill) |
| [hammerdin] | Descriptions |
| -------------- | ----------------------------------------------------------------------------------- |
| teleport | Optional Hotkey for teleport. If left empty hammerdin will run instead of teleport. |
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

+7 -1
View File
@@ -86,7 +86,7 @@ item_width_min=20
; pather
reached_node_dist=100
min_walk_dist=25
max_walk_dist=127
max_walk_dist=177
[ui_roi]
; all rois are in [left, top, width, height] format
@@ -101,6 +101,7 @@ gold_btn_stash=150,480,40,65
health_globe=160,580,240,140
mana_globe=887,580,240,140
cut_skill_bar=0,0,1280,653
reduce_to_center=120,60,1040,540
merc_icon=0,0,100,100
loading_left_black=0,0,350,720
death=513,193,253,50
@@ -140,3 +141,8 @@ ni2_a_safe_dist=267,499, 279,533, 372,536, 283,529, 360,524, 513,553, 289,449, 1
ni2_b_safe_dist=864,373, 987,549, 842,521, 977,504, 886,453, 896,449, 861,461, 1206,279, 1068,569
ni2_c_safe_dist=810,98, 989,195, 969,250, 957,234, 962,179, 954,203, 1202,220, 985,89
ni2_d_safe_dist=449,165, 334,198, 377,183, 402,181, 382,196, 377,230, 9,288, 290,104
# Arcane Sanctuary Layouts
arc_top_right=1236,43, 1236,43, 1241,39, 1060,179, 1240,669, 1208,649, 1109,63, 1181,60, 1221,64, 1237,51, 1239,43, 1226,48, 1234,48, 1246,60, 93,50, 115,65, 1203,36, 1169,198
arc_bottom_left=60,653, 60,653, 60,653, 254,559, 1249,634, 1209,628, 54,613, 58,613, 69,658, 74,643, 74,643, 74,643, 74,643, 89,614, 17,122, 103,146, 20,495, 88,624
arc_top_left=86,55, 98,69, 110,111, 135,69, 1195,97, 1154,89, 95,81, 93,88, 93,88, 92,88, 92,88, 92,88, 92,88, 92,88, 92,88, 63,654, 103,609, 101,63, 133,104
arc_bottom_right=1230,649, 1189,685, 1201,632, 992,503, 1161,45, 1166,55, 1236,607, 1233,617, 1162,663, 1169,650, 1224,627, 1221,613, 1224,638, 1254,627, 60,691, 63,617, 1234,701, 1199,668
+4 -1
View File
@@ -25,8 +25,9 @@ run_pindle=1
run_eldritch=1
run_shenk=0
run_nihlatak=0
run_arcane=0
; Note: You can not split up or change order of eldritch and shenk as botty treats it as a single run
order=run_trav, run_pindle, run_eldritch, run_shenk, run_nihlatak
order=run_trav, run_pindle, run_eldritch, run_shenk, run_nihlatak, run_arcane
[char]
; ==========================
@@ -60,6 +61,7 @@ stash_gold=1
gold_trav_only=0
use_merc=1
; Attack length for barbarians should be as high as 8-10 and even 10-12 for trav/shenk
atk_len_arc=2.5
atk_len_trav=3.0
atk_len_pindle=3.0
atk_len_eldritch=3.0
@@ -79,6 +81,7 @@ belt_mp_columns=1
pre_buff_every_run=0
always_repair=0
id_items=0
open_chests=1
; ===========================
; ==== Builds: Sorceress ====
+1
View File
@@ -45,6 +45,7 @@ misc_greater_mana_potion=0
;misc
misc_gold=0
misc_key_of_destruction=1
misc_key_of_hate=1
;---------------------------------------;
;================= GEMS ================;
+21 -7
View File
@@ -28,8 +28,8 @@ from char.sorceress import LightSorc, BlizzSorc, NovaSorc
from char.trapsin import Trapsin
from char.hammerdin import Hammerdin
from char.barbarian import Barbarian
from run import Pindle, ShenkEld, Trav, Nihlatak
from town import TownManager, A3, A4, A5
from run import Pindle, ShenkEld, Trav, Nihlatak, Arcane
from town import TownManager, A2, A3, A4, A5
# Added for dclone ip hunt
from messenger import Messenger
@@ -70,7 +70,8 @@ class Bot:
a5 = A5(self._screen, self._template_finder, self._pather, self._char, npc_manager)
a4 = A4(self._screen, self._template_finder, self._pather, self._char, npc_manager)
a3 = A3(self._screen, self._template_finder, self._pather, self._char, npc_manager)
self._town_manager = TownManager(self._template_finder, self._ui_manager, self._item_finder, a3, a4, a5)
a2 = A2(self._screen, self._template_finder, self._pather, self._char, npc_manager)
self._town_manager = TownManager(self._template_finder, self._ui_manager, self._item_finder, a2, a3, a4, a5)
self._route_config = self._config.routes
self._route_order = self._config.routes_order
@@ -83,6 +84,7 @@ class Bot:
"run_pindle": self._route_config["run_pindle"],
"run_shenk": self._route_config["run_shenk"] or self._route_config["run_eldritch"],
"run_nihlatak": self._route_config["run_nihlatak"],
"run_arcane": self._route_config["run_arcane"],
}
# Adapt order to the config
self._do_runs = OrderedDict((k, self._do_runs[k]) for k in self._route_order if k in self._do_runs and self._do_runs[k])
@@ -94,6 +96,7 @@ class Bot:
self._shenk = ShenkEld(self._template_finder, self._pather, self._town_manager, self._ui_manager, self._char, self._pickit)
self._trav = Trav(self._template_finder, self._pather, self._town_manager, self._ui_manager, self._char, self._pickit)
self._nihlatak = Nihlatak(self._screen, self._template_finder, self._pather, self._town_manager, self._ui_manager, self._char, self._pickit)
self._arcane = Arcane(self._template_finder, self._pather, self._town_manager, self._ui_manager, self._char, self._pickit)
# Create member variables
self._pick_corpse = pick_corpse
@@ -108,7 +111,7 @@ class Bot:
self._ran_no_pickup = False
# Create State Machine
self._states=['hero_selection', 'town', 'pindle', 'shenk', 'trav', 'nihlatak']
self._states=['hero_selection', 'town', 'pindle', 'shenk', 'trav', 'nihlatak', 'arcane']
self._transitions = [
{ 'trigger': 'create_game', 'source': 'hero_selection', 'dest': 'town', 'before': "on_create_game"},
# Tasks within town
@@ -118,9 +121,10 @@ class Bot:
{ 'trigger': 'run_shenk', 'source': 'town', 'dest': 'shenk', 'before': "on_run_shenk"},
{ 'trigger': 'run_trav', 'source': 'town', 'dest': 'trav', 'before': "on_run_trav"},
{ 'trigger': 'run_nihlatak', 'source': 'town', 'dest': 'nihlatak', 'before': "on_run_nihlatak"},
{ 'trigger': 'run_arcane', 'source': 'town', 'dest': 'arcane', 'before': "on_run_arcane"},
# End run / game
{ 'trigger': 'end_run', 'source': ['shenk', 'pindle', 'nihlatak','trav'], 'dest': 'town', 'before': "on_end_run"},
{ 'trigger': 'end_game', 'source': ['town', 'shenk', 'pindle', 'nihlatak', 'trav', 'end_run'], 'dest': 'hero_selection', 'before': "on_end_game"},
{ 'trigger': 'end_run', 'source': ['shenk', 'pindle', 'nihlatak', 'trav', 'arcane'], 'dest': 'town', 'before': "on_end_run"},
{ 'trigger': 'end_game', 'source': ['town', 'shenk', 'pindle', 'nihlatak', 'trav', 'arcane', 'end_run'], 'dest': 'hero_selection', 'before': "on_end_game"},
]
self.machine = Machine(model=self, states=self._states, initial="hero_selection", transitions=self._transitions, queued=True)
@@ -258,7 +262,7 @@ class Bot:
# Check if we are out of tps or need repairing
need_repair = self._ui_manager.repair_needed()
if self._tps_left < random.randint(2, 5) or need_repair or self._config.char["always_repair"]:
if self._tps_left < random.randint(3, 5) or need_repair or self._config.char["always_repair"]:
if need_repair: Logger.info("Repair needed. Gear is about to break")
else: Logger.info("Repairing and buying TPs at next Vendor")
self._curr_loc = self._town_manager.repair_and_fill_tps(self._curr_loc)
@@ -360,3 +364,13 @@ class Bot:
if self._curr_loc:
res = self._nihlatak.battle(not self._pre_buffed)
self._ending_run_helper(res)
def on_run_arcane(self):
res = False
self._do_runs["run_arcane"] = False
self._game_stats.update_location("Arc" if self._config.general['discord_status_condensed'] else "Arcane")
self._curr_loc = self._arcane.approach(self._curr_loc)
if self._curr_loc:
res = self._arcane.battle(not self._pre_buffed)
self._tps_left -= self._arcane.used_tps
self._ending_run_helper(res)
+14
View File
@@ -143,6 +143,20 @@ class Hammerdin(IChar):
self._cast_hammers(1.2, "redemption")
return True
def kill_summoner(self) -> bool:
# move mouse to below altar
pos_m = self._screen.convert_abs_to_monitor((0, 20))
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
# Attack
self._cast_hammers(self._char_config["atk_len_arc"])
wait(0.1, 0.15)
self._cast_hammers(1.6, "redemption")
# Move a bit back and another round
self._move_and_attack((0, 80), self._char_config["atk_len_arc"] * 0.5)
wait(0.1, 0.15)
self._cast_hammers(1.6, "redemption")
return True
if __name__ == "__main__":
import os
+5 -2
View File
@@ -42,7 +42,8 @@ class IChar:
template_type: Union[str, List[str]],
success_func: Callable = None,
time_out: float = 8,
threshold: float = 0.68
threshold: float = 0.68,
telekinesis: bool = False
) -> bool:
"""
Finds any template from the template finder and interacts with it
@@ -192,7 +193,6 @@ class IChar:
Logger.warning("Failed to switch weapon, try again")
wait(0.5)
def pre_buff(self):
pass
@@ -210,3 +210,6 @@ class IChar:
def kill_nihlatak(self, end_nodes: list[int]) -> bool:
raise ValueError("Nihlatak is not implemented!")
def kill_summoner(self) -> bool:
raise ValueError("Arcane is not implemented!")
+13 -1
View File
@@ -220,7 +220,19 @@ class BlizzSorc(Sorceress):
self._blizzard((0, 0), spray=10)
return True
def kill_summoner(self) -> bool:
# Attack
delay = [0.2, 0.3]
cast_pos_abs = np.array([0, 0])
pos_m = self._screen.convert_abs_to_monitor((-20, 20))
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
for _ in range(int(self._char_config["atk_len_arc"])):
self._right_attack(cast_pos_abs, delay, 11)
self._left_attack(cast_pos_abs, delay, 11)
wait(self._cast_duration, self._cast_duration + 0.2)
return True
if __name__ == "__main__":
import os
import keyboard
+11
View File
@@ -145,6 +145,17 @@ class LightSorc(Sorceress):
self._pather.traverse_nodes(end_nodes, self, time_out=0.8)
return True
def kill_summoner(self) -> bool:
# Attack
delay = [0.2, 0.3]
cast_pos_abs = np.array([0, 0])
pos_m = self._screen.convert_abs_to_monitor((-20, 20))
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
for _ in range(int(self._char_config["atk_len_arc"])):
self._right_attack(cast_pos_abs, delay, 11)
self._left_attack(cast_pos_abs, delay, 11)
wait(self._cast_duration, self._cast_duration + 0.2)
return True
if __name__ == "__main__":
import os
+14 -4
View File
@@ -41,15 +41,13 @@ class NovaSorc(Sorceress):
def kill_eldritch(self) -> bool:
self._pather.traverse_nodes_fixed([(675, 30)], self)
self._cast_static(0.6)
atk_len = max(1.3, self._char_config["atk_len_eldritch"] - 0.7)
self._nova(atk_len)
self._nova(self._char_config["atk_len_eldritch"])
return True
def kill_shenk(self) -> bool:
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, time_out=1.0)
self._cast_static(0.6)
atk_len = max(1.5, self._char_config["atk_len_shenk"] - 1.0)
self._nova(atk_len)
self._nova(self._char_config["atk_len_shenk"])
return True
def kill_council(self) -> bool:
@@ -84,6 +82,18 @@ class NovaSorc(Sorceress):
self._move_and_attack((-70, -35), atk_len)
return True
def kill_summoner(self) -> bool:
# move mouse to below altar
pos_m = self._screen.convert_abs_to_monitor((0, 20))
mouse.move(*pos_m, randomize=80, delay_factor=[0.5, 0.7])
# Attack
self._nova(self._char_config["atk_len_arc"])
# Move a bit back and another round
self._move_and_attack((0, 80), self._char_config["atk_len_arc"] * 0.5)
wait(0.1, 0.15)
self._nova(self._char_config["atk_len_arc"] * 0.5)
return True
if __name__ == "__main__":
import os
+34
View File
@@ -1,4 +1,5 @@
import keyboard
from typing import Dict, Tuple, Union, List, Callable
from utils.custom_mouse import mouse
from char import IChar
from template_finder import TemplateFinder
@@ -33,6 +34,39 @@ class Sorceress(IChar):
else:
return super().pick_up_item(pos, item_name, prev_cast_start)
def select_by_template(
self,
template_type: Union[str, List[str]],
success_func: Callable = None,
time_out: float = 8,
threshold: float = 0.68,
telekinesis: bool = False
) -> bool:
# In case telekinesis is False or hotkey is not set, just call the base implementation
if not self._skill_hotkeys["telekinesis"] and telekinesis:
return super().select_by_template(template_type, success_func, time_out, threshold)
if type(template_type) == list and "A5_STASH" in template_type:
# sometimes waypoint is opened and stash not found because of that, check for that
if self._template_finder.search("WAYPOINT_MENU", self._screen.grab()).valid:
keyboard.send("esc")
start = time.time()
while time_out is None or (time.time() - start) < time_out:
template_match = self._template_finder.search(template_type, self._screen.grab(), threshold=threshold)
if template_match.valid:
x_m, y_m = self._screen.convert_screen_to_monitor(template_match.position)
keyboard.send(self._skill_hotkeys["telekinesis"])
wait(0.1, 0.2)
mouse.move(x_m, y_m)
wait(0.2, 0.3)
mouse.click(button="right")
# check the successfunction for 2 sec, if not found, try again
check_success_start = time.time()
while time.time() - check_success_start < 2:
if success_func is None or success_func():
return True
# In case telekinesis fails, try again with the base implementation
return super().select_by_template(template_type, success_func, time_out, threshold)
def pre_buff(self):
if self._char_config["cta_available"]:
self._pre_buff_cta()
-1
View File
@@ -150,4 +150,3 @@ if __name__ == "__main__":
pather = Pather(screen, t_finder)
ui_manager = UiManager(screen, t_finder)
char = Trapsin(config.trapsin, config.char, screen, t_finder, ui_manager, pather)
+80
View File
@@ -0,0 +1,80 @@
import time
import os
from logger import Logger
from template_finder import TemplateFinder
from screen import Screen
from char import IChar
from config import Config
from utils.custom_mouse import mouse
from utils.misc import wait
class Chest:
def __init__(self, char: IChar, template_finder: TemplateFinder, template: str = None):
self._config = Config()
self._screen = Screen(self._config.general["monitor"])
self._char = char
self._template_finder = template_finder
self._folder_name = "chests"
# load all templates
self._templates = []
for filename in os.listdir(f'assets/{self._folder_name}/{template}'):
filename = filename.lower()
if filename.endswith('.png'):
chest = filename[:-4].upper()
self._templates.append(chest)
def open_up_chests(self, time_out: float = 8.0, threshold: float = 0.73) -> bool:
Logger.debug("Open chests")
templates = self._templates
found_chest = True
start = time.time()
while time.time() - start < time_out:
template_match = self._template_finder.search(templates, self._screen.grab(), roi=self._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:
break
else:
found_chest = True
x_m, y_m = self._screen.convert_screen_to_monitor(template_match.position)
# move mouse and check for label
mouse.move(x_m, y_m, delay_factor=[0.4, 0.6])
wait(0.13, 0.16)
chest_label = self._template_finder.search("CHEST_LABEL", self._screen.grab(), threshold=0.85)
if chest_label.valid:
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([x_m, y_m], 'potion')
wait(0.13, 0.16)
locked_chest = self._template_finder.search("LOCKED", self._screen.grab(), threshold=0.85)
if locked_chest.valid:
templates.remove(template_match.name)
Logger.debug("No more keys, removing locked chest template")
continue
else:
templates.remove(template_match.name)
Logger.debug("No chests left")
return found_chest
if __name__ == "__main__":
import keyboard
import os
keyboard.add_hotkey('f12', lambda: Logger.info('Force Exit (f12)') or os._exit(1))
print("Move to d2r window and press f11")
keyboard.wait("f11")
from char.hammerdin import Hammerdin
from screen import Screen
from pather import Pather
from config import Config
from ui import UiManager
config = Config()
screen = Screen(config.general["monitor"])
template_finder = TemplateFinder(screen)
pather = Pather(screen, template_finder)
ui_manager = UiManager(screen, template_finder)
char = Hammerdin(config.hammerdin, config.char, screen, template_finder, ui_manager, pather)
chest = Chest(char, template_finder, 'arcane')
chest.open_up_chests(threshold=0.8)
+3 -1
View File
@@ -104,13 +104,15 @@ class Config:
"stash_gold": bool(int(self._select_val("char", "stash_gold"))),
"gold_trav_only": bool(int(self._select_val("char", "gold_trav_only"))),
"use_merc": bool(int(self._select_val("char", "use_merc"))),
"id_items": bool(int(self._select_val("char", "ID_items"))),
"id_items": bool(int(self._select_val("char", "id_items"))),
"open_chests": bool(int(self._select_val("char", "open_chests"))),
"pre_buff_every_run": bool(int(self._select_val("char", "pre_buff_every_run"))),
"cta_available": bool(int(self._select_val("char", "cta_available"))),
"weapon_switch": self._select_val("char", "weapon_switch"),
"battle_orders": self._select_val("char", "battle_orders"),
"battle_command": self._select_val("char", "battle_command"),
"casting_frames": int(self._select_val("char", "casting_frames")),
"atk_len_arc": float(self._select_val("char", "atk_len_arc")),
"atk_len_trav": float(self._select_val("char", "atk_len_trav")),
"atk_len_pindle": float(self._select_val("char", "atk_len_pindle")),
"atk_len_eldritch": float(self._select_val("char", "atk_len_eldritch")),
+1 -1
View File
@@ -48,7 +48,7 @@ class HealthManager:
def update_location(self, loc: Location):
if loc is not None and type(loc) == str:
bosses = ["shenk", "eldritch", "pindle", "nihlatak", "trav"]
bosses = ["shenk", "eldritch", "pindle", "nihlatak", "trav", "arc"]
prev_value = self._pausing
self._pausing = not any(substring in loc for substring in bosses)
if self._pausing != prev_value:
+1 -1
View File
@@ -145,4 +145,4 @@ if __name__ == "__main__":
# cv2.putText(img, item.name, item.center, cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 255, 255), 1, cv2.LINE_AA)
# img = cv2.resize(img, None, fx=0.5, fy=0.5)
cv2.imshow('test', img)
cv2.waitKey(1)
cv2.waitKey(1)
+12
View File
@@ -16,6 +16,7 @@ class Npc:
ANYA = "anya"
TYRAEL = "tyrael"
ORMUS = "ormus"
FARA = "fara"
CAIN = "cain"
class NpcManager:
@@ -90,6 +91,17 @@ class NpcManager:
},
"template_group": ["ORMUS_0", "ORMUS_1", "ORMUS_2", "ORMUS_3", "ORMUS_4", "ORMUS_5"]
},
Npc.FARA: {
"name_tag_white": color_filter(self._template_finder.get_template("FARA_NAME_TAG_WHITE"), self._config.colors["white"])[1],
"name_tag_gold": color_filter(self._template_finder.get_template("FARA_NAME_TAG_GOLD"), self._config.colors["gold"])[1],
"action_btns": {
"trade_repair": {
"white": color_filter(self._template_finder.get_template("TRADE_REPAIR"), self._config.colors["white"])[1],
"blue": color_filter(self._template_finder.get_template("TRADE_REPAIR_BLUE"), self._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"]
},
# Currently just nametag added to avoid having him focused when wanting to talk to tyrael or qual
#added in the ability to ID items at cain.
Npc.CAIN: {
+43 -7
View File
@@ -46,12 +46,21 @@ class Location:
A3_ORMUS = "a3_ormus"
A3_STASH_WP = "a3_stash_wp"
A3_ASHEARA = "a3_asheara"
# A2 Town
A2_TOWN_START = "a2_town_start"
A2_WP = "a2_wp"
A2_TP = "a2_tp"
A2_FARA_STASH = "a2_fara_stash"
# Trav
A3_TRAV_START = "a3_trav_start"
A3_TRAV_CENTER_STAIRS = "a3_trav_center_stairs"
# Nihalatk
A5_NIHLATAK_START = "a5_nihlatak_lvl1_start"
A5_NIHLATAK_END = "a5_nihlatak_lvl2_end"
# Arcane
A2_ARC_START = "a2_arc_start"
A2_ARC_CHECKPOINT = "a2_arc_checkpoint"
A2_ARC_END = "a2_arc_end"
class Pather:
@@ -126,6 +135,15 @@ class Pather:
190: {"A3_TOWN_12": (27, -198), "A3_TOWN_16": (831, 258), "A3_TOWN_14": (-318, 185), "A3_TOWN_15": (411, -9), "A3_TOWN_5": (-301, -334), "A3_TOWN_13": (-543, -10), "A3_TOWN_17": (707, 541)},
191: {"A3_TOWN_15": (-79, -256), "A3_TOWN_16": (341, 11), "A3_TOWN_17": (217, 294), "A3_TOWN_18": (542, 408), "A3_TOWN_19": (779, 171)},
192: {"A3_TOWN_17": (-187, 78), "A3_TOWN_16": (-63, -205), "A3_TOWN_18": (138, 192), "A3_TOWN_19": (375, -45)},
# A2 town
400: {"A2_TOWN_2": (-169, 160), "A2_TOWN_0": (290, -114), "A2_TOWN_3": (-348, 225), "A2_TOWN_1": (-345, -374)},
401: {"A2_TOWN_3": (45, -202), "A2_TOWN_7": (-40, 266), "A2_TOWN_2": (224, -267), "A2_TOWN_6": (-505, 26), "A2_TOWN_5": (116, 203)},
402: {"A2_TOWN_8": (182, 121), "A2_TOWN_6": (103, -348), "A2_TOWN_11": (128, 357), "A2_TOWN_9": (-324, 223), "A2_TOWN_12": (-213, 500)},
403: {"A2_TOWN_11": (161, -18), "A2_TOWN_12": (-180, 125), "A2_TOWN_9": (-291, -152), "A2_TOWN_8": (215, -254), "A2_TOWN_13": (149, 392)},
404: {"A2_TOWN_14": (79, 190), "A2_TOWN_15": (244, -12), "A2_TOWN_13": (-270, 123), "A2_TOWN_11": (-258, -287), "A2_TOWN_12": (-599, -143)},
405: {"A2_TOWN_10": (65, -175), "A2_TOWN_17": (-108, 164), "A2_TOWN_16": (-304, -11), "A2_TOWN_9": (319, -68), "A2_TOWN_18": (-415, -284)},
406: {"A2_TOWN_18": (108, -143), "A2_TOWN_16": (219, 129), "A2_TOWN_19": (-293, 21), "A2_TOWN_17": (415, 304), "A2_TOWN_10": (588, -34)},
408: {"A2_TOWN_20": (-26, -109), "A2_TOWN_22": (-82, 278), "A2_TOWN_19": (344, 38), "A2_TOWN_21": (-518, -299), "A2_TOWN_18": (745, -125)},
# Trav
220: {"TRAV_0": (445, 384), "TRAV_20": (-259, 267), "TRAV_1": (-248, -139), "TRAV_2": (-682, 21), "TRAV_21": (25, 180)},
221: {"TRAV_2": (-153, -101), "TRAV_3": (-125, 201), "TRAV_20": (270, 145), "TRAV_1": (281, -261), "TRAV_4": (-459, 122)},
@@ -142,6 +160,13 @@ class Pather:
301: {"TRAV_V3_7": (63, -113), "TRAV_V3_6": (55, 77), "TRAV_V3_0": (-27, -315), "TRAV_V3_5": (-450, -175), "TRAV_V3_8": (329, -188)},
302: {"TRAV_V3_0": (-18, 6), "TRAV_V3_7": (73, 208), "TRAV_V3_8": (339, 133), "TRAV_V3_6": (65, 398), "TRAV_V3_5": (-440, 146)},
304: {"TRAV_V2_4": (125, -148), "TRAV_V2_3": (-187, 55), "TRAV_V2_1": (-207, 59), "TRAV_V2_2": (267, 183), "TRAV_V2_0": (-159, 403)},
# Arcane
450: {"ARC_START": (49, 62)},
453: {"ARC_START": (-259, 62)},
456: {"ARC_START": (145, 264)},
459: {"ARC_START": (-356, 258)},
461: {"ARC_ALTAR": (60, 120), "ARC_ALTAR3": (-272, 200), "ARC_CENTER_2": (67, 41), "ARC_END_STAIRS": (76, -344), "ARC_END_STAIRS_2": (60, -160)},
462: {"ARC_PLATFORM_1": (0, -100), "ARC_PLATFORM_2": (50, -100), "ARC_PLATFORM_3": (70, 0)},
# Nil - End of Arm A
500: {"NIL2A_0": (-200, 1), "NIL2A_2": (-181, -102), "NIL2A_1": (329, 146), "NIL2A_4": (835, 24), "NIL2A_5": (-384, -20), "NIL2A_6": (-600, 410)},
501: {"NIL2A_4": (270+20, 259-50), "NIL2A_1": (-236+20, 381-50), "NIL2A_2": (-746+20, 133-50), "NIL2A_0": (-765+20, 236-50)},
@@ -176,7 +201,7 @@ class Pather:
(Location.A5_QUAL_KEHK, Location.A5_NIHLATHAK_PORTAL): [12, 11, 10, 6, 8, 9],
(Location.A5_QUAL_KEHK, Location.A5_WP): [12, 11, 10, 6],
(Location.A5_QUAL_KEHK, Location.A5_STASH): [12, 11, 10, 6, 5],
(Location.A5_LARZUK, Location.A5_QUAL_KEHK): [13, 14, 5, 6, 10, 11, 12],
(Location.A5_LARZUK, Location.A5_QUAL_KEHK): [13, 14, 5, 6, 10, 11, 12],
(Location.A5_LARZUK, Location.A5_NIHLATHAK_PORTAL): [14, 13, 5, 6, 8, 9],
(Location.A5_LARZUK, Location.A5_WP): [14, 13, 5],
# Pindle
@@ -201,6 +226,12 @@ class Pather:
(Location.A3_ASHEARA, Location.A3_STASH_WP): [191, 190, 189, 185, 186, 187, 188],
(Location.A3_STASH_WP, Location.A3_STASH_WP): [188],
(Location.A3_STASH_WP, Location.A3_ORMUS): [187, 186, 185],
# A2 Town
(Location.A2_TOWN_START, Location.A2_WP): [400, 401, 402, 403, 404],
(Location.A2_TOWN_START, Location.A2_FARA_STASH): [400, 401, 402, 405],
(Location.A2_FARA_STASH, Location.A2_WP): [403, 404],
(Location.A2_TP, Location.A2_FARA_STASH): [408, 406, 405],
(Location.A2_WP, Location.A2_FARA_STASH): [404, 403, 405],
# Trav
(Location.A3_TRAV_START, Location.A3_TRAV_CENTER_STAIRS): [220, 221, 222, 223, 224, 225, 226],
}
@@ -226,7 +257,7 @@ class Pather:
def _convert_rel_to_abs(rel_loc: Tuple[float, float], pos_abs: Tuple[float, float]) -> Tuple[float, float]:
return (rel_loc[0] + pos_abs[0], rel_loc[1] + pos_abs[1])
def traverse_nodes_fixed(self, key: Union[str, List[Tuple[float, float]]], char: IChar):
def traverse_nodes_fixed(self, key: Union[str, List[Tuple[float, float]]], char: IChar) -> bool:
if not char.can_teleport():
error_msg = "Teleport is requiered for static pathing"
Logger.error(error_msg)
@@ -237,6 +268,7 @@ class Pather:
else:
path = key
i = 0
stuck_count = 0
while i < len(path):
x_m, y_m = self._screen.convert_screen_to_monitor(path[i])
x_m += int(random.random() * 6 - 3)
@@ -252,9 +284,13 @@ class Pather:
if score > 0.15:
i += 1
else:
stuck_count += 1
Logger.debug(f"Teleport cancel detected. Try same teleport action again. ({score:.4f})")
if stuck_count >= 5:
return False
# if type(key) == str and ("_save_dist" in key or "_end" in key):
# cv2.imwrite(f"./info_screenshots/nil_path_{key}_" + time.strftime("%Y%m%d_%H%M%S") + ".png", self._screen.grab())
return True
def _adjust_abs_range_to_screen(self, abs_pos: Tuple[float, float]) -> Tuple[float, float]:
"""
@@ -367,7 +403,7 @@ class Pather:
Logger.error("Got stuck exit pather")
return False
# Sometimes we get stuck at rocks and stuff, after 2.5 seconds force a move into the last know direction
# Sometimes we get stuck at rocks and stuff, after a few seconds force a move into the last know direction
if not did_force_move and time.time() - last_move > 3.1:
pos_abs = (0, 150)
if last_direction is not None:
@@ -425,7 +461,7 @@ if __name__ == "__main__":
x, y = pather._screen.convert_abs_to_screen(ref_pos_abs)
cv2.circle(display_img, (x, y), 5, (0, 255, 0), 3)
cv2.putText(display_img, template_type, (x, y), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
# display_img = cv2.resize(display_img, None, fx=0.5, fy=0.5)
display_img = cv2.resize(display_img, None, fx=0.5, fy=0.5)
cv2.imshow("debug", display_img)
cv2.waitKey(1)
@@ -441,7 +477,7 @@ if __name__ == "__main__":
t_finder = TemplateFinder(screen)
pather = Pather(screen, t_finder)
display_all_nodes(pather, "ELD")
display_all_nodes(pather, "A2_TOWN")
# # changing node pos and generating new code
# code = ""
@@ -455,5 +491,5 @@ if __name__ == "__main__":
ui_manager = UiManager(screen, t_finder)
char = Hammerdin(config.hammerdin, config.char, screen, t_finder, ui_manager, pather)
pather.traverse_nodes_fixed("trav_safe_dist", char)
# pather.traverse_nodes([226, 228, 229], char)
# pather.traverse_nodes([452], char)
pather.traverse_nodes_fixed("arc_top_right", char)
+1
View File
@@ -2,3 +2,4 @@ from .pindle import Pindle
from .shenk_eld import ShenkEld
from .trav import Trav
from .nihlatak import Nihlatak
from .arcane import Arcane
+125
View File
@@ -0,0 +1,125 @@
from char.i_char import IChar
from config import Config
from logger import Logger
from pather import Location, Pather
from typing import Union
from item.pickit import PickIt
from template_finder import TemplateFinder
from town.town_manager import TownManager
from ui import UiManager
from utils.misc import wait
from dataclasses import dataclass
from chest import Chest
class Arcane:
def __init__(
self,
template_finder: TemplateFinder,
pather: Pather,
town_manager: TownManager,
ui_manager: UiManager,
char: IChar,
pickit: PickIt
):
self._config = Config()
self._template_finder = template_finder
self._pather = pather
self._town_manager = town_manager
self._ui_manager = ui_manager
self._char = char
self._pickit = pickit
self._chest = Chest(self._char, self._template_finder, 'arcane')
self.used_tps = 0
def approach(self, start_loc: Location) -> Union[bool, Location]:
Logger.info("Run Arcane")
if not self._char.can_teleport():
raise ValueError("Arcane requires teleport")
if not self._town_manager.open_wp(start_loc):
return False
wait(0.4)
self._ui_manager.use_wp(2, 7)
return Location.A2_ARC_START
def _find_summoner(self, traverse_to_summoner: list[tuple[float, float]]) -> bool:
# 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 = self._template_finder.search_and_wait(templates_platform, threshold=0.55, time_out=0.5, use_grayscale=True, take_ss=False)
match_summoner = self._template_finder.search_and_wait(tempaltes_summoner, threshold=0.79, time_out=0.5, use_grayscale=True, take_ss=False)
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 = self._template_finder.search_and_wait(tempaltes_summoner, threshold=0.79, time_out=1.0, use_grayscale=True, take_ss=False)
if match_summoner.valid:
if self._pather.traverse_nodes([461], self._char, time_out=2.2, force_tp=True):
return True
else:
# Traverse to center of platform
self._pather.traverse_nodes([462], self._char, time_out=1.3, force_tp=True)
return False
def battle(self, do_pre_buff: bool) -> Union[bool, tuple[Location, bool]]:
picked_up_items = False
@dataclass
class PathData:
calib_node_start: int
static_path_forward: str
jump_to_summoner: list[tuple[float, float]]
path_arr = [
PathData(450, "arc_top_right", [(500, 40)]),
PathData(453, "arc_top_left", [(500, 40)]),
PathData(456, "arc_bottom_right", [(500, 40)]),
PathData(459, "arc_bottom_left", [(500, 20), (700, 100)])
]
picked_up_items = False
self.used_tps = 0
if do_pre_buff:
self._char.pre_buff()
for i, data in enumerate(path_arr):
# calibrating at start and moving towards the end of the arm
self._pather.traverse_nodes([data.calib_node_start], self._char, force_tp=True)
if not self._pather.traverse_nodes_fixed(data.static_path_forward, self._char):
return False
found = self._find_summoner(data.jump_to_summoner)
# Kill the summoner or trash mob
self._char.kill_summoner()
if self._config.char["open_chests"]:
self._chest.open_up_chests()
picked_up_items |= self._pickit.pick_up_items(self._char)
if found:
return (Location.A2_ARC_END, picked_up_items)
elif i < len(path_arr) - 1:
# Open TP and return back to town, walk to wp and start over
if not self._char.tp_town():
Logger.warning("TP to town failed, cancel run")
self.used_tps += 20
return False
self.used_tps += 1
if not self._town_manager.wait_for_tp(Location.A2_TP):
return False
if not self.approach(Location.A2_FARA_STASH):
return False
return False
if __name__ == "__main__":
from screen import Screen
import keyboard
from game_stats import GameStats
import os
keyboard.add_hotkey('f12', lambda: os._exit(1))
keyboard.wait("f11")
from config import Config
from ui import UiManager
from bot import Bot
config = Config()
screen = Screen(config.general["monitor"])
game_stats = GameStats()
bot = Bot(screen, game_stats, False)
bot._arcane._find_summoner([(500, 40)])
+1 -1
View File
@@ -107,4 +107,4 @@ class Nihlatak:
self._char.kill_nihlatak(end_nodes)
wait(0.2, 0.3)
picked_up_items = self._pickit.pick_up_items(self._char)
return (Location.A5_NIHLATAK_END, picked_up_items)
return (Location.A5_NIHLATAK_END, picked_up_items)
+1 -1
View File
@@ -117,7 +117,7 @@ if __name__ == "__main__":
p1 = (x, y)
p2 = (x+w, y+h)
cv2.rectangle(test_img, p1, p2, (0, 255, 0), 2)
cv2.putText(test_img, roi_key, (p1[0], p1[1]+20), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 1, cv2.LINE_AA)
cv2.putText(test_img, roi_key, (p1[0], p1[1]+20), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 1, cv2.LINE_AA)
if show_pt:
pass
+6 -5
View File
@@ -22,7 +22,7 @@ class TemplateFinder:
Loads images from assets/templates and assets/npc and provides search functions
to find these assets within another image
"""
def __init__(self, screen: Screen, template_pathes: list[str] = ["assets\\templates", "assets\\npc", "assets\\item_properties"]):
def __init__(self, screen: Screen, template_pathes: list[str] = ["assets\\templates", "assets\\npc", "assets\\item_properties", "assets\\chests"]):
self._screen = screen
self._config = Config()
self.last_res = None
@@ -185,18 +185,19 @@ if __name__ == "__main__":
config = Config()
screen = Screen(config.general["monitor"])
template_finder = TemplateFinder(screen)
search_templates = ["REPAIR_NEEDED"]
search_templates = ["ARC_ALTAR", "ARC_ALTAR2"]
while 1:
# img = cv2.imread("")
img = screen.grab()
display_img = img.copy()
start = time.time()
for key in search_templates:
template_match = template_finder.search(key, img, best_match=True, threshold=0.35, use_grayscale=True)
template_match = template_finder.search(key, img, best_match=True, threshold=0.5, use_grayscale=True)
if template_match.valid:
cv2.putText(display_img, str(template_match.name), template_match.position, cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2, cv2.LINE_AA)
cv2.putText(display_img, str(template_match.name), template_match.position, cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv2.LINE_AA)
cv2.circle(display_img, template_match.position, 7, (255, 0, 0), thickness=5)
print(f"Name: {template_match.name} Pos: {template_match.position}, Score: {template_match.score}")
x, y = template_match.position
print(f"Name: {template_match.name} Pos: {template_match.position}, Dist: {625-x, 360-y}, Score: {template_match.score}")
# print(time.time() - start)
display_img = cv2.resize(display_img, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_NEAREST)
cv2.imshow('test', display_img)
+1
View File
@@ -1,3 +1,4 @@
from .a2 import A2
from .a3 import A3
from .a4 import A4
from .a5 import A5

Some files were not shown because too many files have changed in this diff Show More