A2 Pathing + Arcane run + Static for Sorc + Static Tele robustification + Chest class (#348)
@@ -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. |
|
||||
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 5.2 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
After Width: | Height: | Size: 9.5 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 7.7 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 9.4 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 47 KiB |
|
After Width: | Height: | Size: 48 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 124 KiB |
|
After Width: | Height: | Size: 97 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 74 KiB |
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 165 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 89 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 80 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 106 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 136 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -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
|
||||
|
||||
@@ -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 ====
|
||||
|
||||
@@ -45,6 +45,7 @@ misc_greater_mana_potion=0
|
||||
;misc
|
||||
misc_gold=0
|
||||
misc_key_of_destruction=1
|
||||
misc_key_of_hate=1
|
||||
|
||||
;---------------------------------------;
|
||||
;================= GEMS ================;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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!")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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")),
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
@@ -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: {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)])
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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,3 +1,4 @@
|
||||
from .a2 import A2
|
||||
from .a3 import A3
|
||||
from .a4 import A4
|
||||
from .a5 import A5
|
||||
|
||||