From 30d64c88dc479415e450321492aa9f30b648882e Mon Sep 17 00:00:00 2001 From: pinibat Date: Mon, 17 Apr 2023 21:51:41 +0100 Subject: [PATCH 1/6] Adding functionality to specify the slot order of items to drop. This can better mimic how humans drop from the inventory. Also added an optional chance of misclicking when dropping an item which is more human-like. The missed item(s) will be reattempted once all other slots to drop have been attempted. Added this functionality to the sample woodcutter bot. --- src/model/bot.py | 97 ++++++++++++++++++++++++++++++------ src/model/osrs/woodcutter.py | 3 +- 2 files changed, 85 insertions(+), 15 deletions(-) diff --git a/src/model/bot.py b/src/model/bot.py index 48190351..f6e014d4 100644 --- a/src/model/bot.py +++ b/src/model/bot.py @@ -77,6 +77,61 @@ class BotStatus(Enum): CONFIGURING = 4 CONFIGURED = 5 +class DropOrder(Enum): + """ + Contains a list of slot indexes that define the order in which to drop. + This provides more natural drop orders than the default 'horizontal' order. + Slots are 0-indexed. + """ + """ + Left to right, row by row. + Drop order: + 1 2 3 4 + 5 6 7 8 + 9 10 11 12 + 13 14 15 16 + 17 18 19 20 + 21 22 23 24 + 25 26 27 28 + """ + HORIZONTAL = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27] + """ + Top to bottom, column by column. + Drop order: + 1 8 15 22 + 2 9 16 23 + 3 10 17 24 + 4 11 18 25 + 5 12 19 26 + 6 13 20 27 + 7 14 21 28 + """ + VERTICAL = [0,4,8,12,16,20,24,1,5,9,13,17,21,25,2,6,10,14,18,22,26,3,7,11,15,19,23,27] + """ + Top to bottom, bottom to top, column by column. + Drop order: + 1 14 15 28 + 2 13 16 27 + 3 12 17 26 + 4 11 18 25 + 5 10 19 24 + 6 9 20 23 + 7 8 21 22 + """ + VERTICAL_ALTERNATING = [0,4,8,12,16,20,24,25,21,17,13,9,5,1,2,6,10,14,18,22,26,27,23,19,15,11,7,3] + """ + Left to right, right to left, row by row. + Drop order: + 1 2 3 4 + 8 7 6 5 + 9 10 11 12 + 16 15 14 13 + 17 18 19 20 + 24 23 22 21 + 25 26 27 28 + """ + HORIZONTAL_ALTERNATING = [0,1,2,3,7,6,5,4,8,9,10,11,15,14,13,12,16,17,18,19,23,22,21,20,24,25,26,27] + class Bot(ABC): mouse = Mouse() @@ -265,26 +320,40 @@ def drop_all(self, skip_rows: int = 0, skip_slots: List[int] = None) -> None: pag.keyUp("shift") def drop(self, slots: List[int]) -> None: + self.drop(slots, DropOrder.HORIZONTAL, False) + + def drop(self, slots: List[int], drop_order: DropOrder, misclick_chance: bool) -> None: """ - Shift-clicks inventory slots to drop items. + Shift-clicks inventory slots to drop items. Args: slots: The indices of slots to drop. + drop_order: The overall order in which to try dropping slots + misclick_chance: If True provides a chance that a slot will not be dropped on the first pass. + If a slot is missed then it will be reattempted after all the others have been attempted. + Offers more human-like behavior if set. """ + slots_to_drop = slots + probablity_to_miss = 0.05 self.log_msg("Dropping items...") pag.keyDown("shift") - for i, slot in enumerate(self.win.inventory_slots): - if i not in slots: - continue - p = slot.random_point() - self.mouse.move_to( - (p[0], p[1]), - mouseSpeed="fastest", - knotsCount=1, - offsetBoundaryY=40, - offsetBoundaryX=40, - tween=pytweening.easeInOutQuad, - ) - self.mouse.click() + + while len(slots_to_drop) > 0: + for i in drop_order.value: + if i not in slots_to_drop: + continue + slot = self.win.inventory_slots[i] + p = slot.random_point() + self.mouse.move_to( + (p[0], p[1]), + mouseSpeed="fastest", + knotsCount=1, + offsetBoundaryY=40, + offsetBoundaryX=40, + tween=pytweening.easeInOutQuad, + ) + if not rd.random_chance(probability=probablity_to_miss): + self.mouse.click() + slots_to_drop.remove(i) pag.keyUp("shift") def friends_nearby(self) -> bool: diff --git a/src/model/osrs/woodcutter.py b/src/model/osrs/woodcutter.py index 9dfcd5de..27e94958 100644 --- a/src/model/osrs/woodcutter.py +++ b/src/model/osrs/woodcutter.py @@ -3,6 +3,7 @@ import utilities.api.item_ids as ids import utilities.color as clr import utilities.random_util as rd +from model.bot import DropOrder from model.osrs.osrs_bot import OSRSBot from model.runelite_bot import BotStatus from utilities.api.morg_http_client import MorgHTTPSocket @@ -135,7 +136,7 @@ def __drop_logs(self, api_s: StatusSocket): Since we made the `api` and `logs` variables assigned to `self`, we can access them from this function. """ slots = api_s.get_inv_item_indices(ids.logs) - self.drop(slots) + self.drop(slots, DropOrder.VERTICAL_ALTERNATING, True) self.logs += len(slots) self.log_msg(f"Logs cut: ~{self.logs}") time.sleep(1) From d24b2b1caf6c03023c4c723598032ec5457fea03 Mon Sep 17 00:00:00 2001 From: pinibat Date: Tue, 18 Apr 2023 16:18:56 +0100 Subject: [PATCH 2/6] Removing misclick functionality as I want to redo it into something smarter than just missing a click --- src/model/bot.py | 7 ++----- src/model/osrs/woodcutter.py | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/model/bot.py b/src/model/bot.py index f6e014d4..1c1d9cc3 100644 --- a/src/model/bot.py +++ b/src/model/bot.py @@ -320,17 +320,14 @@ def drop_all(self, skip_rows: int = 0, skip_slots: List[int] = None) -> None: pag.keyUp("shift") def drop(self, slots: List[int]) -> None: - self.drop(slots, DropOrder.HORIZONTAL, False) + self.drop(slots, DropOrder.HORIZONTAL) - def drop(self, slots: List[int], drop_order: DropOrder, misclick_chance: bool) -> None: + def drop(self, slots: List[int], drop_order: DropOrder) -> None: """ Shift-clicks inventory slots to drop items. Args: slots: The indices of slots to drop. drop_order: The overall order in which to try dropping slots - misclick_chance: If True provides a chance that a slot will not be dropped on the first pass. - If a slot is missed then it will be reattempted after all the others have been attempted. - Offers more human-like behavior if set. """ slots_to_drop = slots probablity_to_miss = 0.05 diff --git a/src/model/osrs/woodcutter.py b/src/model/osrs/woodcutter.py index 27e94958..f237ed06 100644 --- a/src/model/osrs/woodcutter.py +++ b/src/model/osrs/woodcutter.py @@ -136,7 +136,7 @@ def __drop_logs(self, api_s: StatusSocket): Since we made the `api` and `logs` variables assigned to `self`, we can access them from this function. """ slots = api_s.get_inv_item_indices(ids.logs) - self.drop(slots, DropOrder.VERTICAL_ALTERNATING, True) + self.drop(slots, DropOrder.VERTICAL_ALTERNATING) self.logs += len(slots) self.log_msg(f"Logs cut: ~{self.logs}") time.sleep(1) From 38bef2f1bb538150ec0a666de283c45853e19674 Mon Sep 17 00:00:00 2001 From: pinibat Date: Tue, 18 Apr 2023 19:58:09 +0100 Subject: [PATCH 3/6] Revert "Removing misclick functionality as I want to redo it into something smarter than just missing a click" This reverts commit 375c573bb6b7ec9a90faa0f2e9d3425b201e4bdf. --- src/model/bot.py | 7 +++++-- src/model/osrs/woodcutter.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/model/bot.py b/src/model/bot.py index 1c1d9cc3..f6e014d4 100644 --- a/src/model/bot.py +++ b/src/model/bot.py @@ -320,14 +320,17 @@ def drop_all(self, skip_rows: int = 0, skip_slots: List[int] = None) -> None: pag.keyUp("shift") def drop(self, slots: List[int]) -> None: - self.drop(slots, DropOrder.HORIZONTAL) + self.drop(slots, DropOrder.HORIZONTAL, False) - def drop(self, slots: List[int], drop_order: DropOrder) -> None: + def drop(self, slots: List[int], drop_order: DropOrder, misclick_chance: bool) -> None: """ Shift-clicks inventory slots to drop items. Args: slots: The indices of slots to drop. drop_order: The overall order in which to try dropping slots + misclick_chance: If True provides a chance that a slot will not be dropped on the first pass. + If a slot is missed then it will be reattempted after all the others have been attempted. + Offers more human-like behavior if set. """ slots_to_drop = slots probablity_to_miss = 0.05 diff --git a/src/model/osrs/woodcutter.py b/src/model/osrs/woodcutter.py index f237ed06..27e94958 100644 --- a/src/model/osrs/woodcutter.py +++ b/src/model/osrs/woodcutter.py @@ -136,7 +136,7 @@ def __drop_logs(self, api_s: StatusSocket): Since we made the `api` and `logs` variables assigned to `self`, we can access them from this function. """ slots = api_s.get_inv_item_indices(ids.logs) - self.drop(slots, DropOrder.VERTICAL_ALTERNATING) + self.drop(slots, DropOrder.VERTICAL_ALTERNATING, True) self.logs += len(slots) self.log_msg(f"Logs cut: ~{self.logs}") time.sleep(1) From c6613fd51975d1f180d54f42ef83f189ee631765 Mon Sep 17 00:00:00 2001 From: pinibat Date: Wed, 19 Apr 2023 15:29:55 +0100 Subject: [PATCH 4/6] Adding misclick functionality to dropping --- src/model/bot.py | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/src/model/bot.py b/src/model/bot.py index f6e014d4..380429f5 100644 --- a/src/model/bot.py +++ b/src/model/bot.py @@ -333,7 +333,10 @@ def drop(self, slots: List[int], drop_order: DropOrder, misclick_chance: bool) - Offers more human-like behavior if set. """ slots_to_drop = slots - probablity_to_miss = 0.05 + probablity_to_miss = 0.01 + probablity_to_drag = 0.01 + probablity_to_click_between_slots = 0.02 + is_dragging = False self.log_msg("Dropping items...") pag.keyDown("shift") @@ -342,7 +345,22 @@ def drop(self, slots: List[int], drop_order: DropOrder, misclick_chance: bool) - if i not in slots_to_drop: continue slot = self.win.inventory_slots[i] - p = slot.random_point() + + point_in_slot = True + + # Chance to choose a point on the inventory grid between items + if misclick_chance and rd.random_chance(probability=probablity_to_click_between_slots): + self.log_msg("Clicking grid not slot.") + # Find bounds of the slot grid by increasing the slot bounds by 6 pixels horizontally, and 4 pixels vertically + grid = Rectangle(left=slot.left - 3, top=slot.top - 2, width=slot.width + 6, height=slot.height + 4) + + # Find a point that is on the grid boundary by looking for a random point in the grid rectangle and relooking if the point lies in the slot + while(point_in_slot): + p = grid.random_point() + point_in_slot = (p[0] >= slot.left and p[0] <= slot.left + slot.width and p[1] >= slot.top and p[1] <= slot.top + slot.height) + else: + p = slot.random_point() + self.mouse.move_to( (p[0], p[1]), mouseSpeed="fastest", @@ -351,11 +369,29 @@ def drop(self, slots: List[int], drop_order: DropOrder, misclick_chance: bool) - offsetBoundaryX=40, tween=pytweening.easeInOutQuad, ) - if not rd.random_chance(probability=probablity_to_miss): + # Release button if previous iteration dragged to this position + if(is_dragging): + pag.mouseUp() + is_dragging = False + + if misclick_chance and rd.random_chance(probability=probablity_to_miss): + self.log_msg("Failing to click.") + continue + elif misclick_chance and rd.random_chance(probability=probablity_to_drag): + self.log_msg("Dragging instead of clicking") + pag.mouseDown() + is_dragging = True + else: self.mouse.click() - slots_to_drop.remove(i) + # Only treat item as dropped if we clicked on the slot, not the grid + if(point_in_slot): + slots_to_drop.remove(i) pag.keyUp("shift") + # Release button if final iteration + if(is_dragging): + pag.mouseUp() + def friends_nearby(self) -> bool: """ Checks the minimap for green dots to indicate friends nearby. From b0c68c7737348dbac7f1b38078fb726f45d66928 Mon Sep 17 00:00:00 2001 From: pinibat Date: Sun, 23 Apr 2023 15:06:33 +0100 Subject: [PATCH 5/6] Adding imgur links for drop behaviour examples --- src/model/bot.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/model/bot.py b/src/model/bot.py index 380429f5..711306ce 100644 --- a/src/model/bot.py +++ b/src/model/bot.py @@ -84,7 +84,7 @@ class DropOrder(Enum): Slots are 0-indexed. """ """ - Left to right, row by row. + Left to right, row by row. https://imgur.com/CksPmKe Drop order: 1 2 3 4 5 6 7 8 @@ -96,7 +96,7 @@ class DropOrder(Enum): """ HORIZONTAL = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27] """ - Top to bottom, column by column. + Top to bottom, column by column. https://imgur.com/R42mEcg Drop order: 1 8 15 22 2 9 16 23 @@ -108,7 +108,7 @@ class DropOrder(Enum): """ VERTICAL = [0,4,8,12,16,20,24,1,5,9,13,17,21,25,2,6,10,14,18,22,26,3,7,11,15,19,23,27] """ - Top to bottom, bottom to top, column by column. + Top to bottom, bottom to top, column by column. https://imgur.com/oorxfE2 Drop order: 1 14 15 28 2 13 16 27 @@ -120,7 +120,7 @@ class DropOrder(Enum): """ VERTICAL_ALTERNATING = [0,4,8,12,16,20,24,25,21,17,13,9,5,1,2,6,10,14,18,22,26,27,23,19,15,11,7,3] """ - Left to right, right to left, row by row. + Left to right, right to left, row by row. https://imgur.com/GWKZixY Drop order: 1 2 3 4 8 7 6 5 From 07c3f0295199e940fd45d66c418bf52d085cb45e Mon Sep 17 00:00:00 2001 From: pinibat Date: Tue, 25 Apr 2023 21:37:21 +0100 Subject: [PATCH 6/6] Updating drop function of bot to handle a deep copy rather than a shallow copy of the slots to drop --- src/model/bot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/model/bot.py b/src/model/bot.py index 711306ce..2533bbcc 100644 --- a/src/model/bot.py +++ b/src/model/bot.py @@ -2,6 +2,7 @@ A Bot is a base class for bot script models. It is abstract and cannot be instantiated. Many of the methods in this base class are pre-implemented and can be used by subclasses, or called by the controller. Code in this class should not be modified. """ +import copy import ctypes import platform import re @@ -332,7 +333,7 @@ def drop(self, slots: List[int], drop_order: DropOrder, misclick_chance: bool) - If a slot is missed then it will be reattempted after all the others have been attempted. Offers more human-like behavior if set. """ - slots_to_drop = slots + slots_to_drop = copy.deepcopy(slots) probablity_to_miss = 0.01 probablity_to_drag = 0.01 probablity_to_click_between_slots = 0.02