Gambling by @RB-Development (#445)

This branch will detect if stash is full of gold. Afterwards the gambling functionality (currently only in act 4 implemented) will be called. The bot will gamble for specific items (determined in params.ini) until no gold is left. He will check for properties specified in pickit.ini and sell not matching items.
Supported items are: ring, amulet, circlet, coronet, talon

Leverages recent advanced pickit PR.
This commit is contained in:
RB-Development
2022-02-04 14:12:56 -05:00
committed by GitHub
parent 06623142f7
commit 36d3c2e913
21 changed files with 186 additions and 47 deletions
+1
View File
@@ -127,6 +127,7 @@ run_shenk=0
| 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. |
| fill_shared_stash_first | Fill stash tabs starting from right to left, filling personal stash last |
| gamble_items | List of items to gamble when stash fills with gold. Leave blank to disable. Supported items currently include circlet, ring, coronet, talon, amulet
### Builds
| [sorceress] | Descriptions |
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

+2
View File
@@ -89,6 +89,8 @@ always_repair=0
id_items=1
open_chests=1
fill_shared_stash_first=0
; to gamble, add any/all of the following: circlet, ring, coronet, talon, amulet
gamble_items=
; ===========================
; ==== Builds: Sorceress ====
+13
View File
@@ -298,6 +298,19 @@ class Bot:
if not self._curr_loc:
return self.trigger_or_stop("end_game", failed=True)
# Check if gambling is needed
gambling = self._ui_manager.gambling_needed()
if gambling:
for x in range (4):
self._curr_loc = self._town_manager.gamble(self._curr_loc)
self._ui_manager.gamble(self._item_finder)
if (x ==3):
self._curr_loc = self._town_manager.stash (self._curr_loc)
else:
self._curr_loc = self._town_manager.stash (self._curr_loc, gamble=gambling)
self._ui_manager.set__gold_full (False)
# Start a new run
started_run = False
for key in self._do_runs:
+2 -1
View File
@@ -28,6 +28,7 @@ class Config:
# config data
general = {}
advanced_options = {}
gamble = {}
ui_roi = {}
ui_pos = {}
dclone = {}
@@ -241,8 +242,8 @@ class Config:
"atk_len_cs_trashmobs": float(Config._select_val("char", "atk_len_cs_trashmobs")),
"kill_cs_trash": float(Config._select_val("char", "kill_cs_trash")),
"always_repair": bool(int(Config._select_val("char", "always_repair"))),
"gamble_items": Config._select_val("char", "gamble_items").replace(" ","").split(","),
}
# Sorc base config
sorc_base_cfg = dict(Config._config["sorceress"])
if "sorceress" in Config._custom:
View File
+4
View File
@@ -166,6 +166,10 @@ class NpcManager:
"trade": {
"white": color_filter(self._template_finder.get_template("TRADE"), self._config.colors["white"])[1],
"blue": color_filter(self._template_finder.get_template("TRADE_BLUE"), self._config.colors["blue"])[1],
},
"gamble": {
"white": color_filter(self._template_finder.get_template("GAMBLE"), self._config.colors["white"])[1],
"blue": color_filter(self._template_finder.get_template("GAMBLE_BLUE"), self._config.colors["blue"])[1],
}
},
"roi": [337, 188, (733-337), (622-188)],
+1 -6
View File
@@ -28,12 +28,7 @@ class TemplateFinder:
to find these assets within another image
IMPORTANT: This method must be thread safe!
"""
def __init__(
self,
screen: Screen,
template_pathes: list[str] = ["assets\\templates", "assets\\npc", "assets\\item_properties", "assets\\chests"],
save_last_res: bool = False
):
def __init__(self, screen: Screen, template_pathes: list[str] = ["assets\\templates", "assets\\npc", "assets\\item_properties", "assets\\chests", "assets\\gamble"], save_last_res: bool = False):
self._screen = screen
self._config = Config()
self._save_last_res = save_last_res
+8
View File
@@ -22,6 +22,7 @@ class A4(IAct):
def can_resurrect(self) -> bool: return True
def can_buy_pots(self) -> bool: return True
def can_identify(self) -> bool: return True
def can_gamble(self) -> bool: return True
def can_heal(self) -> bool: return True
def can_stash(self) -> bool: return True
def can_trade_and_repair(self) -> bool: return True
@@ -53,6 +54,13 @@ class A4(IAct):
self._npc_manager.press_npc_btn(Npc.CAIN, "identify")
return Location.A4_TYRAEL_STASH
return False
def gamble (self, curr_loc: Location) -> Union[Location, bool]:
if not self._pather.traverse_nodes((curr_loc, Location.A4_JAMELLA), self._char, force_move=True): return False
if self._npc_manager.open_npc_menu(Npc.JAMELLA):
self._npc_manager.press_npc_btn(Npc.JAMELLA, "gamble")
return Location.A4_JAMELLA
return False
def open_trade_menu(self, curr_loc: Location) -> Union[Location, bool]:
if not self._pather.traverse_nodes((curr_loc, Location.A4_JAMELLA), self._char, force_move=True): return False
+2
View File
@@ -22,6 +22,7 @@ class IAct:
def can_trade_and_repair(self) -> bool: return False
# Is merc resurrection implemented for this Town?
def can_identify(self) -> bool: return False
def can_gamble (self) -> bool: return False
# If any of the above functions return True for the Town, the respective method must be implemented
def open_trade_menu(self, curr_loc: Location) -> Union[Location, bool]: return False
def heal(self, curr_loc: Location) -> Union[Location, bool]: return False
@@ -29,3 +30,4 @@ class IAct:
def open_stash(self, curr_loc: Location) -> Union[Location, bool]: return False
def open_trade_and_repair_menu(self, curr_loc: Location) -> Union[Location, bool]: return False
def identify(self, curr_loc: Location) -> Union[Location, bool]: return False
def gamble (self, curr_loc: Location) -> Union[Location, bool]: return False
+12 -2
View File
@@ -125,8 +125,18 @@ class TownManager:
new_loc = self.go_to_act(5, curr_loc)
if not new_loc: return False
return self._acts[Location.A5_TOWN_START].identify(new_loc)
def gamble (self, curr_loc: Location) -> Union[Location, bool]:
curr_act = TownManager.get_act_from_location(curr_loc)
if curr_act is None: return False
# check if we can Identify in current act
if self._acts[curr_act].can_gamble():
return self._acts[curr_act].gamble(curr_loc)
new_loc = self.go_to_act(4, curr_loc)
if not new_loc: return False
return self._acts[Location.A4_TOWN_START].gamble(new_loc)
def stash(self, curr_loc: Location) -> Union[Location, bool]:
def stash(self, curr_loc: Location, gamble=False) -> Union[Location, bool]:
curr_act = TownManager.get_act_from_location(curr_loc)
if curr_act is None: return False
# check if we can stash in current act
@@ -134,7 +144,7 @@ class TownManager:
new_loc = self._acts[curr_act].open_stash(curr_loc)
if not new_loc: return False
wait(1.0)
self._ui_manager.stash_all_items(self._config.char["num_loot_columns"], self._item_finder)
self._ui_manager.stash_all_items(self._config.char["num_loot_columns"], self._item_finder, gamble)
return new_loc
new_loc = self.go_to_act(5, curr_loc)
if not new_loc: return False
+141 -38
View File
@@ -28,6 +28,8 @@ class UiManager():
self._messenger = Messenger()
self._game_stats = game_stats
self._screen = screen
self._gold_full = False
self._gambling_round = 1
self._curr_stash = {
"items": 3 if self._config.char["fill_shared_stash_first"] else 0,
"gold": 0
@@ -240,14 +242,14 @@ class UiManager():
center_pos = (int(slot[0] + (slot_width // 2)), int(slot[1] + (slot_height // 2)))
return center_pos, slot_img
def _inventory_has_items(self, img, num_loot_columns: int) -> bool:
def _inventory_has_items(self, img, num_loot_columns: int, num_ignore_columns=0) -> bool:
"""
Check if Inventory has any items
:param img: Img from screen.grab() with inventory open
:param num_loot_columns: Number of columns to check from left
:return: Bool if inventory still has items or not
"""
for column, row in itertools.product(range(num_loot_columns), range(4)):
for column, row in itertools.product(range(num_ignore_columns, num_loot_columns), range(4)):
_, slot_img = self.get_slot_pos_and_img(self._config, img, column, row)
if self._slot_has_item(slot_img):
return True
@@ -372,7 +374,7 @@ class UiManager():
mouse.click(button="left")
wait(0.3, 0.4)
def stash_all_items(self, num_loot_columns: int, item_finder: ItemFinder):
def stash_all_items(self, num_loot_columns: int, item_finder: ItemFinder, gamble = False):
"""
Stashing all items in inventory. Stash UI must be open when calling the function.
:param num_loot_columns: Number of columns used for loot from left
@@ -387,41 +389,51 @@ class UiManager():
Logger.error("Could not determine to be in stash menu. Continue...")
return
Logger.debug("Found inventory gold btn")
# stash gold
if self._config.char["stash_gold"]:
inventory_no_gold = self._template_finder.search("INVENTORY_NO_GOLD", self._screen.grab(), roi=self._config.ui_roi["inventory_gold"], threshold=0.83)
if inventory_no_gold.valid:
Logger.debug("Skipping gold stashing")
else:
Logger.debug("Stashing gold")
self._move_to_stash_tab(min(3, self._curr_stash["gold"]))
mouse.move(*gold_btn.center, randomize=4)
wait(0.1, 0.15)
mouse.press(button="left")
wait(0.25, 0.35)
mouse.release(button="left")
wait(0.4, 0.6)
keyboard.send("enter") #if stash already full of gold just nothing happens -> gold stays on char -> no popup window
wait(1.0, 1.2)
# move cursor away from button to interfere with screen grab
mouse.move(-120, 0, absolute=False, randomize=15)
if not gamble:
# stash gold
if self._config.char["stash_gold"]:
inventory_no_gold = self._template_finder.search("INVENTORY_NO_GOLD", self._screen.grab(), roi=self._config.ui_roi["inventory_gold"], threshold=0.83)
if not inventory_no_gold.valid:
Logger.info("Stash tab is full of gold, selecting next stash tab.")
self._curr_stash["gold"] += 1
if self._config.general["info_screenshots"]:
cv2.imwrite("./info_screenshots/info_gold_stash_full_" + time.strftime("%Y%m%d_%H%M%S") + ".png", self._screen.grab())
if self._curr_stash["gold"] > 3:
# turn off gold pickup
self._config.turn_off_goldpickup()
# inform user about it
Logger.info("All stash tabs and character are full of gold, turn of gold pickup")
if self._config.general["custom_message_hook"]:
self._messenger.send_gold()
else:
# move to next stash
wait(0.5, 0.6)
return self.stash_all_items(num_loot_columns, item_finder)
if inventory_no_gold.valid:
Logger.debug("Skipping gold stashing")
else:
Logger.debug("Stashing gold")
self._move_to_stash_tab(min(3, self._curr_stash["gold"]))
mouse.move(*gold_btn.center, randomize=4)
wait(0.1, 0.15)
mouse.press(button="left")
wait(0.25, 0.35)
mouse.release(button="left")
wait(0.4, 0.6)
keyboard.send("enter") #if stash already full of gold just nothing happens -> gold stays on char -> no popup window
wait(1.0, 1.2)
# move cursor away from button to interfere with screen grab
mouse.move(-120, 0, absolute=False, randomize=15)
inventory_no_gold = self._template_finder.search("INVENTORY_NO_GOLD", self._screen.grab(), roi=self._config.ui_roi["inventory_gold"], threshold=0.83)
if not inventory_no_gold.valid:
Logger.info("Stash tab is full of gold, selecting next stash tab.")
self._curr_stash["gold"] += 1
if self._config.general["info_screenshots"]:
cv2.imwrite("./info_screenshots/info_gold_stash_full_" + time.strftime("%Y%m%d_%H%M%S") + ".png", self._screen.grab())
if self._curr_stash["gold"] > 3:
#decide if gold pickup should be disabled or gambling is active
if (self._config.char["gamble_items"]):
self._gold_full = True
else:
# turn off gold pickup
self._config.char["stash_gold"] = False
self._config.items["misc_gold"].pickit_type = 0
item_finder.update_items_to_pick(self._config)
# inform user about it
msg = "All stash tabs and character are full of gold, turn of gold pickup"
Logger.info(msg)
if self._config.general["custom_message_hook"]:
self._messenger.send(msg=f"{self._config.general['name']}: {msg}")
else:
# move to next stash
wait(0.5, 0.6)
return self.stash_all_items(num_loot_columns, item_finder)
else:
self.transfer_shared_to_private_gold (self._gambling_round)
# stash stuff
self._move_to_stash_tab(self._curr_stash["items"])
center_m = self._screen.convert_abs_to_monitor((0, 0))
@@ -502,6 +514,34 @@ class UiManager():
wait(0.4, 0.5)
keyboard.send("esc")
def transfer_shared_to_private_gold (self, count: int):
for x in range (3):
self._move_to_stash_tab (count)
stash_gold_btn = self._template_finder.search("INVENTORY_GOLD_BTN", self._screen.grab(), roi=self._config.ui_roi["gold_btn_stash"], threshold=0.83)
if stash_gold_btn.valid:
x,y = self._screen.convert_screen_to_monitor(stash_gold_btn.center)
mouse.move(x, y, randomize=4)
wait (0.4, 0.5)
mouse.press(button="left")
wait (0.1, 0.15)
mouse.release(button="left")
wait (0.1, 0.15)
keyboard.send ("Enter")
wait (0.1, 0.15)
self._move_to_stash_tab (0)
inventory_gold_btn = self._template_finder.search("INVENTORY_GOLD_BTN", self._screen.grab(), roi=self._config.ui_roi["gold_btn"], threshold=0.83)
if inventory_gold_btn.valid:
x,y = self._screen.convert_screen_to_monitor(inventory_gold_btn.center)
mouse.move(x, y, randomize=4)
wait (0.4, 0.5)
mouse.press(button="left")
wait (0.1, 0.15)
mouse.release(button="left")
wait (0.1, 0.15)
keyboard.send ("Enter")
wait (0.1, 0.15)
self._gambling_round += 1
def should_stash(self, num_loot_columns: int):
"""
Check if there are items that need to be stashed in the inventory
@@ -596,6 +636,69 @@ class UiManager():
use_grayscale=True)
return template_match.valid
def gambling_needed(self) -> bool:
return self._gold_full
def set__gold_full (self, bool: bool):
self._gold_full = bool
self._gambling_round = 1
def gamble(self, item_finder: ItemFinder):
gold = True
gamble_on = True
if self._config.char["num_loot_columns"]%2==0:
ignore_columns = self._config.char["num_loot_columns"]-1
else:
ignore_columns = self._config.char["num_loot_columns"]-2
template_match = self._template_finder.search_and_wait("REFRESH", threshold=0.79, time_out=4)
if template_match.valid:
#Gambling window is open. Starting to spent some coins
while (gamble_on and gold):
if (self._inventory_has_items (self._screen.grab(),self._config.char["num_loot_columns"], ignore_columns) and self._inventory_has_items (self._screen.grab(),2)):
gamble_on = False
self.close_vendor_screen ()
break
for item in self._config.char["gamble_items"]:
template_match_item = self._template_finder.search (item.upper(), self._screen.grab(), roi=self._config.ui_roi["vendor_stash"], normalize_monitor=True)
while not template_match_item.valid:
#Refresh gambling screen
template_match = self._template_finder.search ("REFRESH", self._screen.grab(), normalize_monitor=True)
if (template_match.valid):
mouse.move(*template_match.center, randomize=12, delay_factor=[1.0, 1.5])
wait(0.1, 0.15)
mouse.click(button="left")
wait(0.1, 0.15)
template_match_item = self._template_finder.search (item.upper(), self._screen.grab(), roi=self._config.ui_roi["vendor_stash"], normalize_monitor=True)
#item found in gambling menu
mouse.move(*template_match_item.center, randomize=12, delay_factor=[1.0, 1.5])
wait(0.1, 0.15)
mouse.click(button="right")
wait(0.1, 0.15)
template_match = self._template_finder.search ("no_gold".upper(), self._screen.grab(), threshold= 0.90)
#check if gold is av
if template_match.valid:
gold = False
self.close_vendor_screen ()
break
for column, row in itertools.product(range(self._config.char["num_loot_columns"]), range(4)):
img = self._screen.grab()
slot_pos, slot_img = self.get_slot_pos_and_img(self._config, img, column, row)
if self._slot_has_item(slot_img):
x_m, y_m = self._screen.convert_screen_to_monitor(slot_pos)
mouse.move(x_m, y_m, randomize=10, delay_factor=[1.0, 1.3])
# check item again and discard it or stash it
wait(1.2, 1.4)
hovered_item = self._screen.grab()
if not self._keep_item(item_finder, hovered_item):
keyboard.send('ctrl', do_release=False)
wait(0.1, 0.15)
mouse.click (button="left")
wait(0.1, 0.15)
keyboard.send('ctrl', do_press=False)
#Stashing needed
else:
Logger.warning("gambling failed")
def enable_no_pickup(self) -> bool:
"""
Checks the best match between enabled and disabled an retrys if already set.
@@ -658,4 +761,4 @@ if __name__ == "__main__":
template_finder = TemplateFinder(screen)
item_finder = ItemFinder()
ui_manager = UiManager(screen, template_finder, game_stats)
ui_manager.stash_all_items(5, item_finder)
ui_manager.gamble (item_finder)