Major feature: FOHdin, Chaos Sanctuary (#765)
* Setting Up FOHdin for CS * replaced hammers with FOH, but not targeted yet * finish merging * demo * implemented all runs. avoid nihlatak. * replaced FOH with Hammer for Nihl * using hammers for dia now * reverted dia hammers to FOH, so pure FOH runs CS * added non mob-detection attacks to trav * some tweaks * tweaked hammer pindle for tele amu * improved pindle for walker FOHdin * Pindle, Eld, Shenk, CS tweaked. Trav needs work. * fix NoneType error if nodes aren't found * consolidate trav code * small adjustment * increase attack duration in loop condition rather than attack sequence * pindle optimizations * optimizations of pindle, eld, shenk * more optimizations, trav still kinda ghetto * update target detect to include closer targets * implement concentration with holy bolt * nihlathak and summoner * revised CS trash atk pattern * tweaked sealbosses! * CS ready for pressure tests * implement in-chaos pickit, match/case logic over if/elif, consolidate more functions, move some to i_char.py, patch health_manager.py to prevent it from closing inventory, update inspect_items() to allow items to be dropped and not kept to be sold * revert change * reserve hammerdin changes for a separate PR * get rid of extra attack sequence * disable target/attack logs for merge * code cleanup Co-authored-by: mgleed <[email protected]>
This commit is contained in:
committed by
GitHub
co-authored by
mgleed
parent
241fc15269
commit
70debc2b0d
@@ -31,7 +31,7 @@ Check out the [development.md](development.md) docu for infos on how to build fr
|
||||
|
||||
## Support this project 💓
|
||||
|
||||
You can support this project by giving feedback, reporting bugs, or creating pull requests.
|
||||
You can support this project by giving feedback, reporting bugs, or creating pull requests.
|
||||
Contributions are welcome, and we encourage you to contribute to this project if you would like to help out. Botty is a open source project and almost excessively maintained by contributors (there has been 50+ contributors! <3). In our [**discord**](https://discord.gg/Jf3J8cuXWg) there is a contributor role, and you can ping one of the admins and ask for the role to talk to other contributors! Though you don't need to be in the [**discord**](https://discord.gg/Jf3J8cuXWg) to contribute, we do encourage you to do so.
|
||||
|
||||
## Graphic Debugger
|
||||
@@ -49,7 +49,7 @@ All botty configuration files are located in the __config__ folder. To ease the
|
||||
name=MyCustomName
|
||||
|
||||
[routes]
|
||||
order=run_pindle, run_eldritch
|
||||
order=run_pindle, run_eldritch
|
||||
```
|
||||
|
||||
| [general] | Descriptions |
|
||||
@@ -108,7 +108,7 @@ order=run_pindle, run_eldritch
|
||||
| atk_len_diablo_infector | Attack length for hdin or number of attack sequences when fighting Sealboss C "Infector of Souls" in Chaos Sanctuary (Diablo) |
|
||||
| atk_len_diablo_infector | Attack length for hdin or number of attack sequences when fighting Diablo in Chaos Sanctuary |
|
||||
| kill_cs_trash | If 1, most Trash mob packs from Chaos Sancturay Entrance to Pentagram are cleared. If 0, the run starts at Pentagram and just kills Sealbosses & Diablo (default) |
|
||||
| cs_town_visits | If 1, it will go to town, buy pots & stash items between clearing the seals, set 0 to deactivate (default) |
|
||||
| cs_town_visits | CURRENTLY BROKEN, LEAVE AT 0 FOR NOW |
|
||||
| cs_mob_detect | If 1, it will attempt to use holy freeze from merc / conviction aura / poison to detect nearby mobs to help speed-up CS run.
|
||||
| take_health_potion | Health percentage when healing potion will be used. e.g. 0.6 = 60% helath |
|
||||
| take_mana_potion | Mana percentage when mana potion will be used |
|
||||
|
||||
@@ -209,6 +209,7 @@ dia_a2y_sealfake_sealboss=1194, 57
|
||||
dia_a2y_hop_622=1020,147
|
||||
dia_b1s_layout2_seal=1125,102, 1125,102, 1125,102, 1171,576, 651,584
|
||||
dia_b1s_seal_deseis=302,128, 267,193, 395,502
|
||||
dia_b1s_seal_deseis_foh=302,128, 267,193
|
||||
dia_c1f_651_654=1250,328, 1250,328
|
||||
dia_c1f_654_651=100,413, 100,413
|
||||
dia_c1f_651_652=1261,402
|
||||
|
||||
+14
-3
@@ -157,15 +157,26 @@ hydra=f2
|
||||
; =========================
|
||||
; ==== Builds: Paladin ====
|
||||
; =========================
|
||||
[hammerdin]
|
||||
[paladin]
|
||||
teleport=
|
||||
concentration=
|
||||
holy_shield=
|
||||
blessed_hammer=
|
||||
vigor=
|
||||
redemption=
|
||||
cleansing=
|
||||
|
||||
[fohdin]
|
||||
; foh must be left skill, hotkey required
|
||||
foh=
|
||||
holy_bolt=
|
||||
conviction=
|
||||
concentration=
|
||||
blessed_hammer=
|
||||
|
||||
[hammerdin]
|
||||
concentration=
|
||||
blessed_hammer=
|
||||
|
||||
|
||||
; ==========================
|
||||
; ==== Builds: Assassin ====
|
||||
; ==========================
|
||||
|
||||
+5
-2
@@ -24,7 +24,8 @@ from item.pickit import PickIt
|
||||
from pather import Pather, Location
|
||||
from char.sorceress import LightSorc, BlizzSorc, NovaSorc,HydraSorc
|
||||
from char.trapsin import Trapsin
|
||||
from char.hammerdin import Hammerdin
|
||||
from char.paladin.hammerdin import Hammerdin
|
||||
from char.paladin import FoHdin
|
||||
from char.barbarian import Barbarian
|
||||
from char.necro import Necro
|
||||
from char.poison_necro import Poison_Necro
|
||||
@@ -61,8 +62,10 @@ class Bot:
|
||||
self._char: IChar = NovaSorc(Config().nova_sorc, self._pather)
|
||||
case "hydra_sorc":
|
||||
self._char: IChar = HydraSorc(Config().hydra_sorc, self._pather)
|
||||
case "hammerdin":
|
||||
case "hammerdin" | "paladin":
|
||||
self._char: IChar = Hammerdin(Config().hammerdin, self._pather, self._pickit) #pickit added for diablo
|
||||
case "fohdin":
|
||||
self._char: IChar = FoHdin(Config().fohdin, self._pather, self._pickit) #pickit added for diablo
|
||||
case "trapsin":
|
||||
self._char: IChar = Trapsin(Config().trapsin, self._pather)
|
||||
case "barbarian":
|
||||
|
||||
+41
-9
@@ -29,6 +29,35 @@ class IChar:
|
||||
self._cast_duration = Config().char["casting_frames"] * 0.04 + 0.01
|
||||
self.damage_scaling = float(Config().char.get("damage_scaling", 1.0))
|
||||
self.capabilities = None
|
||||
self._active_skill = {
|
||||
"left": "",
|
||||
"right": ""
|
||||
}
|
||||
|
||||
def _set_active_skill(self, mouse_click_type: str = "left", skill: str =""):
|
||||
self._active_skill[mouse_click_type] = skill
|
||||
|
||||
def _select_skill(self, skill: str, mouse_click_type: str = "left", delay: float | list | tuple = None):
|
||||
if not (
|
||||
skill in self._skill_hotkeys and (hotkey := self._skill_hotkeys[skill])
|
||||
or (skill in Config().char and (hotkey := Config().char[skill]))
|
||||
):
|
||||
Logger.warning(f"No hotkey for skill: {skill}")
|
||||
self._set_active_skill(mouse_click_type, "")
|
||||
return False
|
||||
|
||||
if self._active_skill[mouse_click_type] != skill:
|
||||
keyboard.send(hotkey)
|
||||
self._set_active_skill(mouse_click_type, skill)
|
||||
if delay:
|
||||
try:
|
||||
wait(*delay)
|
||||
except:
|
||||
try:
|
||||
wait(delay)
|
||||
except Exception as e:
|
||||
Logger.warning(f"_select_skill: Failed to delay with delay: {delay}. Exception: {e}")
|
||||
return True
|
||||
|
||||
def _discover_capabilities(self) -> CharacterCapabilities:
|
||||
override = Config().advanced_options["override_capabilities"]
|
||||
@@ -148,12 +177,18 @@ class IChar:
|
||||
# if teleport hotkey is set and if teleport is not already selected
|
||||
if self.capabilities.can_teleport_natively:
|
||||
self.select_tp()
|
||||
self._set_active_skill("right", "teleport")
|
||||
|
||||
def move(self, pos_monitor: tuple[float, float], force_tp: bool = False, force_move: bool = False):
|
||||
factor = Config().advanced_options["pathing_delay_factor"]
|
||||
if self._skill_hotkeys["teleport"] and \
|
||||
(force_tp or (skills.is_right_skill_selected(["TELE_ACTIVE"]) and \
|
||||
skills.is_right_skill_active())):
|
||||
if "teleport" in self._skill_hotkeys and self._skill_hotkeys["teleport"] and (
|
||||
force_tp
|
||||
or (
|
||||
skills.is_right_skill_selected(["TELE_ACTIVE"])
|
||||
and skills.is_right_skill_active()
|
||||
)
|
||||
):
|
||||
self._set_active_skill("right", "teleport")
|
||||
mouse.move(pos_monitor[0], pos_monitor[1], randomize=3, delay_factor=[factor*0.1, factor*0.14])
|
||||
wait(0.012, 0.02)
|
||||
mouse.click(button="right")
|
||||
@@ -244,8 +279,7 @@ class IChar:
|
||||
while time.time() - start < 4:
|
||||
keyboard.send(Config().char["weapon_switch"])
|
||||
wait(0.3, 0.35)
|
||||
keyboard.send(Config().char["battle_command"])
|
||||
wait(0.1, 0.19)
|
||||
self._select_skill(skill = "battle_command", mouse_click_type="right", delay=(0.1, 0.2))
|
||||
if skills.is_right_skill_selected(["BC", "BO"]):
|
||||
switch_sucess = True
|
||||
break
|
||||
@@ -257,8 +291,7 @@ class IChar:
|
||||
# We switched succesfully, let's pre buff
|
||||
mouse.click(button="right")
|
||||
wait(self._cast_duration + 0.16, self._cast_duration + 0.18)
|
||||
keyboard.send(Config().char["battle_orders"])
|
||||
wait(0.1, 0.19)
|
||||
self._select_skill(skill = "battle_orders", mouse_click_type="right", delay=(0.1, 0.2))
|
||||
mouse.click(button="right")
|
||||
wait(self._cast_duration + 0.16, self._cast_duration + 0.18)
|
||||
|
||||
@@ -290,8 +323,7 @@ class IChar:
|
||||
if not self._skill_hotkeys[ability]:
|
||||
raise ValueError(f"You did not set {ability} hotkey!")
|
||||
keyboard.send(Config().char["stand_still"], do_release=False)
|
||||
keyboard.send(self._skill_hotkeys[ability])
|
||||
wait(0.02, 0.08)
|
||||
self._select_skill(skill = ability, mouse_click_type="right", delay=(0.02, 0.08))
|
||||
|
||||
target = self.vec_to_monitor(arc_spread(cast_pos_abs, spread_deg=spread_deg))
|
||||
mouse.move(*target,delay_factor=[0.95, 1.05])
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from .paladin import Paladin
|
||||
from .fohdin import FoHdin
|
||||
from .hammerdin import Hammerdin
|
||||
@@ -0,0 +1,762 @@
|
||||
import random
|
||||
import keyboard
|
||||
import time
|
||||
import numpy as np
|
||||
|
||||
from health_manager import get_panel_check_paused, set_panel_check_paused
|
||||
from inventory.personal import inspect_items
|
||||
from screen import convert_abs_to_monitor, convert_screen_to_abs, grab, convert_abs_to_screen
|
||||
from utils.custom_mouse import mouse
|
||||
from char.paladin import Paladin
|
||||
from logger import Logger
|
||||
from config import Config
|
||||
from utils.misc import wait
|
||||
from pather import Location
|
||||
from target_detect import get_visible_targets, TargetInfo, log_targets
|
||||
|
||||
class FoHdin(Paladin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
Logger.info("Setting up FoHdin")
|
||||
super().__init__(*args, **kwargs)
|
||||
self._pather.adapt_path((Location.A3_TRAV_START, Location.A3_TRAV_CENTER_STAIRS), [220, 221, 222, 903, 904, 905, 906])
|
||||
|
||||
|
||||
def _cast_foh(self, cast_pos_abs: tuple[float, float], spray: int = 10, min_duration: float = 0, aura: str = "conviction"):
|
||||
return self._cast_skill_with_aura(skill_name = "foh", cast_pos_abs = cast_pos_abs, spray = spray, min_duration = min_duration, aura = aura)
|
||||
|
||||
def _cast_holy_bolt(self, cast_pos_abs: tuple[float, float], spray: int = 10, min_duration: float = 0, aura: str = "concentration"):
|
||||
#if skill is bound : concentration, use concentration, otherwise move on with conviction. alternatively use redemption whilst holybolting. conviction does not help holy bolt (its magic damage)
|
||||
return self._cast_skill_with_aura(skill_name = "holy_bolt", cast_pos_abs = cast_pos_abs, spray = spray, min_duration = min_duration, aura = aura)
|
||||
|
||||
def _cast_hammers(self, min_duration: float = 0, aura: str = "concentration"): #for nihlathak
|
||||
return self._cast_skill_with_aura(skill_name = "blessed_hammer", spray = 0, min_duration = min_duration, aura = aura)
|
||||
|
||||
def _move_and_attack(self, abs_move: tuple[int, int], atk_len: float, aura: str = "concentration"): #for nihalthak
|
||||
pos_m = convert_abs_to_monitor(abs_move)
|
||||
self.pre_move()
|
||||
self.move(pos_m, force_move=True)
|
||||
self._cast_hammers(atk_len, aura=aura)
|
||||
|
||||
def _generic_foh_attack_sequence(
|
||||
self,
|
||||
default_target_abs: tuple[int, int] = (0, 0),
|
||||
min_duration: float = 0,
|
||||
max_duration: float = 15,
|
||||
foh_to_holy_bolt_ratio: int = 3,
|
||||
target_detect: bool = True,
|
||||
default_spray: int = 50,
|
||||
aura: str = ""
|
||||
) -> bool:
|
||||
start = time.time()
|
||||
target_check_count = 1
|
||||
foh_aura = aura if aura else "conviction"
|
||||
holy_bolt_aura = aura if aura else "concentration"
|
||||
while (elapsed := (time.time() - start)) <= max_duration:
|
||||
cast_pos_abs = default_target_abs
|
||||
spray = default_spray
|
||||
# if targets are detected, switch to targeting with reduced spread rather than present default cast position and default spread
|
||||
if target_detect and (targets := get_visible_targets()):
|
||||
# log_targets(targets)
|
||||
spray = 5
|
||||
cast_pos_abs = targets[0].center_abs
|
||||
|
||||
# TODO: add delay between FOH casts--doesn't properly cast each FOH in sequence
|
||||
|
||||
# cast foh to holy bolt with preset ratio (e.g. 3 foh followed by 1 holy bolt if foh_to_holy_bolt_ratio = 3)
|
||||
if foh_to_holy_bolt_ratio > 0 and not target_check_count % (foh_to_holy_bolt_ratio + 1):
|
||||
self._cast_holy_bolt(cast_pos_abs, spray=spray, aura=holy_bolt_aura)
|
||||
else:
|
||||
self._cast_foh(cast_pos_abs, spray=spray, aura=foh_aura)
|
||||
|
||||
# if time > minimum and either targets aren't set or targets don't exist, exit loop
|
||||
if elapsed > min_duration and (not target_detect or not targets):
|
||||
break
|
||||
target_check_count += 1
|
||||
return True
|
||||
|
||||
#FOHdin Attack Sequence Optimized for trash
|
||||
def _cs_attack_sequence(self, min_duration: float = Config().char["atk_len_cs_trashmobs"], max_duration: float = Config().char["atk_len_cs_trashmobs"] * 3):
|
||||
self._generic_foh_attack_sequence(default_target_abs=(20,20), min_duration = min_duration, max_duration = max_duration, default_spray=100, foh_to_holy_bolt_ratio=6)
|
||||
self._activate_redemption_aura()
|
||||
|
||||
def _cs_trash_mobs_attack_sequence(self, min_duration: float = 1.2, max_duration: float = Config().char["atk_len_cs_trashmobs"]):
|
||||
self._cs_attack_sequence(min_duration = min_duration, max_duration = max_duration)
|
||||
|
||||
def _cs_pickit(self, skip_inspect: bool = False):
|
||||
new_items = self._pickit.pick_up_items(self)
|
||||
self._picked_up_items |= new_items
|
||||
if not skip_inspect and new_items:
|
||||
set_panel_check_paused(True)
|
||||
inspect_items(grab(), ignore_sell=True)
|
||||
set_panel_check_paused(False)
|
||||
|
||||
|
||||
def kill_pindle(self) -> bool:
|
||||
atk_len_dur = float(Config().char["atk_len_pindle"])
|
||||
pindle_pos_abs = convert_screen_to_abs(Config().path["pindle_end"][0])
|
||||
|
||||
if self.capabilities.can_teleport_natively or self.capabilities.can_teleport_with_charges:
|
||||
# Slightly retreating, so the Merc gets charged
|
||||
if not self._pather.traverse_nodes([102], self, timeout=1.0, do_pre_move=self._do_pre_move, force_move=True,force_tp=False, use_tp_charge=False):
|
||||
return False
|
||||
# Doing one Teleport to safe_dist to grab our Merc
|
||||
Logger.debug("Teleporting backwards to let Pindle charge the MERC. Looks strange, but is intended!") #I would leave this message in, so users dont complain that there is a strange movement pattern.
|
||||
if not self._pather.traverse_nodes([103], self, timeout=1.0, do_pre_move=self._do_pre_move, force_tp=True, use_tp_charge=True):
|
||||
return False
|
||||
# Slightly retreating, so the Merc gets charged
|
||||
if not self._pather.traverse_nodes([103], self, timeout=1.0, do_pre_move=self._do_pre_move, force_move=True, force_tp=False, use_tp_charge=False):
|
||||
return False
|
||||
else:
|
||||
if not self._do_pre_move:
|
||||
keyboard.send(self._skill_hotkeys["conviction"])
|
||||
wait(0.05, 0.15)
|
||||
self._pather.traverse_nodes([103], self, timeout=1.0, do_pre_move=self._do_pre_move)
|
||||
|
||||
cast_pos_abs = [pindle_pos_abs[0] * 0.9, pindle_pos_abs[1] * 0.9]
|
||||
self._generic_foh_attack_sequence(default_target_abs=cast_pos_abs, min_duration=atk_len_dur, max_duration=atk_len_dur*3, default_spray=11)
|
||||
|
||||
if self.capabilities.can_teleport_natively:
|
||||
self._pather.traverse_nodes_fixed("pindle_end", self)
|
||||
else:
|
||||
if not self._do_pre_move:
|
||||
keyboard.send(self._skill_hotkeys["redemption"])
|
||||
wait(0.05, 0.15)
|
||||
self._pather.traverse_nodes((Location.A5_PINDLE_SAFE_DIST, Location.A5_PINDLE_END), self, timeout=1.0, do_pre_move=self._do_pre_move)
|
||||
|
||||
# Use target-based attack sequence one more time before pickit
|
||||
self._generic_foh_attack_sequence(default_target_abs=cast_pos_abs, max_duration=atk_len_dur, default_spray=11)
|
||||
self._activate_cleanse_redemption()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def kill_council(self) -> bool:
|
||||
atk_len_dur = float(Config().char["atk_len_trav"])
|
||||
|
||||
# traverse to nodes and attack
|
||||
nodes = [225, 226, 300]
|
||||
for node in nodes:
|
||||
self._pather.traverse_nodes([node], self, timeout=2, force_tp=True)
|
||||
default_target_abs = self._pather.find_abs_node_pos(node, img := grab()) or self._pather.find_abs_node_pos(906, img) or (-50, -50)
|
||||
self._generic_foh_attack_sequence(default_target_abs=default_target_abs, min_duration=atk_len_dur, max_duration=atk_len_dur*3, default_spray=80)
|
||||
|
||||
# return to 226 and prepare for pickit
|
||||
self._pather.traverse_nodes([226], self, timeout=2, force_tp=True)
|
||||
default_target_abs = self._pather.find_abs_node_pos(226, img := grab()) or self._pather.find_abs_node_pos(906, img) or (-50, -50)
|
||||
self._generic_foh_attack_sequence(default_target_abs=default_target_abs, max_duration=atk_len_dur*3, default_spray=80)
|
||||
|
||||
self._activate_cleanse_redemption()
|
||||
|
||||
return True
|
||||
|
||||
def kill_eldritch(self) -> bool:
|
||||
eld_pos_abs = convert_screen_to_abs(Config().path["eldritch_end"][0])
|
||||
atk_len_dur = float(Config().char["atk_len_eldritch"])
|
||||
|
||||
self._generic_foh_attack_sequence(default_target_abs=eld_pos_abs, min_duration=atk_len_dur, max_duration=atk_len_dur*3, default_spray=70)
|
||||
|
||||
# move to end node
|
||||
pos_m = convert_abs_to_monitor((70, -200))
|
||||
self.pre_move()
|
||||
self.move(pos_m, force_move=True)
|
||||
self._pather.traverse_nodes((Location.A5_ELDRITCH_SAFE_DIST, Location.A5_ELDRITCH_END), self, timeout=0.1)
|
||||
|
||||
# check mobs one more time before pickit
|
||||
self._generic_foh_attack_sequence(default_target_abs=eld_pos_abs, max_duration=atk_len_dur, default_spray=70)
|
||||
self._activate_cleanse_redemption()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def kill_shenk(self):
|
||||
atk_len_dur = float(Config().char["atk_len_shenk"])
|
||||
|
||||
# traverse to shenk
|
||||
if not self._do_pre_move:
|
||||
keyboard.send(self._skill_hotkeys["conviction"])
|
||||
wait(0.05, 0.15)
|
||||
self._pather.traverse_nodes((Location.A5_SHENK_SAFE_DIST, Location.A5_SHENK_END), self, timeout=1.0, do_pre_move=self._do_pre_move, force_tp=True, use_tp_charge=True)
|
||||
wait(0.05, 0.1)
|
||||
|
||||
# bypass mob detect first
|
||||
self._cast_foh((0, 0), spray=11, min_duration = 2, aura = "conviction")
|
||||
# then do generic mob detect sequence
|
||||
diff = atk_len_dur if atk_len_dur <= 2 else (atk_len_dur - 2)
|
||||
self._generic_foh_attack_sequence(min_duration=atk_len_dur - diff, max_duration=atk_len_dur*3 - diff, default_spray=10, target_detect=False)
|
||||
self._activate_cleanse_redemption()
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def kill_nihlathak(self, end_nodes: list[int]) -> bool:
|
||||
atk_len_dur = Config().char["atk_len_nihlathak"]
|
||||
# Move close to nihlathak
|
||||
self._pather.traverse_nodes(end_nodes, self, timeout=0.8, do_pre_move=False)
|
||||
if self._select_skill("blessed_hammer"):
|
||||
self._cast_hammers(atk_len_dur/4)
|
||||
self._cast_hammers(2*atk_len_dur/4, "redemption")
|
||||
self._move_and_attack((30, 15), atk_len_dur/4, "redemption")
|
||||
else:
|
||||
Logger.warning("FOHDin without blessed hammer is not very strong vs. Nihlathak!")
|
||||
self._generic_foh_attack_sequence(min_duration=atk_len_dur/2, max_duration=atk_len_dur, default_spray=70, aura="redemption")
|
||||
self._generic_foh_attack_sequence(min_duration=atk_len_dur/2, max_duration=atk_len_dur, default_spray=70, aura="redemption")
|
||||
self._generic_foh_attack_sequence(max_duration=atk_len_dur*2, default_spray=70)
|
||||
self._activate_cleanse_redemption()
|
||||
return True
|
||||
|
||||
def kill_summoner(self) -> bool:
|
||||
# Attack
|
||||
atk_len_dur = Config().char["atk_len_arc"]
|
||||
self._generic_foh_attack_sequence(min_duration=atk_len_dur, max_duration=atk_len_dur*2, default_spray=80)
|
||||
self._activate_cleanse_redemption()
|
||||
return True
|
||||
|
||||
|
||||
########################################################################################
|
||||
# Chaos Sanctuary, Trash, Seal Bosses (a = Vizier, b = De Seis, c = Infector) & Diablo #
|
||||
########################################################################################
|
||||
|
||||
def kill_cs_trash(self, location:str) -> bool:
|
||||
|
||||
###########
|
||||
# SEALDANCE
|
||||
###########
|
||||
|
||||
#these locations have no traverses and are basically identical.
|
||||
#if location in ("sealdance", "rof_01", "rof_02", "entrance_hall_01", "entrance_hall_02", "entrance1_01", "entrance1_02", "entrance1_03", "entrance1_04", "entrance2_01", "entrance2_03"):
|
||||
|
||||
match location:
|
||||
case "sealdance": #if seal opening fails & trash needs to be cleared -> used at ANY seal
|
||||
### APPROACH
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
|
||||
|
||||
################
|
||||
# CLEAR CS TRASH
|
||||
################
|
||||
|
||||
case "rof_01": #node 603 - outside CS in ROF
|
||||
### APPROACH ###
|
||||
if not self._pather.traverse_nodes([603], self, timeout=3): return False #calibrate after static path
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
if not self._pather.traverse_nodes([603], self): return False #calibrate after looting
|
||||
|
||||
|
||||
case "rof_02": #node 604 - inside ROF
|
||||
### APPROACH ###
|
||||
if not self._pather.traverse_nodes([604], self, timeout=3): return False #threshold=0.8 (ex 601)
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
|
||||
case "entrance_hall_01": ##static_path "diablo_entrance_hall_1", node 677, CS Entrance Hall1
|
||||
### APPROACH ###
|
||||
self._pather.traverse_nodes_fixed("diablo_entrance_hall_1", self) # 604 -> 671 Hall1
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
|
||||
case "entrance_hall_02": #node 670,671, CS Entrance Hall1, CS Entrance Hall1
|
||||
### APPROACH ###
|
||||
if not self._pather.traverse_nodes([670], self): return False # pull top mobs 672 to bottom 670
|
||||
self._pather.traverse_nodes_fixed("diablo_entrance_1_670_672", self) # 604 -> 671 Hall1
|
||||
if not self._pather.traverse_nodes([670], self): return False # pull top mobs 672 to bottom 670
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
if not self._pather.traverse_nodes([671], self): return False # calibrate before static path
|
||||
self._pather.traverse_nodes_fixed("diablo_entrance_hall_2", self) # 671 -> LC Hall2
|
||||
|
||||
|
||||
|
||||
# TRASH LAYOUT A
|
||||
|
||||
case "entrance1_01": #static_path "diablo_entrance_hall_2", Hall1 (before layout check)
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
if not self._pather.traverse_nodes([673], self): return False # , timeout=3): # Re-adjust itself and continues to attack
|
||||
|
||||
case "entrance1_02": #node 673
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
self._pather.traverse_nodes_fixed("diablo_entrance_1_1", self) # Moves char to postion close to node 674 continues to attack
|
||||
if not self._pather.traverse_nodes([674], self): return False#, timeout=3)
|
||||
|
||||
case "entrance1_03": #node 674
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
if not self._pather.traverse_nodes([675], self): return False#, timeout=3) # Re-adjust itself
|
||||
self._pather.traverse_nodes_fixed("diablo_entrance_1_1", self) #static path to get to be able to spot 676
|
||||
if not self._pather.traverse_nodes([676], self): return False#, timeout=3)
|
||||
|
||||
case "entrance1_04": #node 676- Hall3
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
|
||||
# TRASH LAYOUT B
|
||||
|
||||
case "entrance2_01": #static_path "diablo_entrance_hall_2"
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
|
||||
case "entrance2_02": #node 682
|
||||
### APPROACH ###
|
||||
#if not self._pather.traverse_nodes([682], self): return False # , timeout=3):
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
|
||||
case "entrance2_03": #node 683
|
||||
### APPROACH ###
|
||||
#if not self._pather.traverse_nodes([682], self): return False # , timeout=3):
|
||||
#self._pather.traverse_nodes_fixed("diablo_entrance2_1", self)
|
||||
#if not self._pather.traverse_nodes([683], self): return False # , timeout=3):
|
||||
self._pather.traverse_nodes_fixed("diablo_trash_b_hall2_605_top1", self) #pull mobs from top
|
||||
wait (0.2, 0.5)
|
||||
self._pather.traverse_nodes_fixed("diablo_trash_b_hall2_605_top2", self) #pull mobs from top
|
||||
if not self._pather.traverse_nodes([605], self): return False#, timeout=3)
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
|
||||
case "entrance2_04": #node 686 - Hall3
|
||||
### APPROACH ###
|
||||
if not self._pather.traverse_nodes([605], self): return False#, timeout=3)
|
||||
#if not self._pather.traverse_nodes([683,684], self): return False#, timeout=3)
|
||||
#self._pather.traverse_nodes_fixed("diablo_entrance2_2", self)
|
||||
#if not self._pather.traverse_nodes([685,686], self): return False#, timeout=3)
|
||||
self._pather.traverse_nodes_fixed("diablo_trash_b_hall2_605_hall3", self)
|
||||
if not self._pather.traverse_nodes([609], self): return False#, timeout=3)
|
||||
self._pather.traverse_nodes_fixed("diablo_trash_b_hall3_pull_609", self)
|
||||
if not self._pather.traverse_nodes([609], self): return False#, timeout=3)
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit(skip_inspect=True)
|
||||
if not self._pather.traverse_nodes([609], self): return False#, timeout=3)
|
||||
self._cs_pickit()
|
||||
if not self._pather.traverse_nodes([609], self): return False#, timeout=3)
|
||||
|
||||
####################
|
||||
# PENT TRASH TO SEAL
|
||||
####################
|
||||
|
||||
case "dia_trash_a" | "dia_trash_b" | "dia_trash_c": #trash before between Pentagramm and Seal A Layoutcheck
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
|
||||
###############
|
||||
# LAYOUT CHECKS
|
||||
###############
|
||||
|
||||
case "layoutcheck_a" | "layoutcheck_b" | "layoutcheck_c": #layout check seal A, node 619 A1-L, node 620 A2-Y
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
|
||||
##################
|
||||
# PENT BEFORE SEAL
|
||||
##################
|
||||
|
||||
case "pent_before_a": #node 602, pentagram, before CTA buff & depature to layout check - not needed when trash is skipped & seals run in right order
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
### LOOT ###
|
||||
Logger.debug("No attack choreography available in fohdin.py for this node " + location + " - skipping to shorten run.")
|
||||
|
||||
case "pent_before_b" | "pent_before_c": #node 602, pentagram, before CTA buff & depature to layout check
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
|
||||
###########
|
||||
# SEAL A1-L
|
||||
###########
|
||||
|
||||
case "A1-L_01": #node 611 seal layout A1-L: safe_dist
|
||||
### APPROACH ###
|
||||
if not self._pather.traverse_nodes([611], self): return False # , timeout=3):
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
# we loot at boss
|
||||
|
||||
case "A1-L_02": #node 612 seal layout A1-L: center
|
||||
### APPROACH ###
|
||||
if not self._pather.traverse_nodes([612], self): return False # , timeout=3):
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
# we loot at boss
|
||||
|
||||
case "A1-L_03": #node 613 seal layout A1-L: fake_seal
|
||||
### APPROACH ###
|
||||
if not self._pather.traverse_nodes([613], self): return False # , timeout=3):
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
|
||||
case "A1-L_seal1": #node 613 seal layout A1-L: fake_seal
|
||||
### APPROACH ###
|
||||
self._cs_pickit()
|
||||
if not self._pather.traverse_nodes([614], self): return False
|
||||
### ATTACK ###
|
||||
self._activate_redemption_aura()
|
||||
### LOOT ###
|
||||
# we loot at boss
|
||||
|
||||
case "A1-L_seal2": #node 614 seal layout A1-L: boss_seal
|
||||
### APPROACH ###
|
||||
if not self._pather.traverse_nodes([613, 615], self): return False # , timeout=3):
|
||||
### ATTACK ###
|
||||
self._activate_redemption_aura()
|
||||
### LOOT ###
|
||||
# we loot at boss
|
||||
|
||||
###########
|
||||
# SEAL A2-Y
|
||||
###########
|
||||
|
||||
case "A2-Y_01": #node 622 seal layout A2-Y: safe_dist
|
||||
### APPROACH ###
|
||||
if not self._pather.traverse_nodes_fixed("dia_a2y_hop_622", self): return False
|
||||
Logger.debug("A2-Y: Hop!")
|
||||
#if not self._pather.traverse_nodes([622], self): return False # , timeout=3):
|
||||
if not self._pather.traverse_nodes([622], self): return False
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
# we loot at boss
|
||||
|
||||
case "A2-Y_02": #node 623 seal layout A2-Y: center
|
||||
### APPROACH ###
|
||||
# if not self._pather.traverse_nodes([623,624], self): return False #
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
# we loot at boss
|
||||
|
||||
case "A2-Y_03": #skipped
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
### LOOT ###
|
||||
# we loot at boss
|
||||
Logger.debug("No attack choreography available in fohdin.py for this node " + location + " - skipping to shorten run.")
|
||||
|
||||
case "A2-Y_seal1": #node 625 seal layout A2-Y: fake seal
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
### LOOT ###
|
||||
# we loot at boss
|
||||
if not self._pather.traverse_nodes([625], self): return False # , timeout=3):
|
||||
self._activate_redemption_aura()
|
||||
|
||||
case "A2-Y_seal2":
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
### LOOT ###
|
||||
# we loot at boss
|
||||
self._pather.traverse_nodes_fixed("dia_a2y_sealfake_sealboss", self) #instead of traversing node 626 which causes issues
|
||||
self._activate_redemption_aura()
|
||||
|
||||
###########
|
||||
# SEAL B1-S
|
||||
###########
|
||||
|
||||
case "B1-S_01" | "B1-S_02" | "B1-S_03":
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
### LOOT ###
|
||||
# we loot at boss
|
||||
Logger.debug("No attack choreography available in fohdin.py for this node " + location + " - skipping to shorten run.")
|
||||
|
||||
case "B1-S_seal2": #B only has 1 seal, which is the boss seal = seal2
|
||||
### APPROACH ###
|
||||
if not self._pather.traverse_nodes([634], self): return False # , timeout=3):
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
|
||||
|
||||
###########
|
||||
# SEAL B2-U
|
||||
###########
|
||||
|
||||
case "B2-U_01" | "B2-U_02" | "B2-U_03":
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
### LOOT ###
|
||||
# we loot at boss
|
||||
Logger.debug("No attack choreography available in fohdin.py for this node " + location + " - skipping to shorten run.")
|
||||
|
||||
case "B2-U_seal2": #B only has 1 seal, which is the boss seal = seal2
|
||||
### APPROACH ###
|
||||
self._pather.traverse_nodes_fixed("dia_b2u_bold_seal", self)
|
||||
if not self._pather.traverse_nodes([644], self): return False # , timeout=3):
|
||||
### ATTACK ###
|
||||
self._activate_redemption_aura()
|
||||
### LOOT ###
|
||||
# we loot at boss
|
||||
|
||||
|
||||
###########
|
||||
# SEAL C1-F
|
||||
###########
|
||||
|
||||
case "C1-F_01" | "C1-F_02" | "C1-F_03":
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
### LOOT ###
|
||||
# we loot at boss
|
||||
Logger.debug("No attack choreography available in fohdin.py for this node " + location + " - skipping to shorten run.")
|
||||
|
||||
case "C1-F_seal1":
|
||||
### APPROACH ###
|
||||
wait(0.1,0.3)
|
||||
self._pather.traverse_nodes_fixed("dia_c1f_hop_fakeseal", self)
|
||||
wait(0.1,0.3)
|
||||
if not self._pather.traverse_nodes([655], self): return False # , timeout=3):
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
if not self._pather.traverse_nodes([655], self): return False # , timeout=3):
|
||||
self._activate_redemption_aura()
|
||||
|
||||
case "C1-F_seal2":
|
||||
### APPROACH ###
|
||||
self._pather.traverse_nodes_fixed("dia_c1f_654_651", self)
|
||||
if not self._pather.traverse_nodes([652], self): return False # , timeout=3):
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
if not self._pather.traverse_nodes([652], self): return False # , timeout=3):
|
||||
self._activate_redemption_aura()
|
||||
|
||||
###########
|
||||
# SEAL C2-G
|
||||
###########
|
||||
|
||||
case "C2-G_01" | "C2-G_02" | "C2-G_03": #skipped
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
### LOOT ###
|
||||
# we loot at boss
|
||||
Logger.debug("No attack choreography available in fohdin.py for this node " + location + " - skipping to shorten run.")
|
||||
|
||||
case "C2-G_seal1":
|
||||
### APPROACH ###
|
||||
if not self._pather.traverse_nodes([663, 662], self) or not self._pather.traverse_nodes_fixed("dia_c2g_lc_661", self):
|
||||
return False
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
if not self._pather.traverse_nodes([662], self): return False
|
||||
self._activate_redemption_aura()
|
||||
|
||||
|
||||
case "C2-G_seal2":
|
||||
### APPROACH ###
|
||||
# Killing infector here, because for C2G its the only seal where a bossfight occures BETWEEN opening seals
|
||||
seal_layout="C2-G"
|
||||
if not self._pather.traverse_nodes([662], self) or not self._pather.traverse_nodes_fixed("dia_c2g_663", self):
|
||||
return False
|
||||
### ATTACK ###
|
||||
atk_dur_min = Config().char["atk_len_diablo_infector"]
|
||||
atk_dur_max = atk_dur_min * 3
|
||||
Logger.debug(seal_layout + ": Attacking Infector at position 1/2")
|
||||
self._cs_attack_sequence(min_duration=atk_dur_min, max_duration=atk_dur_max)
|
||||
if not self._pather.traverse_nodes([663], self): return False # , timeout=3):
|
||||
Logger.debug(seal_layout + ": Attacking Infector at position 2/2")
|
||||
self._cs_attack_sequence(min_duration=2, max_duration=atk_dur_max)
|
||||
### LOOT ###
|
||||
self._cs_pickit(skip_inspect=True) # inspect on other
|
||||
if not self._pather.traverse_nodes([664, 665], self): return False # , timeout=3):
|
||||
|
||||
case _:
|
||||
### APPROACH ###
|
||||
Logger.error("No location argument given for kill_cs_trash(" + location + "), should not happen")
|
||||
### ATTACK ###
|
||||
self._cs_trash_mobs_attack_sequence()
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
return True
|
||||
|
||||
|
||||
def kill_vizier(self, seal_layout:str) -> bool:
|
||||
atk_dur_min = Config().char["atk_len_diablo_vizier"]
|
||||
atk_dur_max = atk_dur_min * 3
|
||||
match seal_layout:
|
||||
case "A1-L":
|
||||
### APPROACH ###
|
||||
if not self._pather.traverse_nodes([612], self): return False # , timeout=3):
|
||||
### ATTACK ###
|
||||
Logger.debug(seal_layout + ": Attacking Vizier at position 1/2")
|
||||
self._cs_attack_sequence(min_duration=atk_dur_min, max_duration=atk_dur_max)
|
||||
Logger.debug(seal_layout + ": Attacking Vizier at position 2/2")
|
||||
self._pather.traverse_nodes([611], self, timeout=3)
|
||||
self._cs_attack_sequence(min_duration=2, max_duration=atk_dur_max)
|
||||
### LOOT ###
|
||||
self._cs_pickit(skip_inspect=True)
|
||||
if not self._pather.traverse_nodes([612], self): return False # , timeout=3):
|
||||
self._activate_cleanse_redemption()
|
||||
self._cs_pickit()
|
||||
if not self._pather.traverse_nodes([612], self): return False # , timeout=3): # recalibrate after loot
|
||||
|
||||
case "A2-Y":
|
||||
### APPROACH ###
|
||||
if not self._pather.traverse_nodes([627, 622], self): return False # , timeout=3):
|
||||
### ATTACK ###
|
||||
Logger.debug(seal_layout + ": Attacking Vizier at position 1/3")
|
||||
self._cs_attack_sequence(min_duration=atk_dur_min, max_duration=atk_dur_max)
|
||||
Logger.debug(seal_layout + ": Attacking Vizier at position 2/3")
|
||||
self._pather.traverse_nodes([623], self, timeout=3)
|
||||
self._cs_attack_sequence(min_duration=1.5, max_duration=atk_dur_max)
|
||||
Logger.debug(seal_layout + ": Attacking Vizier at position 3/3")
|
||||
if not self._pather.traverse_nodes([624], self): return False
|
||||
self._cs_attack_sequence(min_duration=1.5, max_duration=atk_dur_max)
|
||||
### LOOT ###
|
||||
self._cs_pickit(skip_inspect=True)
|
||||
if not self._pather.traverse_nodes([624], self): return False
|
||||
if not self._pather.traverse_nodes_fixed("dia_a2y_hop_622", self): return False
|
||||
Logger.debug(seal_layout + ": Hop!")
|
||||
self._activate_cleanse_redemption()
|
||||
if not self._pather.traverse_nodes([622], self): return False #, timeout=3):
|
||||
self._activate_redemption_aura()
|
||||
self._cs_pickit()
|
||||
if not self._pather.traverse_nodes([622], self): return False # , timeout=3): #recalibrate after loot
|
||||
|
||||
case _:
|
||||
Logger.warning(seal_layout + ": Invalid location for kill_vizier("+ seal_layout +"), should not happen.")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def kill_deseis(self, seal_layout:str) -> bool:
|
||||
atk_dur_min = Config().char["atk_len_diablo_deseis"]
|
||||
atk_dur_max = atk_dur_min * 3
|
||||
match seal_layout:
|
||||
case "B1-S":
|
||||
### APPROACH ###
|
||||
self._pather.traverse_nodes_fixed("dia_b1s_seal_deseis_foh", self)
|
||||
nodes = [631]
|
||||
### ATTACK ###
|
||||
Logger.debug(f"{seal_layout}: Attacking De Seis at position 1/{len(nodes)+1}")
|
||||
self._cs_attack_sequence(min_duration=atk_dur_min, max_duration=atk_dur_max)
|
||||
for i, node in enumerate(nodes):
|
||||
Logger.debug(f"{seal_layout}: Attacking De Seis at position {i+2}/{len(nodes)+1}")
|
||||
self._pather.traverse_nodes([node], self, timeout=3)
|
||||
self._cs_attack_sequence(min_duration=atk_dur_min, max_duration=atk_dur_max)
|
||||
self._activate_redemption_aura(delay=[1, 2])
|
||||
#if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_check_deseis_dead" + seal_layout + "_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab())
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
|
||||
case "B2-U":
|
||||
### APPROACH ###
|
||||
self._pather.traverse_nodes_fixed("dia_b2u_644_646", self) # We try to breaking line of sight, sometimes makes De Seis walk into the hammercloud. A better attack sequence here could make sense.
|
||||
#self._pather.traverse_nodes_fixed("dia_b2u_seal_deseis", self) # We try to breaking line of sight, sometimes makes De Seis walk into the hammercloud. A better attack sequence here could make sense.
|
||||
nodes = [646, 641]
|
||||
### ATTACK ###
|
||||
Logger.debug(seal_layout + ": Attacking De Seis at position 1/{len(nodes)+1}")
|
||||
self._cs_attack_sequence(min_duration=atk_dur_min, max_duration=atk_dur_max)
|
||||
for i, node in enumerate(nodes):
|
||||
Logger.debug(f"{seal_layout}: Attacking De Seis at position {i+2}/{len(nodes)+1}")
|
||||
self._pather.traverse_nodes([node], self, timeout=3)
|
||||
self._cs_attack_sequence(min_duration=2, max_duration=atk_dur_max)
|
||||
#if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_check_deseis_dead" + seal_layout + "_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab())
|
||||
### LOOT ###
|
||||
self._cs_pickit(skip_inspect=True)
|
||||
if not self._pather.traverse_nodes([641], self): return False # , timeout=3):
|
||||
if not self._pather.traverse_nodes([646], self): return False # , timeout=3):
|
||||
self._cs_pickit(skip_inspect=True)
|
||||
if not self._pather.traverse_nodes([646], self): return False # , timeout=3):
|
||||
if not self._pather.traverse_nodes([640], self): return False # , timeout=3):
|
||||
self._cs_pickit()
|
||||
|
||||
case _:
|
||||
Logger.warning(seal_layout + ": Invalid location for kill_deseis("+ seal_layout +"), should not happen.")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def kill_infector(self, seal_layout:str) -> bool:
|
||||
atk_dur_min = Config().char["atk_len_diablo_infector"]
|
||||
atk_dur_max = atk_dur_min * 3
|
||||
match seal_layout:
|
||||
case "C1-F":
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
Logger.debug(seal_layout + ": Attacking Infector at position 1/1")
|
||||
self._cs_attack_sequence(min_duration=atk_dur_min, max_duration=atk_dur_max)
|
||||
self._pather.traverse_nodes_fixed("dia_c1f_652", self)
|
||||
Logger.debug(seal_layout + ": Attacking Infector at position 2/2")
|
||||
self._cs_attack_sequence(min_duration=2, max_duration=atk_dur_max)
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
|
||||
case "C2-G":
|
||||
### APPROACH ###
|
||||
if not self._pather.traverse_nodes([665], self): return False # , timeout=3):
|
||||
### ATTACK ###
|
||||
Logger.debug(seal_layout + ": Attacking Infector at position 1/2")
|
||||
self._cs_attack_sequence(min_duration=atk_dur_min, max_duration=atk_dur_max)
|
||||
if not self._pather.traverse_nodes([663], self): return False # , timeout=3):
|
||||
Logger.debug(seal_layout + ": Attacking Infector at position 2/2")
|
||||
self._cs_attack_sequence(min_duration=2, max_duration=atk_dur_max)
|
||||
### LOOT ###
|
||||
self._cs_pickit()
|
||||
|
||||
case _:
|
||||
Logger.warning(seal_layout + ": Invalid location for kill_infector("+ seal_layout +"), should not happen.")
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
#no aura effect on dia. not good for mob detection
|
||||
def kill_diablo(self) -> bool:
|
||||
### APPROACH ###
|
||||
### ATTACK ###
|
||||
atk_len_dur = float(Config().char["atk_len_diablo"])
|
||||
Logger.debug("Attacking Diablo at position 1/1")
|
||||
diablo_abs = [100,-100] #hardcoded dia pos.
|
||||
self._generic_foh_attack_sequence(default_target_abs=diablo_abs, min_duration=atk_len_dur, max_duration=atk_len_dur*3, aura="concentration", foh_to_holy_bolt_ratio=2)
|
||||
self._activate_cleanse_redemption()
|
||||
### LOOT ###
|
||||
#self._cs_pickit()
|
||||
return True
|
||||
@@ -1,29 +1,26 @@
|
||||
import keyboard
|
||||
from ui import skills
|
||||
from screen import convert_abs_to_monitor, convert_screen_to_abs, grab
|
||||
from utils.custom_mouse import mouse
|
||||
from char import IChar, CharacterCapabilities
|
||||
import template_finder
|
||||
from char.paladin import Paladin
|
||||
from pather import Pather
|
||||
from logger import Logger
|
||||
from screen import convert_abs_to_monitor
|
||||
from config import Config
|
||||
from utils.misc import wait
|
||||
import time
|
||||
from pather import Location
|
||||
import random
|
||||
|
||||
from ui import skills
|
||||
from char import IChar, CharacterCapabilities
|
||||
from pather import Pather, Location
|
||||
import cv2 #for Diablo
|
||||
from item.pickit import PickIt #for Diablo
|
||||
import numpy as np
|
||||
from target_detect import get_visible_targets
|
||||
|
||||
|
||||
class Hammerdin(IChar):
|
||||
def __init__(self, skill_hotkeys: dict, pather: Pather, pickit: PickIt):
|
||||
class Hammerdin(Paladin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
Logger.info("Setting up Hammerdin")
|
||||
super().__init__(skill_hotkeys)
|
||||
self._pather = pather
|
||||
self._do_pre_move = True
|
||||
self._pickit = pickit #for Diablo
|
||||
self._picked_up_items = False #for Diablo
|
||||
super().__init__(*args, **kwargs)
|
||||
#hammerdin needs to be closer to shenk to reach it with hammers
|
||||
self._pather.offset_node(149, (70, 10))
|
||||
|
||||
@@ -1286,4 +1283,4 @@ if __name__ == "__main__":
|
||||
keyboard.wait("f11")
|
||||
from config import Config
|
||||
pather = Pather()
|
||||
char = Hammerdin(Config().hammerdin, Config().char, pather)
|
||||
char = Hammerdin(Config().hammerdin, Config().char, pather)
|
||||
@@ -0,0 +1,108 @@
|
||||
import keyboard
|
||||
from ui import skills
|
||||
import time
|
||||
import random
|
||||
from utils.custom_mouse import mouse
|
||||
from char import IChar, CharacterCapabilities
|
||||
from pather import Pather
|
||||
from logger import Logger
|
||||
from config import Config
|
||||
from utils.misc import wait
|
||||
from screen import convert_abs_to_screen, convert_abs_to_monitor
|
||||
from pather import Pather
|
||||
#import cv2 #for Diablo
|
||||
from item.pickit import PickIt #for Diablo
|
||||
|
||||
class Paladin(IChar):
|
||||
def __init__(self, skill_hotkeys: dict, pather: Pather, pickit: PickIt):
|
||||
Logger.info("Setting up Paladin")
|
||||
super().__init__(skill_hotkeys)
|
||||
self._pather = pather
|
||||
self._do_pre_move = True
|
||||
self._pickit = pickit #for Diablo
|
||||
self._picked_up_items = False #for Diablo
|
||||
|
||||
def pre_buff(self):
|
||||
if Config().char["cta_available"]:
|
||||
self._pre_buff_cta()
|
||||
keyboard.send(self._skill_hotkeys["holy_shield"])
|
||||
wait(0.04, 0.1)
|
||||
mouse.click(button="right")
|
||||
wait(self._cast_duration, self._cast_duration + 0.06)
|
||||
|
||||
def on_capabilities_discovered(self, capabilities: CharacterCapabilities):
|
||||
# In case we have a running pala, we want to switch to concentration when moving to the boss
|
||||
# ass most likely we will click on some mobs and already cast hammers
|
||||
if capabilities.can_teleport_natively:
|
||||
self._do_pre_move = False
|
||||
|
||||
def pre_move(self):
|
||||
# select teleport if available
|
||||
super().pre_move()
|
||||
# in case teleport hotkey is not set or teleport can not be used, use vigor if set
|
||||
should_cast_vigor = self._skill_hotkeys["vigor"] and not skills.is_right_skill_selected(["VIGOR"])
|
||||
can_teleport = self.capabilities.can_teleport_natively and skills.is_right_skill_active()
|
||||
if should_cast_vigor and not can_teleport:
|
||||
keyboard.send(self._skill_hotkeys["vigor"])
|
||||
wait(0.15, 0.25)
|
||||
|
||||
def _log_cast(self, skill_name: str, cast_pos_abs: tuple[float, float], spray: int, min_duration: float, aura: str):
|
||||
msg = f"Casting skill {skill_name}"
|
||||
if cast_pos_abs:
|
||||
msg += f" at screen coordinate {convert_abs_to_screen(cast_pos_abs)}"
|
||||
if spray:
|
||||
msg += f" with spray of {spray}"
|
||||
if min_duration:
|
||||
msg += f" for {round(min_duration, 1)}s"
|
||||
if aura:
|
||||
msg += f" with {aura} active"
|
||||
Logger.debug(msg)
|
||||
|
||||
def _click_cast(self, cast_pos_abs: tuple[float, float], spray: int, mouse_click_type: str = "left"):
|
||||
if cast_pos_abs:
|
||||
x = cast_pos_abs[0]
|
||||
y = cast_pos_abs[1]
|
||||
if spray:
|
||||
x += (random.random() * 2 * spray - spray)
|
||||
y += (random.random() * 2 * spray - spray)
|
||||
pos_m = convert_abs_to_monitor((x, y))
|
||||
mouse.move(*pos_m, delay_factor=[0.1, 0.2])
|
||||
wait(0.06, 0.08)
|
||||
mouse.press(button = mouse_click_type)
|
||||
wait(0.06, 0.08)
|
||||
mouse.release(button = mouse_click_type)
|
||||
|
||||
def _cast_skill_with_aura(self, skill_name: str, cast_pos_abs: tuple[float, float] = None, spray: int = 0, min_duration: float = 0, aura: str = ""):
|
||||
#self._log_cast(skill_name, cast_pos_abs, spray, min_duration, aura)
|
||||
|
||||
# set aura if needed
|
||||
if aura:
|
||||
self._select_skill(aura, mouse_click_type = "right")
|
||||
|
||||
# ensure character stands still
|
||||
keyboard.send(Config().char["stand_still"], do_release=False)
|
||||
|
||||
# set left hand skill
|
||||
self._select_skill(skill_name, mouse_click_type = "left")
|
||||
wait(0.05, 0.1)
|
||||
|
||||
# cast left hand skill
|
||||
start = time.time()
|
||||
if min_duration:
|
||||
while (time.time() - start) <= min_duration:
|
||||
self._click_cast(cast_pos_abs, spray)
|
||||
else:
|
||||
self._click_cast(cast_pos_abs, spray)
|
||||
|
||||
# release stand still key
|
||||
keyboard.send(Config().char["stand_still"], do_press=False)
|
||||
|
||||
def _activate_redemption_aura(self, delay = [0.6, 0.8]):
|
||||
self._select_skill("redemption", delay=delay)
|
||||
|
||||
def _activate_cleanse_aura(self, delay = [0.3, 0.4]):
|
||||
self._select_skill("cleansing", delay=delay)
|
||||
|
||||
def _activate_cleanse_redemption(self):
|
||||
self._activate_cleanse_aura()
|
||||
self._activate_redemption_aura()
|
||||
+12
-1
@@ -43,6 +43,7 @@ class Config:
|
||||
nova_sorc = {}
|
||||
hydra_sorc = {}
|
||||
hammerdin = {}
|
||||
fohdin = {}
|
||||
trapsin = {}
|
||||
barbarian = {}
|
||||
poison_necro = {}
|
||||
@@ -333,10 +334,20 @@ class Config:
|
||||
self.hydra_sorc.update(dict(self.configs["custom"]["parser"]["hydra_sorc"]))
|
||||
self.hydra_sorc.update(sorc_base_cfg)
|
||||
|
||||
# Palandin config
|
||||
# Paladin base config
|
||||
paladin_base_cfg = dict(self.configs["config"]["parser"]["paladin"])
|
||||
if "paladin" in self.configs["custom"]["parser"]:
|
||||
paladin_base_cfg.update(dict(self.configs["custom"]["parser"]["paladin"]))
|
||||
# Hammerdin config
|
||||
self.hammerdin = self.configs["config"]["parser"]["hammerdin"]
|
||||
if "hammerdin" in self.configs["custom"]["parser"]:
|
||||
self.fohdin.update(self.configs["custom"]["parser"]["fohdin"])
|
||||
self.hammerdin.update(self.configs["custom"]["parser"]["hammerdin"])
|
||||
# FoHdin config
|
||||
self.fohdin = dict(self.configs["config"]["parser"]["fohdin"])
|
||||
if "fohdin" in self.configs["custom"]["parser"]:
|
||||
self.fohdin.update(self.configs["custom"]["parser"]["fohdin"])
|
||||
self.fohdin.update(paladin_base_cfg)
|
||||
|
||||
# Assasin config
|
||||
self.trapsin = self.configs["config"]["parser"]["trapsin"]
|
||||
|
||||
+14
-3
@@ -15,19 +15,30 @@ from ui_manager import ScreenObjects, is_visible
|
||||
from random import uniform
|
||||
|
||||
pause_state = True
|
||||
panel_check_paused = False
|
||||
|
||||
def get_pause_state():
|
||||
global pause_state
|
||||
return pause_state
|
||||
|
||||
def set_pause_state(state: bool):
|
||||
global pause_state
|
||||
prev = get_pause_state()
|
||||
if prev != state:
|
||||
debug_str = "pausing" if state else "active"
|
||||
debug_str = "paused" if state else "active"
|
||||
Logger.info(f"Health Manager is now {debug_str}")
|
||||
pause_state = state
|
||||
|
||||
def get_panel_check_paused():
|
||||
return panel_check_paused
|
||||
|
||||
def set_panel_check_paused(state: bool):
|
||||
global panel_check_paused
|
||||
prev = get_panel_check_paused()
|
||||
if prev != state:
|
||||
debug_str = "pausing" if state else "activating"
|
||||
Logger.info(f"Health Manager is now {debug_str} inventory panel check")
|
||||
panel_check_paused = state
|
||||
|
||||
class HealthManager:
|
||||
def __init__(self):
|
||||
self._do_monitor = False
|
||||
@@ -124,7 +135,7 @@ class HealthManager:
|
||||
elif merc_health_percentage <= Config().char["heal_merc"] and last_drink > merc_hp_potion_delay:
|
||||
belt.drink_potion("health", merc=True, stats=[merc_health_percentage])
|
||||
self._last_merc_heal = time.time()
|
||||
if is_visible(ScreenObjects.LeftPanel, img) or is_visible(ScreenObjects.RightPanel, img):
|
||||
if not get_panel_check_paused() and (is_visible(ScreenObjects.LeftPanel, img) or is_visible(ScreenObjects.RightPanel, img)):
|
||||
Logger.warning(f"Found an open inventory / quest / skill / stats page. Close it.")
|
||||
self._count_panel_detects += 1
|
||||
if self._count_panel_detects >= 2:
|
||||
|
||||
@@ -270,7 +270,7 @@ def open(img: np.ndarray = None) -> np.ndarray:
|
||||
img = grab()
|
||||
return img
|
||||
|
||||
def inspect_items(inp_img: np.ndarray = None, close_window: bool = True, game_stats: GameStats = None) -> list[BoxInfo]:
|
||||
def inspect_items(inp_img: np.ndarray = None, close_window: bool = True, game_stats: GameStats = None, ignore_sell: bool = False) -> list[BoxInfo]:
|
||||
"""
|
||||
Iterate over all picked items in inventory--ID items and decide which to stash
|
||||
:param img: Image in which the item is searched (item details should be visible)
|
||||
@@ -377,6 +377,8 @@ def inspect_items(inp_img: np.ndarray = None, close_window: bool = True, game_st
|
||||
keep = keep_item(item_box, found_item) if found_item else False
|
||||
if keep:
|
||||
sell = need_id = False
|
||||
if ignore_sell:
|
||||
sell = False
|
||||
|
||||
box = BoxInfo(
|
||||
img = item_box.data,
|
||||
|
||||
+1
-2
@@ -178,8 +178,7 @@ if __name__ == "__main__":
|
||||
import os
|
||||
from config import Config
|
||||
from char.sorceress import LightSorc
|
||||
from char.hammerdin import Hammerdin
|
||||
import template_finder
|
||||
from char.paladin import Hammerdin
|
||||
from pather import Pather
|
||||
import keyboard
|
||||
|
||||
|
||||
+1
-1
@@ -749,7 +749,7 @@ if __name__ == "__main__":
|
||||
start_detecting_window()
|
||||
from config import Config
|
||||
from char.sorceress import LightSorc
|
||||
from char.hammerdin import Hammerdin
|
||||
from char.paladin.hammerdin import Hammerdin
|
||||
from item.pickit import PickIt
|
||||
pather = Pather()
|
||||
|
||||
|
||||
+2
-8
@@ -464,14 +464,10 @@ class Diablo:
|
||||
#Arrive at and clear Pentagram
|
||||
if not self._cs_pentagram(): return False
|
||||
|
||||
""" NEW APPROACH ONLY HAS 50% SUCCESS RATE
|
||||
#if Config().char["kill_cs_trash"]: if not self._trash_seals("A", "dia_trash_a", [606], "dia_trash_a_loop", 0.78): return False
|
||||
#if Config().char["kill_cs_trash"]: if not self._trash_seals("B", "dia_trash_b", [607], "dia_trash_b_loop", 0.85): return False #high threshold needed to avoid chasing 607 ghosts
|
||||
#if Config().char["kill_cs_trash"]: if not self._trash_seals("C", "dia_trash_c", [608], "dia_trash_c_loop", 0.78): return False
|
||||
"""
|
||||
#OLD APPROACH HAS 80% SUCCESS RATE
|
||||
if Config().char["kill_cs_trash"]: self._trash_seals()
|
||||
|
||||
|
||||
# Maintenance at Pentagram after Trash & clear Seal A: Vizier (to the left)
|
||||
if Config().char["kill_cs_trash"]: self._char.kill_cs_trash("pent_before_a")
|
||||
if not self._pather.traverse_nodes([602], self._char): return False
|
||||
@@ -492,13 +488,11 @@ class Diablo:
|
||||
if Config().char["cs_town_visits"]: self._cs_town_visit("C")
|
||||
if do_pre_buff: self._char.pre_buff()
|
||||
if not self._layoutcheck("C", "Infector", "dia_c_layout_bold", "layoutcheck_c", [650660], 0.83, None, ["DIA_C2G_BOSS_CLOSED_LAYOUTCHECK1", "DIA_C2G_BOSS_CLOSED_LAYOUTCHECK4", "DIA_C2G_BOSS_CLOSED_LAYOUTCHECK5", "DIA_C2G_BOSS_CLOSED_LAYOUTCHECK2", "DIA_C2G_BOSS_CLOSED_LAYOUTCHECK3",], ["DIA_C1F_LAYOUTCHECK1", "DIA_C1F_LAYOUTCHECK2", "DIA_C1F_LAYOUTCHECK3"]): return False
|
||||
|
||||
# Kill Diablo
|
||||
Logger.info("Waiting for Diablo to spawn")
|
||||
if not self._pather.traverse_nodes([602], self._char): return False
|
||||
self._char.kill_diablo()
|
||||
if Config().general["info_screenshots"]: cv2.imwrite(f"./info_screenshots/info_dia_kill_" + time.strftime("%Y%m%d_%H%M%S") + ".png", grab())
|
||||
self._picked_up_items = self._pickit.pick_up_items(char=self._char)
|
||||
self._picked_up_items |= self._pickit.pick_up_items(char=self._char)
|
||||
wait(0.5, 0.7)
|
||||
return (Location.A4_DIABLO_END, self._picked_up_items)
|
||||
|
||||
|
||||
+52
-13
@@ -4,7 +4,7 @@ import time
|
||||
from screen import convert_screen_to_monitor, grab, convert_screen_to_abs, convert_abs_to_monitor
|
||||
from logger import Logger
|
||||
from math import dist
|
||||
from utils.misc import color_filter
|
||||
from utils.misc import color_filter, is_in_roi
|
||||
import json
|
||||
from dataclasses import dataclass
|
||||
|
||||
@@ -17,26 +17,51 @@ FILTER_RANGES=[
|
||||
class TargetInfo:
|
||||
roi: tuple = None
|
||||
center: tuple = None
|
||||
center_abs: tuple = None
|
||||
center_monitor: tuple = None
|
||||
distance: int = 0
|
||||
|
||||
def log_targets(targets: list[TargetInfo]):
|
||||
if (num_targets := len(targets)) > 0:
|
||||
if num_targets == 1:
|
||||
msg = f"{num_targets} target detected"
|
||||
elif num_targets > 1:
|
||||
msg = f"{num_targets} targets detected, closest"
|
||||
Logger.debug(f"{msg} at {targets[0].center}, distance: {round(targets[0].distance)}")
|
||||
|
||||
def _dist_to_center(pos):
|
||||
return dist(pos, (1280/2, 720/2))
|
||||
|
||||
def get_visible_targets(img: np.ndarray = None, radius_min: int = 150, radius_max: int = 1280) -> list[TargetInfo]:
|
||||
def get_visible_targets(
|
||||
img: np.ndarray = None,
|
||||
radius_min: int = 150,
|
||||
radius_max: int = 1280,
|
||||
ignore_roi: list[int] = [600, 300, (1280/2 - 600)*2, (720/2 - 300)*2],
|
||||
use_radius: bool = False
|
||||
) -> list[TargetInfo]:
|
||||
"""
|
||||
:param img: The image to find targets in
|
||||
:param radius_min: The minimum radius of the target [Default: 150, Integer 0 - 1280]
|
||||
:param radius_max: The maximum radius of the target [Default: 1280, Integer 0 - 1280]
|
||||
:param ignore_roi: The region of interest to ignore [Default: [600, 300, (1280/2 - 600)*2, (720/2 - 300)*2]]
|
||||
:param use_radius: Whether to use the radius of the target (True) or the ignore_roi parameter (False)
|
||||
Returns a list of TargetInfo objects
|
||||
"""
|
||||
img = grab() if img is None else img
|
||||
targets = []
|
||||
for filter in FILTER_RANGES:
|
||||
filterimage, threshz = _process_image(img, mask_char=True, mask_hud=True, info_ss=False, **filter) # HSV Filter for BLUE and GREEN (Posison Nova & Holy Freeze)
|
||||
filterimage, rectangles, positions = _add_markers(filterimage, threshz, info_ss=False, rect_min_size=100, rect_max_size=200, marker=True) # rather large rectangles
|
||||
filterimage, threshz = _process_image(img, mask_char=True, mask_hud=True, **filter) # HSV Filter for BLUE and GREEN (Posison Nova & Holy Freeze)
|
||||
filterimage, rectangles, positions = _add_markers(filterimage, threshz, rect_min_size=100, rect_max_size=200, marker=True) # rather large rectangles
|
||||
if positions:
|
||||
for cnt, position in enumerate(positions):
|
||||
distance = _dist_to_center(position)
|
||||
if radius_min <= _dist_to_center(position) <= radius_max:
|
||||
condition = (radius_min <= distance <= radius_max) if use_radius else (not is_in_roi(ignore_roi, position))
|
||||
if condition:
|
||||
targets.append(TargetInfo(
|
||||
roi = rectangles[cnt],
|
||||
center = position,
|
||||
center_monitor = convert_screen_to_monitor(position),
|
||||
center_abs = convert_screen_to_abs(position),
|
||||
distance = distance
|
||||
))
|
||||
if targets:
|
||||
@@ -72,13 +97,12 @@ def _bright_contrast(img: np.ndarray, brightness: int = 255, contrast: int = 127
|
||||
cal = cv2.addWeighted(cal, alpha, cal, 0, gamma)
|
||||
return cal
|
||||
|
||||
def _process_image(img, mask_char:bool=False, mask_hud:bool=True, info_ss:bool=False, erode:int=None, dilate:int=None, blur:int=None, lh:int=None, ls:int=None, lv:int=None, uh:int=None, us:int=None, uv:int=None, bright:int=None, contrast:int=None, thresh:int=None, invert:int=None):
|
||||
def _process_image(img, mask_char:bool=False, mask_hud:bool=True, erode:int=None, dilate:int=None, blur:int=None, lh:int=None, ls:int=None, lv:int=None, uh:int=None, us:int=None, uv:int=None, bright:int=None, contrast:int=None, thresh:int=None, invert:int=None):
|
||||
"""
|
||||
Helper function that will apply HSV filters
|
||||
:param img: The image to which filters should be applied
|
||||
:param mask_char: apply a black rectangle to mask your char (e.g. filter out Set-Item glow effect or auras) [Default: False, Bool]
|
||||
:param hud_mask: removes the HUD from the returned image [Default: True, Bool]
|
||||
:param info_ss: Save an image of the applied filters in folder INFO_SCREENSHOTS [Default: False, Bool]
|
||||
:param erode: erode (thin lines) in the picture [Default: None, Integer, no filter: 0, 0 - 36]
|
||||
:param dilate: dilate (thicken lines) in the picture [Default: None, Integer, no filter: 0, 0 - 36]
|
||||
:param blur: blur the picture [Default: None, no filter: 0, Integer, 0 - 30]
|
||||
@@ -95,7 +119,6 @@ def _process_image(img, mask_char:bool=False, mask_hud:bool=True, info_ss:bool=F
|
||||
Returns variables filterimage and threshz
|
||||
"""
|
||||
img = img
|
||||
if info_ss: cv2.imwrite(f"./info_screenshots/info_apply_filter_input_" + time.strftime("%Y%m%d_%H%M%S") + ".png", img)
|
||||
if mask_hud:
|
||||
hud_mask = cv2.imread(f"./assets/hud_mask.png", cv2.IMREAD_GRAYSCALE)
|
||||
img = cv2.bitwise_and(img, img, mask=hud_mask)
|
||||
@@ -115,16 +138,14 @@ def _process_image(img, mask_char:bool=False, mask_hud:bool=True, info_ss:bool=F
|
||||
if thresh: _, threshz = cv2.threshold(threshz, thresh, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
|
||||
if invert: img = 255 - img
|
||||
filterimage = img
|
||||
if info_ss: cv2.imwrite(f"./info_screenshots/info_apply_filter_output_" + time.strftime("%Y%m%d_%H%M%S") + ".png", img)
|
||||
return filterimage, threshz
|
||||
|
||||
# add rectangles and crosses - adapted from Nathan's Live-View
|
||||
def _add_markers(img:str, threshz:str, info_ss:bool=False, rect_min_size:int=20, rect_max_size:int=50, line_color:str=(0, 255, 0), line_type:str=cv2.LINE_4, marker:bool=False, marker_color:str=(0, 255, 255), marker_type:str=cv2.MARKER_CROSS):
|
||||
def _add_markers(img:str, threshz:str, rect_min_size:int=20, rect_max_size:int=50, line_color:str=(0, 255, 0), line_type:str=cv2.LINE_4, marker:bool=False, marker_color:str=(0, 255, 255), marker_type:str=cv2.MARKER_CROSS):
|
||||
"""
|
||||
Helper function that will add rectangles and crosshairs to allow object detection
|
||||
:param img: The image to which filters should be applied
|
||||
:param threshz: The image that had the threshold adjusted (obtained from _process_image())
|
||||
:param info_ss: Take a screenshot for each step [Default: False, Bool]
|
||||
:param rect_min_size: Minimum size for the rectangle to be drawn [Default: 20, Integer]
|
||||
:param rect_max_size: Minimum size for the rectangle to be drawn [Default: 50, Integer]
|
||||
:param line_color: Color of the rectangle line [Default: (0, 255, 0)]
|
||||
@@ -210,7 +231,7 @@ class LiveViewer:
|
||||
except cv2.error:
|
||||
return
|
||||
self.image, threshz = _process_image(self.image, **self.settings)
|
||||
filterimage, _, _ = _add_markers(self.image, threshz, info_ss=False, rect_min_size=100, rect_max_size=200, marker=True)
|
||||
filterimage, _, _ = _add_markers(self.image, threshz, rect_min_size=100, rect_max_size=200, marker=True)
|
||||
|
||||
cv2.imshow("Results", filterimage)
|
||||
cv2.waitKey(1)
|
||||
@@ -229,4 +250,22 @@ if __name__ == "__main__":
|
||||
start_detecting_window()
|
||||
print("Move to d2r window and press f11")
|
||||
keyboard.wait("f11")
|
||||
l = LiveViewer()
|
||||
# l = LiveViewer()
|
||||
|
||||
masked_image = False
|
||||
|
||||
def _toggle_masked_image():
|
||||
global masked_image
|
||||
masked_image = not masked_image
|
||||
|
||||
while 1:
|
||||
img = grab()
|
||||
targets = get_visible_targets()
|
||||
|
||||
display_img = img.copy()
|
||||
|
||||
for target in targets:
|
||||
x, y = target.center
|
||||
cv2.rectangle(display_img, target.roi[:2], (target.roi[0] + target.roi[2], target.roi[1] + target.roi[3]), (0, 0, 255), 1)
|
||||
cv2.imshow('test', display_img)
|
||||
key = cv2.waitKey(1)
|
||||
|
||||
@@ -254,7 +254,7 @@ if __name__ == "__main__":
|
||||
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 char.paladin.hammerdin import Hammerdin
|
||||
from pather import Pather
|
||||
pather = Pather()
|
||||
char = Hammerdin(Config().hammerdin, Config().char, pather)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from screen import grab, convert_monitor_to_screen, convert_abs_to_screen
|
||||
from screen import grab, convert_monitor_to_screen, convert_abs_to_screen, convert_screen_to_abs
|
||||
import cv2
|
||||
from config import Config
|
||||
import template_finder
|
||||
|
||||
Reference in New Issue
Block a user