diff --git a/.gitignore b/.gitignore index 566de72..6478001 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,6 @@ m4/lt~obsolete.m4 autom4te.cache /.idea +xp_bot_ac.py +qp_bot.py +fgobot/device-wg.py diff --git a/README.md b/README.md index 514cc75..4cb04d4 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,9 @@ fgo自动脚本 在命令行中输入`adb`,出现帮助信息则说明安装成功。 +如果你的python在wsl下运行,但模拟器在windows下运行,请使用windows下的adb(而不是wsl下的),否则很容易莫名断开 +即在device.py中将`self.adb_path = adb_path`改为`self.adb_path = /mnt/c/adb/adb.exe`的形式 + ### 安装本体 下载这个项目,在根目录下运行 @@ -25,21 +28,19 @@ python3 setup.py install ``` ## 使用前的准备 -- 推荐使用模拟器进行游戏,并调整分辨率为`1280x720`。**使用真机可能导致分辨率和连接等问题。** +- 推荐使用模拟器进行游戏,并调整分辨率为`1280x720`。**使用真机可能导致分辨率和连接等问题,使用其他分辨率可能导致图片识别缓慢的性能问题。** +- 如果使用`1280x720`分辨率的等比缩放分辨率(例如`1920x1080`),可以修改my_bot.py中的相应参数,但除了`1280x720`和`1920x1080`分辨率以外,需要自行制作截图 - 构筑所用的队伍。由于当前版本不支持获取敌方信息,建议选择稳定的宝具速刷队。 -- 在设置中关闭“灵基再临第四阶段展示”选项,可以减少助战识别的工作量 -- 在助战界面选择好想要的职介,确保(在线的)好友可以大概率刷出期望的助战。 +- 在设置中关闭“灵基再临第四阶段展示”选项,可以减少助战识别的工作量。 +- 在助战界面选择好想要的职阶,确保(在线的)好友可以大概率刷出期望的助战。 - 手动进行一场对战。(非必须,用于选中队伍以及调整相关设置) - 在战斗菜单中关闭“技能使用确认”和“宝具匀速”,打开“缩短敌人消失时间。 - 在选择指令卡界面调整游戏速度为2倍速。 -- 在设备/模拟器上对要打的副本和期望的助战截图,放在脚本的同级目录下,可以参考`/qp.png`和`/friend_qp.png`。 - ![quest](https://github.com/will7101/fgo-bot/blob/master/qp.png?raw=true) - - ![friend](https://github.com/will7101/fgo-bot/blob/master/friend_qp.png?raw=true) +- 在设备/模拟器上对要打的副本和期望的助战截图,放在脚本的`userimages`目录下,可以参考`userimages/qp.png`和`userimages/friend_qp.png`。 注意:尽量截取更多的信息,但不要超出可点击的范围。 -- 将游戏置于进入任务前的界面。 +- 将游戏置于进入任务前的界面,或者战斗中的界面,但开始运行程序时,战斗必须处于停顿、等待玩家输入的状态(即可以看到“攻击”按钮的状态)。 ## 使用教程 @@ -67,6 +68,17 @@ python3 setup.py install 选取指令卡并攻击。 -`cards`需要为有三个整数作为元素的`list`,按照顺序表示出的卡。其中`1~5`表示从左往右的常规卡,`6~8`表示从左往右的宝具卡。 +`cards`需要为有零到四个整数作为元素的`list`,按照顺序表示出的卡。其中`1~5`表示从左往右的常规卡,`6~8`表示从左往右的宝具卡。 + +例如`[6, 1, 2]`表示先使用从者1的宝具卡,再使用指令卡1和指令卡2。 + +## 更新内容(byWG) +- 更新到国服最新截图(2019年12月) +- 支持多种分辨率 +- 支持从任务选择界面、战斗界面、战斗结算界面开始运行程序 +- 战斗脚本支持根据不同回合采用不同的出卡(因而支持放完宝具之后自动平砍) +- 支持随机出卡,且防止因AP不足放不出宝具而卡住,每次都会选择4张卡 +- 缩短等待时间 +- 如脚本中的技能正在冷却而无法释放,也能继续运行下去 +- 其他微小调整 -例如`[6, 1, 2]`表示先使用从者1的宝具卡,再使用指令卡1和指令卡2。 \ No newline at end of file diff --git a/fgobot/bot.py b/fgobot/bot.py index 61b7d86..3c08106 100644 --- a/fgobot/bot.py +++ b/fgobot/bot.py @@ -3,6 +3,7 @@ """ import logging +import random from functools import partial from typing import Dict, Any, Callable from .tm import TM @@ -11,13 +12,12 @@ from pathlib import Path from time import sleep from typing import Tuple, List, Union -from random import randint logger = logging.getLogger('bot') -INTERVAL_SHORT = 1 -INTERVAL_MID = 2 -INTERVAL_LONG = 10 +INTERVAL_SHORT = 0.5 +INTERVAL_MID = 1.0 +INTERVAL_LONG = 10.0 class BattleBot: @@ -28,10 +28,11 @@ class BattleBot: def __init__(self, quest: str = 'quest.png', friend: Union[str, List[str]] = 'friend.png', - stage_count = 3, + stage_count: int = 3, ap: List[str] = None, quest_threshold: float = 0.97, - friend_threshold: float = 0.97 + friend_threshold: float = 0.97, + zoom_width: int = 720 ): """ @@ -60,8 +61,11 @@ def __init__(self, # Device self.device = Device() + self.zoom_ratio = float(zoom_width/720) + # Template matcher - self.tm = TM(feed=partial(self.device.capture, method=Device.FROM_SHELL)) + self.tm = TM(feed=partial( + self.device.capture, method=Device.FROM_SHELL), zoom_ratio=self.zoom_ratio) # Target quest path = Path(quest).absolute() @@ -89,6 +93,7 @@ def __init__(self, btn_path = Path(__file__).absolute().parent / 'config' / 'buttons.json' with open(btn_path) as f: self.buttons = json.load(f) + self.__zoom(self.buttons) logger.debug('Bot initialized.') @@ -99,10 +104,20 @@ def __add_stage_handler(self, stage: int, f: Callable): :param stage: the stage number :param f: the handler function """ - assert not self.stage_handlers.get(stage), 'Cannot register multiple function to a single stage.' - logger.debug('Function {} registered to stage {}'.format(f.__name__, stage)) + assert not self.stage_handlers.get( + stage), 'Cannot register multiple function to a single stage.' + logger.debug( + 'Function {} registered to stage {}'.format(f.__name__, stage)) self.stage_handlers[stage] = f + def __zoom(self, b): + for key, value in b.items(): + if isinstance(value, dict): + for k, v in value.items(): + b[key][k] = int(v*self.zoom_ratio) + else: + b[key] = int(value*self.zoom_ratio) + def __button(self, btn): """ Return the __button coords and size. @@ -120,8 +135,7 @@ def __swipe(self, track): :param track: :return: """ - x1, y1, x2, y2 = map(lambda x: x + randint(-5, 5), self.buttons['swipe'][track]) - self.device.swipe((x1, y1), (x2, y2)) + self.device.swipe_rand(**self.buttons[track]) def __find_and_tap(self, im: str, threshold: float = None) -> bool: """ @@ -147,7 +161,7 @@ def __exists(self, im: str, threshold: float = None) -> bool: """ return self.tm.exists(im, threshold=threshold) - def __wait(self, sec): + def __wait(self, sec: float): """ Wait some seconds and update the screen feed. @@ -164,7 +178,7 @@ def __wait_until(self, im: str): logger.debug("Wait until image '{}' appears.".format(im)) self.tm.update_screen() while not self.__exists(im): - self.__wait(INTERVAL_MID) + self.__wait(INTERVAL_SHORT) def __get_current_stage(self) -> int: """ @@ -189,12 +203,12 @@ def __get_current_stage(self) -> int: def __find_friend(self) -> str: self.__wait_until('refresh_friends') - for _ in range(6): + for _ in range(9): for fid in range(self.friend_count): im = 'f_{}'.format(fid) if self.__exists(im, threshold=self.friend_threshold): return im - self.__swipe('friend') + self.__swipe('swipe_friend') self.__wait(INTERVAL_SHORT) return '' @@ -206,7 +220,7 @@ def __enter_battle(self) -> bool: """ self.__wait_until('menu') while not self.__find_and_tap('quest', threshold=self.quest_threshold): - self.__swipe('quest') + self.__swipe('swipe_quest') self.__wait(INTERVAL_SHORT) self.__wait(INTERVAL_MID) @@ -248,23 +262,30 @@ def __play_battle(self) -> int: :return: count of rounds. """ + all_rounds = 0 rounds = 0 + last_stage = 0 while True: stage = self.__get_current_stage() if stage == -1: logger.error("Failed to get current stage. Leaving battle...") - return rounds - - rounds += 1 - logger.info('At stage {}/{}, round {}, calling handler function...' + return all_rounds + if stage == last_stage: + rounds += 1 + else: + last_stage = stage + all_rounds = all_rounds+rounds + rounds = 1 + logger.info('At stage {}/{}, {} rounds for this stage,calling handler function...' .format(stage, self.stage_count, rounds)) - self.stage_handlers[stage]() + self.stage_handlers[stage](rounds) while True: self.__wait(INTERVAL_MID) - if self.__exists('bond') or self.__exists('bond_up'): - logger.info("'与从者的羁绊' detected. Leaving battle...") - return rounds + if self.__exists('bond'): + all_rounds = all_rounds+rounds + logger.info("'Bond' detected. Leaving battle...") + return all_rounds elif self.__exists('attack'): logger.info("'Attack' detected. Continuing loop...") break @@ -283,16 +304,20 @@ def __end_battle(self): self.__find_and_tap('next_step') self.__wait(INTERVAL_MID) - # quest first-complete reward - if self.__exists('please_tap'): - self.__find_and_tap('please_tap') - self.__wait(INTERVAL_SHORT) + while not self.__exists('menu'): + # not send friend application + if self.__exists('not_apply'): + self.__find_and_tap('not_apply') + self.__wait(INTERVAL_SHORT) - # not send friend application - if self.__exists('not_apply'): - self.__find_and_tap('not_apply') + # quest first-complete reward + if self.__exists('please_tap'): + self.__find_and_tap('please_tap') + self.__wait(INTERVAL_SHORT) - self.__wait_until('menu') + self.__wait(INTERVAL_SHORT) + + # self.__wait_until('menu') def at_stage(self, stage: int): """ @@ -332,8 +357,14 @@ def use_skill(self, servant: int, skill: int, obj=None): x += self.buttons['choose_object_distance'] * (obj - 1) self.device.tap_rand(x, y, w, h) logger.debug('Chose skill object {}.'.format(obj)) + elif self.__exists('skill'): + self.__wait(INTERVAL_SHORT) + if self.__exists('skill'): + logger.error( + 'Skill ({}, {}) not ready.'.format(servant, skill)) + self.device.tap_rand(x, y, w, h) - self.__wait(INTERVAL_SHORT * 2) + #self.__wait(INTERVAL_SHORT * 2) def use_master_skill(self, skill: int, obj=None, obj2=None): """ @@ -377,13 +408,20 @@ def use_master_skill(self, skill: int, obj=None, obj2=None): x += self.buttons['change_distance'] * (obj2 - obj) self.device.tap_rand(x, y, w, h) - logger.debug('Chose master skill object ({}, {}).'.format(obj, obj2)) + logger.debug( + 'Chose master skill object ({}, {}).'.format(obj, obj2)) self.__wait(INTERVAL_SHORT) self.__find_and_tap('change') logger.debug('Order Change') else: logger.error('Invalid master skill object.') + elif self.__exists('skill'): + self.__wait(INTERVAL_SHORT) + if self.__exists('skill'): + logger.error('Master skill {} not ready.'.format(skill)) + x, y, w, h = self.__button('master_skill_menu') + self.device.tap_rand(x, y, w, h) self.__wait(INTERVAL_SHORT) @@ -396,12 +434,17 @@ def attack(self, cards: list): :param cards: the cards id, as a list """ - assert len(cards) == 3, 'Number of cards must be 3.' - assert len(set(cards)) == 3, 'Cards must be distinct.' + while len(cards) < 4: + x = random.randrange(1, 6) + if x in cards: + continue + cards.append(x) + assert len(cards) == 4, 'Number of cards must be 4.' + assert len(set(cards)) == 4, 'Cards must be distinct.' self.__wait_until('attack') self.__find_and_tap('attack') - self.__wait(INTERVAL_SHORT * 2) + self.__wait(INTERVAL_MID) for card in cards: if 1 <= card <= 5: x, y, w, h = self.__button('card') @@ -421,14 +464,24 @@ def run(self, max_loops: int = 10): :param max_loops: the max number of loops. """ count = 0 - for n_loop in range(max_loops): - logger.info('Entering battle...') - if not self.__enter_battle(): - logger.info('AP runs out. Quiting...') - break - rounds = self.__play_battle() - self.__end_battle() - count += 1 - logger.info('{}-th Battle complete. {} rounds played.'.format(count, rounds)) - - logger.info('{} Battles played in total. Good bye!'.format(count)) + rounds = 0 + for n_loop in range(1, max_loops+1): + self.__wait(INTERVAL_MID) + if self.__exists('menu'): + logger.info('Entering battle...') + if not self.__enter_battle(): + logger.info('AP runs out. Quiting...') + break + rounds = self.__play_battle() + self.__end_battle() + elif self.__exists('attack'): + logger.info('Starting battle halfway...') + rounds = self.__play_battle() + self.__end_battle() + elif self.__exists('next_step'): + logger.info('Looks like a finished battle...') + self.__end_battle() + logger.info( + '{}-th Battle complete. {} rounds played.'.format(n_loop, rounds)) + + logger.info('{} Battles played in total. Good bye!'.format(n_loop)) diff --git a/fgobot/config/buttons.json b/fgobot/config/buttons.json index 56075b6..17e7c7a 100644 --- a/fgobot/config/buttons.json +++ b/fgobot/config/buttons.json @@ -39,28 +39,36 @@ "h": 50 }, "choose_object_distance": 318, - "card":{ + "card": { "x": 77, "y": 433, "w": 100, "h": 100 }, - "noble_card":{ + "noble_card": { "x": 363, "y": 142, "w": 100, "h": 100 }, "card_distance": 256, - "change":{ - "x":87, - "y":300, - "w":100, - "h":100 + "change": { + "x": 87, + "y": 300, + "w": 100, + "h": 100 }, "change_distance": 200, - "swipe": { - "friend": [600, 602, 600, 400], - "quest": [960, 588, 960, 400] + "swipe_friend": { + "x1": 600, + "y1": 602, + "x2": 600, + "y2": 400 + }, + "swipe_quest": { + "x1": 960, + "y1": 588, + "x2": 960, + "y2": 400 } } \ No newline at end of file diff --git a/fgobot/device.py b/fgobot/device.py index 75bcb95..c5adfae 100644 --- a/fgobot/device.py +++ b/fgobot/device.py @@ -16,7 +16,7 @@ class Device: A class of the android device controller that provides interface such as screenshots and clicking. """ - def __init__(self, timeout: int = 15, adb_path: str = 'adb'): + def __init__(self, timeout: int = 30, adb_path: str = 'adb'): """ :param timeout: the timeout of executing commands. @@ -100,7 +100,8 @@ def get_size(self) -> bool: for line in output: if line.startswith('Physical size'): self.size = tuple(map(int, re.findall(r'\d+', line))) - self.logger.info('Got screen size {:d} x {:d}'.format(self.size[0], self.size[1])) + self.logger.info('Got screen size {:d} x {:d}'.format( + self.size[0], self.size[1])) return True self.logger.error('Failed to get screen size') self.logger.error('Error message: {}'.format('\n'.join(output))) @@ -119,7 +120,8 @@ def tap(self, x: int, y: int) -> bool: for line in output: if line.startswith('error'): self.logger.error('Failed to tap at {}'.format(coords)) - self.logger.error('Error message: {}'.format('\n'.join(output))) + self.logger.error( + 'Error message: {}'.format('\n'.join(output))) return False self.logger.debug('Tapped at {}'.format(coords)) return True @@ -137,7 +139,7 @@ def tap_rand(self, x: int, y: int, w: int, h: int) -> bool: y = randint(y, y + h - 1) return self.tap(x, y) - def swipe(self, pos0: Tuple[int, int], pos1: Tuple[int, int], duration: int = 500) -> bool: + def swipe_rand(self, x1: int, y1: int, x2: int, y2: int, duration: int = 500) -> bool: """ Input a swipe event from `pos0` to `pos1`, taking `duration` milliseconds. @@ -146,15 +148,20 @@ def swipe(self, pos0: Tuple[int, int], pos1: Tuple[int, int], duration: int = 50 :param duration: the time (in milliseconds) the swipe will take. :return: whether the event is successful. """ - coords0 = '{:d} {:d}'.format(pos0[0], pos0[1]) - coords1 = '{:d} {:d}'.format(pos1[0], pos1[1]) - output = self.__run_cmd(['shell', 'input swipe {} {} {:d}'.format(coords0, coords1, duration)]) + x1, y1, x2, y2 = map(lambda x: x + randint(-5, 5), (x1, y1, x2, y2)) + coords0 = '{:d} {:d}'.format(x1, y1) + coords1 = '{:d} {:d}'.format(x2, y2) + output = self.__run_cmd( + ['shell', 'input swipe {} {} {:d}'.format(coords0, coords1, duration)]) for line in output: if line.startswith('error'): - self.logger.error('Failed to swipe from {} to {} taking {:d}ms'.format(coords0, coords1, duration)) - self.logger.error('Error message: {}'.format('\n'.join(output))) + self.logger.error('Failed to swipe from {} to {} taking {:d}ms'.format( + coords0, coords1, duration)) + self.logger.error( + 'Error message: {}'.format('\n'.join(output))) return False - self.logger.debug('Swiped from {} to {} taking {:d}ms'.format(coords0, coords1, duration)) + self.logger.debug('Swiped from {} to {} taking {:d}ms'.format( + coords0, coords1, duration)) return True # methods of capturing the screen. @@ -173,7 +180,8 @@ def __png_sanitize(s: bytes) -> bytes: pos1 = s.find(b'\x1a') pos2 = s.find(b'\n', pos1) pattern = s[pos1 + 1:pos2 + 1] - logging.getLogger('device').debug("Pattern detected: '{}'".format(pattern)) + logging.getLogger('device').debug( + "Pattern detected: '{}'".format(pattern)) return re.sub(pattern, b'\n', s) def capture(self, method=FROM_SHELL) -> Union[np.ndarray, None]: diff --git a/fgobot/images/1080p/1_3.png b/fgobot/images/1080p/1_3.png new file mode 100644 index 0000000..ee50b52 Binary files /dev/null and b/fgobot/images/1080p/1_3.png differ diff --git a/fgobot/images/1080p/2_3.png b/fgobot/images/1080p/2_3.png new file mode 100644 index 0000000..faa0eca Binary files /dev/null and b/fgobot/images/1080p/2_3.png differ diff --git a/fgobot/images/1080p/3_3.png b/fgobot/images/1080p/3_3.png new file mode 100644 index 0000000..a1c4529 Binary files /dev/null and b/fgobot/images/1080p/3_3.png differ diff --git a/fgobot/images/1080p/ap_regen.png b/fgobot/images/1080p/ap_regen.png new file mode 100644 index 0000000..a2ea732 Binary files /dev/null and b/fgobot/images/1080p/ap_regen.png differ diff --git a/fgobot/images/1080p/attack.png b/fgobot/images/1080p/attack.png new file mode 100644 index 0000000..61d37bd Binary files /dev/null and b/fgobot/images/1080p/attack.png differ diff --git a/fgobot/images/1080p/bond.png b/fgobot/images/1080p/bond.png new file mode 100644 index 0000000..4f28cc8 Binary files /dev/null and b/fgobot/images/1080p/bond.png differ diff --git a/fgobot/images/1080p/change.png b/fgobot/images/1080p/change.png new file mode 100644 index 0000000..c33ad28 Binary files /dev/null and b/fgobot/images/1080p/change.png differ diff --git a/fgobot/images/1080p/change_disabled.png b/fgobot/images/1080p/change_disabled.png new file mode 100644 index 0000000..a7dc945 Binary files /dev/null and b/fgobot/images/1080p/change_disabled.png differ diff --git a/fgobot/images/1080p/choose_object.png b/fgobot/images/1080p/choose_object.png new file mode 100644 index 0000000..1a6152e Binary files /dev/null and b/fgobot/images/1080p/choose_object.png differ diff --git a/fgobot/images/1080p/decide.png b/fgobot/images/1080p/decide.png new file mode 100644 index 0000000..b790207 Binary files /dev/null and b/fgobot/images/1080p/decide.png differ diff --git a/fgobot/images/1080p/friend_pick.png b/fgobot/images/1080p/friend_pick.png new file mode 100644 index 0000000..23aa881 Binary files /dev/null and b/fgobot/images/1080p/friend_pick.png differ diff --git a/fgobot/images/1080p/gold_apple.png b/fgobot/images/1080p/gold_apple.png new file mode 100644 index 0000000..3fa0cf1 Binary files /dev/null and b/fgobot/images/1080p/gold_apple.png differ diff --git a/fgobot/images/1080p/menu.png b/fgobot/images/1080p/menu.png new file mode 100644 index 0000000..106ef6a Binary files /dev/null and b/fgobot/images/1080p/menu.png differ diff --git a/fgobot/images/1080p/next_step.png b/fgobot/images/1080p/next_step.png new file mode 100644 index 0000000..5fb0018 Binary files /dev/null and b/fgobot/images/1080p/next_step.png differ diff --git a/fgobot/images/1080p/no.png b/fgobot/images/1080p/no.png new file mode 100644 index 0000000..3591a6a Binary files /dev/null and b/fgobot/images/1080p/no.png differ diff --git a/fgobot/images/1080p/not_apply.png b/fgobot/images/1080p/not_apply.png new file mode 100644 index 0000000..8267288 Binary files /dev/null and b/fgobot/images/1080p/not_apply.png differ diff --git a/fgobot/images/1080p/please_tap.png b/fgobot/images/1080p/please_tap.png new file mode 100644 index 0000000..3828561 Binary files /dev/null and b/fgobot/images/1080p/please_tap.png differ diff --git a/fgobot/images/1080p/refresh_friends.png b/fgobot/images/1080p/refresh_friends.png new file mode 100644 index 0000000..90f6066 Binary files /dev/null and b/fgobot/images/1080p/refresh_friends.png differ diff --git a/fgobot/images/1080p/silver_apple.png b/fgobot/images/1080p/silver_apple.png new file mode 100644 index 0000000..d56a973 Binary files /dev/null and b/fgobot/images/1080p/silver_apple.png differ diff --git a/fgobot/images/1080p/skill.png b/fgobot/images/1080p/skill.png new file mode 100644 index 0000000..bef27f2 Binary files /dev/null and b/fgobot/images/1080p/skill.png differ diff --git a/fgobot/images/1080p/start_quest.png b/fgobot/images/1080p/start_quest.png new file mode 100644 index 0000000..5f94510 Binary files /dev/null and b/fgobot/images/1080p/start_quest.png differ diff --git a/fgobot/images/1080p/yes.png b/fgobot/images/1080p/yes.png new file mode 100644 index 0000000..1c68adf Binary files /dev/null and b/fgobot/images/1080p/yes.png differ diff --git a/fgobot/images/1_3.png b/fgobot/images/720p/1_3.png similarity index 100% rename from fgobot/images/1_3.png rename to fgobot/images/720p/1_3.png diff --git a/fgobot/images/2_3.png b/fgobot/images/720p/2_3.png similarity index 100% rename from fgobot/images/2_3.png rename to fgobot/images/720p/2_3.png diff --git a/fgobot/images/3_3.png b/fgobot/images/720p/3_3.png similarity index 100% rename from fgobot/images/3_3.png rename to fgobot/images/720p/3_3.png diff --git a/fgobot/images/720p/ap_regen.png b/fgobot/images/720p/ap_regen.png new file mode 100644 index 0000000..ddbe60e Binary files /dev/null and b/fgobot/images/720p/ap_regen.png differ diff --git a/fgobot/images/attack.png b/fgobot/images/720p/attack.png similarity index 100% rename from fgobot/images/attack.png rename to fgobot/images/720p/attack.png diff --git a/fgobot/images/720p/bond.png b/fgobot/images/720p/bond.png new file mode 100644 index 0000000..df7959d Binary files /dev/null and b/fgobot/images/720p/bond.png differ diff --git a/fgobot/images/720p/change.png b/fgobot/images/720p/change.png new file mode 100644 index 0000000..5a4d591 Binary files /dev/null and b/fgobot/images/720p/change.png differ diff --git a/fgobot/images/720p/change_disabled.png b/fgobot/images/720p/change_disabled.png new file mode 100644 index 0000000..da5aab4 Binary files /dev/null and b/fgobot/images/720p/change_disabled.png differ diff --git a/fgobot/images/720p/choose_object.png b/fgobot/images/720p/choose_object.png new file mode 100644 index 0000000..cc3eba6 Binary files /dev/null and b/fgobot/images/720p/choose_object.png differ diff --git a/fgobot/images/720p/decide.png b/fgobot/images/720p/decide.png new file mode 100644 index 0000000..e1f1a24 Binary files /dev/null and b/fgobot/images/720p/decide.png differ diff --git a/fgobot/images/720p/friend_pick.png b/fgobot/images/720p/friend_pick.png new file mode 100644 index 0000000..8526a7b Binary files /dev/null and b/fgobot/images/720p/friend_pick.png differ diff --git a/fgobot/images/gain_exp.png b/fgobot/images/720p/gain_exp.png similarity index 100% rename from fgobot/images/gain_exp.png rename to fgobot/images/720p/gain_exp.png diff --git a/fgobot/images/720p/gold_apple.png b/fgobot/images/720p/gold_apple.png new file mode 100644 index 0000000..2608780 Binary files /dev/null and b/fgobot/images/720p/gold_apple.png differ diff --git a/fgobot/images/720p/menu.png b/fgobot/images/720p/menu.png new file mode 100644 index 0000000..86b6a9f Binary files /dev/null and b/fgobot/images/720p/menu.png differ diff --git a/fgobot/images/720p/next_step.png b/fgobot/images/720p/next_step.png new file mode 100644 index 0000000..e37a646 Binary files /dev/null and b/fgobot/images/720p/next_step.png differ diff --git a/fgobot/images/no.png b/fgobot/images/720p/no.png similarity index 100% rename from fgobot/images/no.png rename to fgobot/images/720p/no.png diff --git a/fgobot/images/720p/not_apply.png b/fgobot/images/720p/not_apply.png new file mode 100644 index 0000000..14636e4 Binary files /dev/null and b/fgobot/images/720p/not_apply.png differ diff --git a/fgobot/images/720p/please_tap.png b/fgobot/images/720p/please_tap.png new file mode 100644 index 0000000..073d3a5 Binary files /dev/null and b/fgobot/images/720p/please_tap.png differ diff --git a/fgobot/images/720p/refresh_friends.png b/fgobot/images/720p/refresh_friends.png new file mode 100644 index 0000000..617e30c Binary files /dev/null and b/fgobot/images/720p/refresh_friends.png differ diff --git a/fgobot/images/720p/silver_apple.png b/fgobot/images/720p/silver_apple.png new file mode 100644 index 0000000..eea804f Binary files /dev/null and b/fgobot/images/720p/silver_apple.png differ diff --git a/fgobot/images/720p/skill.png b/fgobot/images/720p/skill.png new file mode 100644 index 0000000..a744ab9 Binary files /dev/null and b/fgobot/images/720p/skill.png differ diff --git a/fgobot/images/720p/start_quest.png b/fgobot/images/720p/start_quest.png new file mode 100644 index 0000000..668b75b Binary files /dev/null and b/fgobot/images/720p/start_quest.png differ diff --git a/fgobot/images/yes.png b/fgobot/images/720p/yes.png similarity index 100% rename from fgobot/images/yes.png rename to fgobot/images/720p/yes.png diff --git a/fgobot/images/bond.png b/fgobot/images/bond.png deleted file mode 100644 index b306a93..0000000 Binary files a/fgobot/images/bond.png and /dev/null differ diff --git a/fgobot/images/choose_object.png b/fgobot/images/choose_object.png deleted file mode 100644 index 93b5151..0000000 Binary files a/fgobot/images/choose_object.png and /dev/null differ diff --git a/fgobot/images/close.png b/fgobot/images/close.png deleted file mode 100644 index 7b450ef..0000000 Binary files a/fgobot/images/close.png and /dev/null differ diff --git a/fgobot/images/friend_caster.png b/fgobot/images/friend_caster.png deleted file mode 100644 index 4c8f827..0000000 Binary files a/fgobot/images/friend_caster.png and /dev/null differ diff --git a/fgobot/images/friend_pick.png b/fgobot/images/friend_pick.png deleted file mode 100644 index a0c5f5c..0000000 Binary files a/fgobot/images/friend_pick.png and /dev/null differ diff --git a/fgobot/images/menu.png b/fgobot/images/menu.png deleted file mode 100644 index ae4bc4a..0000000 Binary files a/fgobot/images/menu.png and /dev/null differ diff --git a/fgobot/images/next_step.png b/fgobot/images/next_step.png deleted file mode 100644 index 653aa99..0000000 Binary files a/fgobot/images/next_step.png and /dev/null differ diff --git a/fgobot/images/not_apply.png b/fgobot/images/not_apply.png deleted file mode 100644 index 3e77704..0000000 Binary files a/fgobot/images/not_apply.png and /dev/null differ diff --git a/fgobot/images/please_tap.png b/fgobot/images/please_tap.png deleted file mode 100644 index 2d8be1c..0000000 Binary files a/fgobot/images/please_tap.png and /dev/null differ diff --git a/fgobot/images/refresh_friends.png b/fgobot/images/refresh_friends.png deleted file mode 100644 index 23f7f4a..0000000 Binary files a/fgobot/images/refresh_friends.png and /dev/null differ diff --git a/fgobot/images/start_quest.png b/fgobot/images/start_quest.png deleted file mode 100644 index a17db06..0000000 Binary files a/fgobot/images/start_quest.png and /dev/null differ diff --git a/fgobot/tm.py b/fgobot/tm.py index 6df019b..feb6154 100644 --- a/fgobot/tm.py +++ b/fgobot/tm.py @@ -16,7 +16,7 @@ class TM: - def __init__(self, feed: Callable, threshold: float = 0.85): + def __init__(self, feed: Callable, threshold: float = 0.85, zoom_ratio: float = 1.0): """ :param feed: the screencap feed function @@ -26,6 +26,7 @@ def __init__(self, feed: Callable, threshold: float = 0.85): self.feed = feed self.threshold = threshold + self.zoom_ratio = zoom_ratio # template image set self.images = {} @@ -54,7 +55,8 @@ def load_images(self): """ Load template images from directory. """ - im_dir = Path(__file__).absolute().parent / 'images' + im_dir = Path(__file__).absolute().parent / \ + 'images' / str("%dp" % int(self.zoom_ratio*720)) for im in im_dir.glob('*.png'): self.load_image(im) diff --git a/friend_qp.png b/friend_qp.png deleted file mode 100644 index 1ff3b0e..0000000 Binary files a/friend_qp.png and /dev/null differ diff --git a/my_bot.py b/my_bot.py index 6741402..de78f2c 100644 --- a/my_bot.py +++ b/my_bot.py @@ -7,21 +7,21 @@ import logging # 指定日志的输出等级(DEBUG / INFO / WARNING / ERROR) -logging.basicConfig(level=logging.DEBUG) +logging.basicConfig(level=logging.INFO) # 实例化一个bot bot = BattleBot( - # 要打的关卡截图为'qp.png',放在这个文件的同一级目录下 - quest='qp.png', + # 要打的关卡截图为'qp.png',放在这个文件(my_bot.py)的相同路径的userimages目录下 + quest='userimages/qp.png', - # 需要的助战截图为'friend_qp.png',放在这个文件的同一级目录下 + # 需要的助战截图为'friend_qp.png',放在这个文件(my_bot.py)的相同路径的userimages目录下 # 如果可以接受的助战有多个,可以传入一个list,例如:friend=['friend1.png', 'friend2.png] - friend='friend_qp.png', + friend='userimages/friend_qp.png', # AP策略为:当体力耗尽时,优先吃银苹果,再吃金苹果 # 如果不指定ap参数,则当体力耗尽时停止运行 - ap=['silver_apple', 'gold_apple'], + ap=['gold_apple', 'silver_apple'], # 要打的关卡有3面 stage_count=3, @@ -32,7 +32,12 @@ # 助战图像识别的阈值为0.95 # 如果设的过低会导致选择错误的助战,太高会导致选不到助战,请根据实际情况调整 - friend_threshold=0.95 + friend_threshold=0.95, + + # 如果你的分辨率不是720p,将你的屏幕宽度写在这里 + # 目前仅支持1280x720分辨率的等比例缩放,例如1920x1080 + # 1080p和720p以外的分辨率,请自行制作截图并放在fgo-bot/fgo-bot/images/xx(宽度)p目录下 + zoom_width=1080 ) # 为了方便,使用了简写 @@ -43,38 +48,50 @@ # 第一面的打法 @bot.at_stage(1) -def stage_1(): - # s(1, 1)表示使用1号从者的技能1 - s(1, 1) - s(2, 1) - s(3, 1) - s(3, 3) - # (a[6, 1, 2])表示出卡顺序为:6号卡(1号从者宝具卡),1号卡,2号卡 - a([6, 1, 2]) +def stage_1(rounds: int): + if rounds == 1: + # s(1, 1)表示使用1号从者的技能1 + s(2, 2) + s(2, 3) + # (a[6, 1, 2])表示出卡顺序为:6号卡(1号从者宝具卡),1号卡,2号卡 + a([7]) + # else部分表示如果该面(stage)打了超过一回合(round),则怎么打,你也可以自己写elif加入第二回合、第三回合等特定打法 + else: + a([]) # 第二面的打法 @bot.at_stage(2) -def stage_2(): - # m(2, 1)表示使用御主技能2,对象为1号从者 - m(2, 1) - a([6, 1, 2]) +def stage_2(rounds: int): + if rounds == 1: + # m(2, 1)表示使用御主技能2,对象为1号从者 + m(3, 2, 4) + s(1, 1) + s(1, 3) + s(3, 1) + s(3, 3) + a([6]) + else: + a([]) # 第三面的打法 @bot.at_stage(3) -def stage_3(): - s(1, 3) - a([6, 1, 2]) +def stage_3(rounds: int): + if rounds == 1: + s(2, 2) + a([7]) + else: + a([]) # 程序的入口点(不加这行也可以) -# 使用时,可以直接在命令行运行'python my_bot.py' +# 使用时,可以直接在命令行运行'python my_bot.py'或'python3 my_bot.py' if __name__ == '__main__': # 检查设备是否连接 if not bot.device.connected(): - # 如果没有连接,则尝试通过本地端口62001连接(具体参数请参考自己的设备/模拟器) - bot.device.connect('127.0.0.1:62001') + # 如果没有连接,则尝试通过本地端口7555连接(具体参数请参考自己的设备/模拟器) + bot.device.connect('127.0.0.1:7555') # 启动bot,最多打5次 bot.run(max_loops=5) diff --git a/qp.png b/qp.png deleted file mode 100644 index 7da3a01..0000000 Binary files a/qp.png and /dev/null differ diff --git a/userimages/friend_qp.png b/userimages/friend_qp.png new file mode 100644 index 0000000..753cad0 Binary files /dev/null and b/userimages/friend_qp.png differ diff --git a/userimages/friend_xp1.png b/userimages/friend_xp1.png new file mode 100644 index 0000000..4af470b Binary files /dev/null and b/userimages/friend_xp1.png differ diff --git a/userimages/friend_xp2.png b/userimages/friend_xp2.png new file mode 100644 index 0000000..e3d569f Binary files /dev/null and b/userimages/friend_xp2.png differ diff --git a/userimages/qp.png b/userimages/qp.png new file mode 100644 index 0000000..764ceb9 Binary files /dev/null and b/userimages/qp.png differ diff --git a/userimages/xp_ac.png b/userimages/xp_ac.png new file mode 100644 index 0000000..9934638 Binary files /dev/null and b/userimages/xp_ac.png differ diff --git a/userimages/xp_la.png b/userimages/xp_la.png new file mode 100644 index 0000000..3d032b7 Binary files /dev/null and b/userimages/xp_la.png differ diff --git a/userimages/xp_sr.png b/userimages/xp_sr.png new file mode 100644 index 0000000..dc44505 Binary files /dev/null and b/userimages/xp_sr.png differ