Advertisement
Guest User

Untitled

a guest
Feb 22nd, 2019
69
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.35 KB | None | 0 0
  1. """
  2. Autoclicker
  3.  
  4. Module workflow:
  5.    1. Setup regions using the HERO_SETTINGS and BOTTOM_LEFT_COORDS settings.
  6.    
  7.    2. Create a MatchController object, which later acts as the "main trigger"
  8.       that kicks off the autoclicking behaviour.
  9.    
  10.    3. Iterate over each Region in SLOTS_REGION, assigning each region to
  11.       a hero by checking against 'facepng' as found in HERO_SETTINGS.
  12.  
  13.    4. Create a suitable Hero object using the data in HERO_SETTINGS. Then,
  14.       register that object to the controller.
  15.  
  16.    5. Call start() on the controller.
  17. """
  18.  
  19. from sikuli import *
  20.  
  21. COOLDOWN_PNG = Pattern("COOLDOWN_PNG.png").similar(0.61)
  22. ## SETTINGS
  23. ###########
  24. HERO_SETTINGS = {
  25.     'Maria': {
  26.         'facepng': "1550770324810.png",
  27.         'opener': [3, 2, 1],
  28.         'rotation': []
  29.     },
  30.     'Annette': {
  31.         'facepng': "1550770347658.png",
  32.         'opener': [3, 1, 2],
  33.         'rotation': []
  34.     },
  35.     'Viska': {
  36.         'facepng': "1550770359778.png",
  37.         'opener': [2, 3, 1],
  38.         'rotation': []
  39.     },
  40.     'Aselica': {
  41.         'facepng': "1550770375611.png",
  42.         'opener': [2, 1, 2],
  43.         'rotation': []
  44.     },
  45. }
  46.  
  47. # Coordinates of bottom left corner of slot 1
  48. BOTTOM_LEFT_COORDS = (75, 757)#########made 45 from 75
  49.  
  50. # Unused
  51. #lowCD = Pattern("CDimageLowDetection-1.png").similar(0.57)
  52.  
  53. ###########
  54.  
  55. # Derive dimensions of UI using settings
  56. # Use the horizontal distance from edge as unit of measure
  57. UNIT = BOTTOM_LEFT_COORDS[0] + 10
  58. # Infer skill dimensions
  59. SKILL_WIDTH = int(1 * UNIT)
  60. SKILL_HEIGHT = int(1.18 * UNIT)
  61.  
  62. # Slot dimensions
  63. SLOT_HEIGHT = int(3.1 * UNIT)
  64. SLOT_WIDTH = int(SKILL_WIDTH * 3.2)
  65.  
  66. # Infer upper bound of slots; this cuts off just above the eyes of the face
  67. SLOTS_Y_COORD = BOTTOM_LEFT_COORDS[1] - SLOT_HEIGHT
  68.  
  69. # The distance between slots
  70. PADDING_BETWEEN_SLOTS = int(0.57 * UNIT)
  71.  
  72. # Rough approximation of the area where the face usually is
  73. FACE_WIDTH, FACE_HEIGHT = SKILL_WIDTH, SKILL_HEIGHT
  74.  
  75.  
  76. # Compile the measurements above into a simple dict of {slotnum: Region obj}
  77. SLOTS_REGION = {
  78.     1: Region(
  79.         UNIT - 9,
  80.         SLOTS_Y_COORD,
  81.         SLOT_WIDTH, SLOT_HEIGHT
  82.     ),
  83.     2: Region(
  84.         UNIT - 9 + PADDING_BETWEEN_SLOTS + SLOT_WIDTH,
  85.         SLOTS_Y_COORD,
  86.         SLOT_WIDTH, SLOT_HEIGHT
  87.     ),
  88.     3: Region(
  89.         UNIT - 9 + PADDING_BETWEEN_SLOTS * 2 + SLOT_WIDTH * 2,
  90.         SLOTS_Y_COORD,
  91.         SLOT_WIDTH, SLOT_HEIGHT
  92.     ),
  93.     4: Region(
  94.         UNIT - 9 + PADDING_BETWEEN_SLOTS * 3 + SLOT_WIDTH * 3,
  95.         SLOTS_Y_COORD,
  96.         SLOT_WIDTH, SLOT_HEIGHT
  97.     ),
  98. }
  99.  
  100.  
  101.  
  102. class MatchController:
  103.     """Starts/stops matches and triggers skillcasts during matches"""
  104.  
  105.     def __init__(self, label):
  106.         self.label = label  # For display purposes
  107.         self.heroes = []
  108.  
  109.  
  110.     def register_hero(self, hero):
  111.         if len(self.heroes) > 4:
  112.             raise ValueError('cannot register more than 4 heroes')
  113.         self.heroes.append(hero)
  114.  
  115.  
  116.     def start(self):
  117.         if not self.heroes:
  118.             self.report('No heroes registered. Failed to start.')
  119.             return
  120.  
  121.         if len(self.heroes) < 4:
  122.             self.report('WARNING: Starting with <4 heroes registered. This '
  123.                         'will probably not work well.')
  124.  
  125.         self.report('Starting MatchController ' + self.label)
  126.  
  127.         # Wait for heros face to appear
  128.         hero = self.heroes[0]
  129.         self.report('Waiting for {} to appear in {} face subregion...'.format(
  130.             hero.facepng, hero))
  131.         # Use the face subregion to check if the heros face appears
  132.         # FOREVER means script will not continue unless it shows up
  133.         hero.subregions['face'].wait(hero.facepng, FOREVER)
  134.         self.report('  Found, starting clicks.')
  135.        
  136.         # Continuously fetch next skill and try to click
  137.         while True:
  138.             try:
  139.                 for hero in self.heroes:
  140.                     skillnum = hero.get_current_skillnum()
  141.                     if hero.is_on_cooldown(skillnum):
  142.                         # If previous skill on CD, means cast successfully
  143.                         # Only rotate and get new skillnum if successful cast
  144.                         skillnum = hero.get_new_skillnum()
  145.                         m = "Detected cooldown, now trying to cast s{}"
  146.                         self.report(m.format(skillnum))
  147.                     hero.click_skill(skillnum)
  148.                      
  149.             except StopIteration:
  150.                 self.report('Stopping macro.')
  151.                 break
  152.            
  153.             except Exception as e:
  154.                 self.report('Unexpected error occurred.')
  155.                 raise e
  156.  
  157.  
  158.     def report(self, msg):
  159.         """Convenience method to show which Hero is producing the message."""
  160.         print('[MatchController-{}] {}'.format(self.label, msg))
  161.  
  162.  
  163.     def __repr__(self):
  164.         """Define output when object is printed"""
  165.         return "<MatchController '{}'>".format(self.label)
  166.  
  167.  
  168.  
  169. class Hero:
  170.     """The interface between MatchController and Sikuli."""
  171.     def __init__(self, heroname, slotnum, facepng, opener=[], rotation=None):
  172.         if slotnum not in SLOTS_REGION:
  173.             raise ValueError('slotnum must be 1, 2, 3 or 4')
  174.  
  175.         self.name = heroname
  176.         self.region = SLOTS_REGION[slotnum]#######sub s
  177.         self.slotnum = slotnum
  178.         self.facepng = facepng
  179.  
  180.         # Extract some data from the region to store as shortcuts
  181.         self.topleft = self.region.getTopLeft()
  182.         self.botleft = self.region.getBottomLeft()
  183.  
  184.         # Define subregions
  185.         self.subregions = {
  186.             'skill1': Region(
  187.                 self.botleft.x,
  188.                 self.botleft.y - SKILL_HEIGHT,
  189.                 SKILL_WIDTH, SKILL_HEIGHT
  190.             ),
  191.             'skill2': Region(
  192.                 self.botleft.x + 8 + SKILL_WIDTH,
  193.                 self.botleft.y - SKILL_HEIGHT,
  194.                 SKILL_WIDTH, SKILL_HEIGHT
  195.             ),
  196.             'skill3': Region(
  197.                 self.botleft.x + 15 + SKILL_WIDTH * 2,
  198.                 self.botleft.y - SKILL_HEIGHT,
  199.                 SKILL_WIDTH, SKILL_HEIGHT
  200.             ),
  201.             'face': Region(1,1,1,1)
  202.         }
  203.  
  204.         # Typecheck and store opener and rotation args
  205.         self.set_opener(opener)
  206.         self.set_rotation(rotation)
  207.         self.report('Initialized to slot {}'.format(self.slotnum))
  208.  
  209.  
  210.     def assert_skillnum_list(self, skill_num_list):
  211.         """Helper function to typecheck lists of skillnums"""
  212.         try:
  213.             assert list(filter(lambda i: 3 >= int(i) >= 1, skill_num_list))
  214.         except (AssertionError, ValueError):
  215.             # catches non-list-like stuff, empty lists and bad list elements
  216.             msg = 'must provide a list containing 1, 2 or 3 only'
  217.             raise ValueError(msg)
  218.  
  219.  
  220.     def set_opener(self, skill_num_list=[]):
  221.         if skill_num_list != []:
  222.             self.assert_skillnum_list(skill_num_list)
  223.         # Since we will be popping off values from the given list of numbers,
  224.         # we store a copy of the list as ._opener and store the original list
  225.         # as .opener
  226.         self.opener = skill_num_list
  227.         self._opener = [i for i in self.opener]
  228.  
  229.  
  230.     def set_rotation(self, skill_num_list):
  231.         skill_num_list = skill_num_list or [1, 2, 3]
  232.         self.assert_skillnum_list(skill_num_list)
  233.         # Since we want to reorder the numbers in the given list later,
  234.         # we store a copy of the list as ._opener and store the original list
  235.         # as .opener
  236.         self.rotation = skill_num_list
  237.         self._rotation = [i for i in self.rotation]
  238.  
  239.    
  240.     def face_is_visible(self):
  241.         """Shorthand func for checking if this heros face is still visible"""
  242.         facereg = self.subregions['face']
  243.         face = self.facepng
  244.         return facereg.exists(face, 1)  # 1 sec timeout
  245.  
  246.  
  247.     def click_skill(self, skillnum):
  248.         self.report('Clicking skill {}.'.format(skillnum))
  249.         # Fetch and click region
  250.         skillreg = self.subregions['skill'+str(skillnum)]
  251.         skillreg.highlight(1) #############################
  252.         click(skillreg)
  253.  
  254.  
  255.     def get_current_skillnum(self):
  256.         """Finds next skill to cast either from opener or from rotation.
  257.  
  258.        Raises StopIteration if face not found.
  259.        Raises RuntimeError if theres no skills to cast for any reason
  260.        """
  261.         skillnum = 0
  262.         if self._opener:
  263.             skillnum = self._opener[0]
  264.         else:
  265.             # itertools.cycle.__next__() doesn't work due to Sikuli's py2
  266.             # interpreter. Instead, cycle by popping initial value and
  267.             # appending to end of list
  268.             skillnum = self._rotation[0]
  269.        
  270.         if not skillnum:
  271.             raise RuntimeError('Unexpectedly ran out of skills to cast')
  272.        
  273.         if not self.face_is_visible():
  274.             self.report('Face no longer visible, sending stop signal!')
  275.             raise StopIteration
  276.        
  277.         return skillnum
  278.  
  279.  
  280.     def get_new_skillnum(self):
  281.         if self._opener:
  282.             self._opener.pop(0)
  283.         else:
  284.             self._rotation.pop(0)
  285.             self._rotation.append(skillnum)
  286.         return self.get_current_skillnum()
  287.  
  288.     def is_on_cooldown(self, skillnum):
  289.         reg = self.subregions['skill' + str(skillnum)]
  290.         return reg.exists(COOLDOWN_PNG, 0)
  291.  
  292.  
  293.     def report(self, msg):
  294.         """Convenience method to show which Hero is producing the message."""
  295.         n = '[Hero-{}]'.format(self.name)
  296.         print('{:>20} {}'.format(n, msg))
  297.  
  298.  
  299.     def __repr__(self):
  300.         """Define output when object is printed"""
  301.         return "<Hero {} @slot{}>".format(self.name, self.slotnum)
  302.  
  303.    
  304. if __name__ == '__main__':
  305.     # Create a MatchController
  306.     controller = MatchController('LoV')
  307.  
  308.     # Assign each slot to a hero
  309.     for slotnum, reg in SLOTS_REGION.items():
  310.         for heroname, herodata in HERO_SETTINGS.items():
  311.             print(reg)
  312.             #reg.highlight(2)
  313.             if reg.exists(herodata['facepng'], 0):
  314.                 reg.highlight(0.1)
  315.                 print('test2')
  316.                 # Use the ** 'splat' operator to unpack herodata as args:
  317.                 h = Hero(heroname, slotnum, **herodata)
  318.                 controller.register_hero(h)
  319.                 break
  320.  
  321.     controller.start()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement