diff --git a/alas.py b/alas.py index a6ce89ba6..b71fa27ee 100644 --- a/alas.py +++ b/alas.py @@ -434,6 +434,10 @@ class AzurLaneAutoScript: from module.storage.box_disassemble import StorageBox StorageBox(config=self.config, device=self.device, task="BoxDisassemble").run() + def island_pearl(self): + from module.island.pearl_daemon import IslandPearl + IslandPearl(config=self.config, device=self.device, task="IslandPearl").run() + def azur_lane_uncensored(self): from module.daemon.uncensored import AzurLaneUncensored AzurLaneUncensored(config=self.config, device=self.device, task="AzurLaneUncensored").run() diff --git a/assets/cn/island/ISLAND_FRIEND.png b/assets/cn/island/ISLAND_FRIEND.png new file mode 100644 index 000000000..c820c720d Binary files /dev/null and b/assets/cn/island/ISLAND_FRIEND.png differ diff --git a/assets/cn/island/ISLAND_FRIEND_CHECK.png b/assets/cn/island/ISLAND_FRIEND_CHECK.png new file mode 100644 index 000000000..83eca57db Binary files /dev/null and b/assets/cn/island/ISLAND_FRIEND_CHECK.png differ diff --git a/assets/cn/island/ISLAND_FRIEND_LEAVE.png b/assets/cn/island/ISLAND_FRIEND_LEAVE.png new file mode 100644 index 000000000..dba54f65b Binary files /dev/null and b/assets/cn/island/ISLAND_FRIEND_LEAVE.png differ diff --git a/assets/cn/island/ISLAND_FRIEND_SCROLL.png b/assets/cn/island/ISLAND_FRIEND_SCROLL.png new file mode 100644 index 000000000..781cfc799 Binary files /dev/null and b/assets/cn/island/ISLAND_FRIEND_SCROLL.png differ diff --git a/assets/cn/island/PEARL_CHECK.png b/assets/cn/island/PEARL_CHECK.png new file mode 100644 index 000000000..346b3407f Binary files /dev/null and b/assets/cn/island/PEARL_CHECK.png differ diff --git a/assets/cn/island/PEARL_ENTER.png b/assets/cn/island/PEARL_ENTER.png new file mode 100644 index 000000000..995aad162 Binary files /dev/null and b/assets/cn/island/PEARL_ENTER.png differ diff --git a/assets/cn/island/PEARL_PRICE_OCR.png b/assets/cn/island/PEARL_PRICE_OCR.png new file mode 100644 index 000000000..8de1ea57a Binary files /dev/null and b/assets/cn/island/PEARL_PRICE_OCR.png differ diff --git a/assets/cn/island/TEMPLATE_FRIEND_VISIT.png b/assets/cn/island/TEMPLATE_FRIEND_VISIT.png new file mode 100644 index 000000000..a5e6c1913 Binary files /dev/null and b/assets/cn/island/TEMPLATE_FRIEND_VISIT.png differ diff --git a/config/template.json b/config/template.json index b0458d7b1..e490ef384 100644 --- a/config/template.json +++ b/config/template.json @@ -2254,6 +2254,11 @@ "Storage": {} } }, + "IslandPearl": { + "Storage": { + "Storage": {} + } + }, "Benchmark": { "Benchmark": { "DeviceType": "emulator", diff --git a/module/config/argument/args.json b/module/config/argument/args.json index 5c8c28933..c8791715f 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -11718,6 +11718,16 @@ } } }, + "IslandPearl": { + "Storage": { + "Storage": { + "type": "storage", + "value": {}, + "valuetype": "ignore", + "display": "disabled" + } + } + }, "Benchmark": { "Benchmark": { "DeviceType": { diff --git a/module/config/argument/menu.json b/module/config/argument/menu.json index b248becd4..b6f7a9c4b 100644 --- a/module/config/argument/menu.json +++ b/module/config/argument/menu.json @@ -105,6 +105,7 @@ "OpsiDaemon", "EventStory", "BoxDisassemble", + "IslandPearl", "Benchmark", "AzurLaneUncensored", "GameManager" diff --git a/module/config/argument/task.yaml b/module/config/argument/task.yaml index 25c3ee8f5..92c76dd76 100644 --- a/module/config/argument/task.yaml +++ b/module/config/argument/task.yaml @@ -360,6 +360,8 @@ Tool: - EventStory BoxDisassemble: - BoxDisassemble + IslandPearl: + - IslandPearl Benchmark: - Benchmark AzurLaneUncensored: diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index fae9be518..d7da10608 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -274,6 +274,10 @@ "name": "Box Disassemble", "help": "" }, + "IslandPearl": { + "name": "Task.IslandPearl.name", + "help": "Task.IslandPearl.help" + }, "Benchmark": { "name": "Performance Test", "help": "" diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index 8d0fa1b0d..fdcfa4485 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -274,6 +274,10 @@ "name": "Task.BoxDisassemble.name", "help": "Task.BoxDisassemble.help" }, + "IslandPearl": { + "name": "Task.IslandPearl.name", + "help": "Task.IslandPearl.help" + }, "Benchmark": { "name": "機能テスト", "help": "" diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index c038382d1..1350a92c4 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -274,6 +274,10 @@ "name": "拆装备箱", "help": "" }, + "IslandPearl": { + "name": "岛屿珍珠", + "help": "" + }, "Benchmark": { "name": "性能测试", "help": "" diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index c094f79c8..90eb67d4b 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -274,6 +274,10 @@ "name": "拆裝備箱", "help": "" }, + "IslandPearl": { + "name": "Task.IslandPearl.name", + "help": "Task.IslandPearl.help" + }, "Benchmark": { "name": "性能測試", "help": "" diff --git a/module/device/control.py b/module/device/control.py index 1224c4547..a2a650bbe 100644 --- a/module/device/control.py +++ b/module/device/control.py @@ -85,10 +85,11 @@ class Control(Hermit, Minitouch, Scrcpy, MaaTouch, NemuIpc): else: self.swipe_adb((x, y), (x, y), duration) - def swipe(self, p1, p2, duration=(0.1, 0.2), name='SWIPE', distance_check=True): + def swipe(self, p1, p2, duration=(0.1, 0.2), hold_time=0, name='SWIPE', distance_check=True): self.handle_control_check(name) p1, p2 = ensure_int(p1, p2) duration = ensure_time(duration) + hold_time = ensure_time(hold_time) * 1000 method = self.config.Emulator_ControlMethod if method == 'uiautomator2': logger.info('Swipe %s -> %s, %s' % (point2str(*p1), point2str(*p2), duration)) @@ -107,7 +108,7 @@ class Control(Hermit, Minitouch, Scrcpy, MaaTouch, NemuIpc): return if method == 'minitouch': - self.swipe_minitouch(p1, p2) + self.swipe_minitouch(p1, p2, hold_time=hold_time) elif method == 'uiautomator2': self.swipe_uiautomator2(p1, p2, duration=duration) elif method == 'scrcpy': diff --git a/module/device/method/minitouch.py b/module/device/method/minitouch.py index d97c2ca88..ef726edf9 100644 --- a/module/device/method/minitouch.py +++ b/module/device/method/minitouch.py @@ -679,7 +679,7 @@ class Minitouch(Connection): builder.send() @retry - def swipe_minitouch(self, p1, p2): + def swipe_minitouch(self, p1, p2, hold_time): points = insert_swipe(p0=p1, p3=p2) builder = self.minitouch_builder @@ -689,6 +689,9 @@ class Minitouch(Connection): for point in points[1:]: builder.move(*point).commit().wait(10) builder.send() + + if hold_time > 0: + builder.wait(hold_time) builder.up().commit() builder.send() diff --git a/module/island/assets.py b/module/island/assets.py index 63329a674..71aea6d88 100644 --- a/module/island/assets.py +++ b/module/island/assets.py @@ -7,6 +7,10 @@ from module.base.template import Template GET_ITEMS_ISLAND = Button(area={'cn': (588, 260, 692, 289), 'en': (588, 260, 692, 289), 'jp': (588, 260, 692, 289), 'tw': (588, 260, 692, 289)}, color={'cn': (178, 180, 180), 'en': (149, 151, 152), 'jp': (178, 180, 180), 'tw': (178, 180, 180)}, button={'cn': (0, 263, 129, 555), 'en': (588, 260, 692, 289), 'jp': (0, 263, 129, 555), 'tw': (0, 263, 129, 555)}, file={'cn': './assets/cn/island/GET_ITEMS_ISLAND.png', 'en': './assets/en/island/GET_ITEMS_ISLAND.png', 'jp': './assets/cn/island/GET_ITEMS_ISLAND.png', 'tw': './assets/cn/island/GET_ITEMS_ISLAND.png'}) ISLAND_AMOUNT_MAX = Button(area={'cn': (960, 382, 988, 405), 'en': (960, 382, 988, 405), 'jp': (960, 382, 988, 405), 'tw': (960, 382, 988, 405)}, color={'cn': (72, 72, 78), 'en': (78, 78, 84), 'jp': (72, 72, 78), 'tw': (72, 72, 78)}, button={'cn': (960, 382, 988, 405), 'en': (960, 382, 988, 405), 'jp': (960, 382, 988, 405), 'tw': (960, 382, 988, 405)}, file={'cn': './assets/cn/island/ISLAND_AMOUNT_MAX.png', 'en': './assets/en/island/ISLAND_AMOUNT_MAX.png', 'jp': './assets/cn/island/ISLAND_AMOUNT_MAX.png', 'tw': './assets/cn/island/ISLAND_AMOUNT_MAX.png'}) ISLAND_CLICK_SAFE_AREA = Button(area={'cn': (0, 263, 129, 555), 'en': (0, 263, 129, 555), 'jp': (0, 263, 129, 555), 'tw': (0, 263, 129, 555)}, color={'cn': (50, 52, 56), 'en': (50, 52, 56), 'jp': (50, 52, 56), 'tw': (50, 52, 56)}, button={'cn': (0, 263, 129, 555), 'en': (0, 263, 129, 555), 'jp': (0, 263, 129, 555), 'tw': (0, 263, 129, 555)}, file={'cn': './assets/cn/island/ISLAND_CLICK_SAFE_AREA.png', 'en': './assets/cn/island/ISLAND_CLICK_SAFE_AREA.png', 'jp': './assets/cn/island/ISLAND_CLICK_SAFE_AREA.png', 'tw': './assets/cn/island/ISLAND_CLICK_SAFE_AREA.png'}) +ISLAND_FRIEND = Button(area={'cn': (887, 648, 920, 675), 'en': (887, 648, 920, 675), 'jp': (887, 648, 920, 675), 'tw': (887, 648, 920, 675)}, color={'cn': (203, 213, 219), 'en': (203, 213, 219), 'jp': (203, 213, 219), 'tw': (203, 213, 219)}, button={'cn': (887, 648, 920, 675), 'en': (887, 648, 920, 675), 'jp': (887, 648, 920, 675), 'tw': (887, 648, 920, 675)}, file={'cn': './assets/cn/island/ISLAND_FRIEND.png', 'en': './assets/cn/island/ISLAND_FRIEND.png', 'jp': './assets/cn/island/ISLAND_FRIEND.png', 'tw': './assets/cn/island/ISLAND_FRIEND.png'}) +ISLAND_FRIEND_CHECK = Button(area={'cn': (171, 22, 217, 45), 'en': (171, 22, 217, 45), 'jp': (171, 22, 217, 45), 'tw': (171, 22, 217, 45)}, color={'cn': (96, 102, 115), 'en': (96, 102, 115), 'jp': (96, 102, 115), 'tw': (96, 102, 115)}, button={'cn': (171, 22, 217, 45), 'en': (171, 22, 217, 45), 'jp': (171, 22, 217, 45), 'tw': (171, 22, 217, 45)}, file={'cn': './assets/cn/island/ISLAND_FRIEND_CHECK.png', 'en': './assets/cn/island/ISLAND_FRIEND_CHECK.png', 'jp': './assets/cn/island/ISLAND_FRIEND_CHECK.png', 'tw': './assets/cn/island/ISLAND_FRIEND_CHECK.png'}) +ISLAND_FRIEND_LEAVE = Button(area={'cn': (380, 47, 406, 71), 'en': (380, 47, 406, 71), 'jp': (380, 47, 406, 71), 'tw': (380, 47, 406, 71)}, color={'cn': (183, 183, 183), 'en': (183, 183, 183), 'jp': (183, 183, 183), 'tw': (183, 183, 183)}, button={'cn': (380, 47, 406, 71), 'en': (380, 47, 406, 71), 'jp': (380, 47, 406, 71), 'tw': (380, 47, 406, 71)}, file={'cn': './assets/cn/island/ISLAND_FRIEND_LEAVE.png', 'en': './assets/cn/island/ISLAND_FRIEND_LEAVE.png', 'jp': './assets/cn/island/ISLAND_FRIEND_LEAVE.png', 'tw': './assets/cn/island/ISLAND_FRIEND_LEAVE.png'}) +ISLAND_FRIEND_SCROLL = Button(area={'cn': (1256, 98, 1257, 694), 'en': (1256, 98, 1257, 694), 'jp': (1256, 98, 1257, 694), 'tw': (1256, 98, 1257, 694)}, color={'cn': (200, 199, 193), 'en': (200, 199, 193), 'jp': (200, 199, 193), 'tw': (200, 199, 193)}, button={'cn': (1256, 98, 1257, 694), 'en': (1256, 98, 1257, 694), 'jp': (1256, 98, 1257, 694), 'tw': (1256, 98, 1257, 694)}, file={'cn': './assets/cn/island/ISLAND_FRIEND_SCROLL.png', 'en': './assets/cn/island/ISLAND_FRIEND_SCROLL.png', 'jp': './assets/cn/island/ISLAND_FRIEND_SCROLL.png', 'tw': './assets/cn/island/ISLAND_FRIEND_SCROLL.png'}) ISLAND_INFO_EXIT = Button(area={'cn': (907, 160, 933, 187), 'en': (907, 160, 933, 187), 'jp': (907, 160, 933, 187), 'tw': (907, 160, 933, 187)}, color={'cn': (96, 96, 96), 'en': (96, 96, 96), 'jp': (96, 96, 96), 'tw': (96, 96, 96)}, button={'cn': (907, 160, 933, 187), 'en': (907, 160, 933, 187), 'jp': (907, 160, 933, 187), 'tw': (907, 160, 933, 187)}, file={'cn': './assets/cn/island/ISLAND_INFO_EXIT.png', 'en': './assets/cn/island/ISLAND_INFO_EXIT.png', 'jp': './assets/cn/island/ISLAND_INFO_EXIT.png', 'tw': './assets/cn/island/ISLAND_INFO_EXIT.png'}) ISLAND_MANAGEMENT = Button(area={'cn': (1077, 493, 1119, 533), 'en': (1077, 493, 1119, 533), 'jp': (1077, 493, 1119, 533), 'tw': (1077, 493, 1119, 533)}, color={'cn': (187, 224, 135), 'en': (187, 224, 135), 'jp': (187, 224, 135), 'tw': (187, 224, 135)}, button={'cn': (1077, 493, 1119, 533), 'en': (1077, 493, 1119, 533), 'jp': (1077, 493, 1119, 533), 'tw': (1077, 493, 1119, 533)}, file={'cn': './assets/cn/island/ISLAND_MANAGEMENT.png', 'en': './assets/cn/island/ISLAND_MANAGEMENT.png', 'jp': './assets/cn/island/ISLAND_MANAGEMENT.png', 'tw': './assets/cn/island/ISLAND_MANAGEMENT.png'}) ISLAND_MANAGEMENT_CHECK = Button(area={'cn': (123, 21, 304, 47), 'en': (123, 21, 304, 47), 'jp': (123, 21, 304, 47), 'tw': (123, 21, 304, 47)}, color={'cn': (77, 83, 96), 'en': (90, 96, 108), 'jp': (77, 83, 96), 'tw': (77, 83, 96)}, button={'cn': (123, 21, 304, 47), 'en': (123, 21, 304, 47), 'jp': (123, 21, 304, 47), 'tw': (123, 21, 304, 47)}, file={'cn': './assets/cn/island/ISLAND_MANAGEMENT_CHECK.png', 'en': './assets/en/island/ISLAND_MANAGEMENT_CHECK.png', 'jp': './assets/cn/island/ISLAND_MANAGEMENT_CHECK.png', 'tw': './assets/cn/island/ISLAND_MANAGEMENT_CHECK.png'}) @@ -20,6 +24,9 @@ OCR_PRODUCTION_TIME_REMAIN = Button(area={'cn': (621, 427, 666, 439), 'en': (621 OCR_TRANSPORT_REFRESH = Button(area={'cn': (716, 211, 805, 231), 'en': (716, 211, 805, 231), 'jp': (716, 211, 805, 231), 'tw': (716, 211, 805, 231)}, color={'cn': (176, 177, 175), 'en': (176, 177, 175), 'jp': (176, 177, 175), 'tw': (176, 177, 175)}, button={'cn': (716, 211, 805, 231), 'en': (716, 211, 805, 231), 'jp': (716, 211, 805, 231), 'tw': (716, 211, 805, 231)}, file={'cn': './assets/cn/island/OCR_TRANSPORT_REFRESH.png', 'en': './assets/cn/island/OCR_TRANSPORT_REFRESH.png', 'jp': './assets/cn/island/OCR_TRANSPORT_REFRESH.png', 'tw': './assets/cn/island/OCR_TRANSPORT_REFRESH.png'}) OCR_TRANSPORT_TIME = Button(area={'cn': (611, 139, 687, 154), 'en': (611, 139, 687, 154), 'jp': (611, 139, 687, 154), 'tw': (611, 139, 687, 154)}, color={'cn': (119, 119, 120), 'en': (119, 119, 120), 'jp': (119, 119, 120), 'tw': (119, 119, 120)}, button={'cn': (611, 139, 687, 154), 'en': (611, 139, 687, 154), 'jp': (611, 139, 687, 154), 'tw': (611, 139, 687, 154)}, file={'cn': './assets/cn/island/OCR_TRANSPORT_TIME.png', 'en': './assets/cn/island/OCR_TRANSPORT_TIME.png', 'jp': './assets/cn/island/OCR_TRANSPORT_TIME.png', 'tw': './assets/cn/island/OCR_TRANSPORT_TIME.png'}) OCR_TRANSPORT_TIME_REMAIN = Button(area={'cn': (753, 210, 842, 230), 'en': (753, 210, 842, 230), 'jp': (753, 210, 842, 230), 'tw': (753, 210, 842, 230)}, color={'cn': (252, 203, 127), 'en': (252, 203, 127), 'jp': (252, 203, 127), 'tw': (252, 203, 127)}, button={'cn': (753, 210, 842, 230), 'en': (753, 210, 842, 230), 'jp': (753, 210, 842, 230), 'tw': (753, 210, 842, 230)}, file={'cn': './assets/cn/island/OCR_TRANSPORT_TIME_REMAIN.png', 'en': './assets/cn/island/OCR_TRANSPORT_TIME_REMAIN.png', 'jp': './assets/cn/island/OCR_TRANSPORT_TIME_REMAIN.png', 'tw': './assets/cn/island/OCR_TRANSPORT_TIME_REMAIN.png'}) +PEARL_CHECK = Button(area={'cn': (113, 22, 228, 45), 'en': (113, 22, 228, 45), 'jp': (113, 22, 228, 45), 'tw': (113, 22, 228, 45)}, color={'cn': (93, 98, 110), 'en': (93, 98, 110), 'jp': (93, 98, 110), 'tw': (93, 98, 110)}, button={'cn': (113, 22, 228, 45), 'en': (113, 22, 228, 45), 'jp': (113, 22, 228, 45), 'tw': (113, 22, 228, 45)}, file={'cn': './assets/cn/island/PEARL_CHECK.png', 'en': './assets/cn/island/PEARL_CHECK.png', 'jp': './assets/cn/island/PEARL_CHECK.png', 'tw': './assets/cn/island/PEARL_CHECK.png'}) +PEARL_ENTER = Button(area={'cn': (809, 380, 836, 407), 'en': (809, 380, 836, 407), 'jp': (809, 380, 836, 407), 'tw': (809, 380, 836, 407)}, color={'cn': (192, 192, 192), 'en': (192, 192, 192), 'jp': (192, 192, 192), 'tw': (192, 192, 192)}, button={'cn': (809, 380, 836, 407), 'en': (809, 380, 836, 407), 'jp': (809, 380, 836, 407), 'tw': (809, 380, 836, 407)}, file={'cn': './assets/cn/island/PEARL_ENTER.png', 'en': './assets/cn/island/PEARL_ENTER.png', 'jp': './assets/cn/island/PEARL_ENTER.png', 'tw': './assets/cn/island/PEARL_ENTER.png'}) +PEARL_PRICE_OCR = Button(area={'cn': (257, 357, 300, 377), 'en': (257, 357, 300, 377), 'jp': (257, 357, 300, 377), 'tw': (257, 357, 300, 377)}, color={'cn': (147, 148, 148), 'en': (147, 148, 148), 'jp': (147, 148, 148), 'tw': (147, 148, 148)}, button={'cn': (257, 357, 300, 377), 'en': (257, 357, 300, 377), 'jp': (257, 357, 300, 377), 'tw': (257, 357, 300, 377)}, file={'cn': './assets/cn/island/PEARL_PRICE_OCR.png', 'en': './assets/cn/island/PEARL_PRICE_OCR.png', 'jp': './assets/cn/island/PEARL_PRICE_OCR.png', 'tw': './assets/cn/island/PEARL_PRICE_OCR.png'}) PRODUCT_MANJUU_CHECK = Button(area={'cn': (535, 107, 575, 138), 'en': (535, 107, 575, 138), 'jp': (535, 107, 575, 138), 'tw': (535, 107, 575, 138)}, color={'cn': (199, 181, 124), 'en': (199, 181, 124), 'jp': (199, 181, 124), 'tw': (199, 181, 124)}, button={'cn': (535, 107, 575, 138), 'en': (535, 107, 575, 138), 'jp': (535, 107, 575, 138), 'tw': (535, 107, 575, 138)}, file={'cn': './assets/cn/island/PRODUCT_MANJUU_CHECK.png', 'en': './assets/cn/island/PRODUCT_MANJUU_CHECK.png', 'jp': './assets/cn/island/PRODUCT_MANJUU_CHECK.png', 'tw': './assets/cn/island/PRODUCT_MANJUU_CHECK.png'}) PROJECT_AKASHI_CHECK = Button(area={'cn': (1157, 176, 1217, 209), 'en': (1157, 176, 1217, 209), 'jp': (1157, 176, 1217, 209), 'tw': (1157, 176, 1217, 209)}, color={'cn': (173, 183, 168), 'en': (173, 183, 168), 'jp': (173, 183, 168), 'tw': (173, 183, 168)}, button={'cn': (1157, 176, 1217, 209), 'en': (1157, 176, 1217, 209), 'jp': (1157, 176, 1217, 209), 'tw': (1157, 176, 1217, 209)}, file={'cn': './assets/cn/island/PROJECT_AKASHI_CHECK.png', 'en': './assets/cn/island/PROJECT_AKASHI_CHECK.png', 'jp': './assets/cn/island/PROJECT_AKASHI_CHECK.png', 'tw': './assets/cn/island/PROJECT_AKASHI_CHECK.png'}) PROJECT_AMAGI_CHAN_CHECK = Button(area={'cn': (1152, 167, 1229, 215), 'en': (1152, 167, 1229, 215), 'jp': (1152, 167, 1229, 215), 'tw': (1152, 167, 1229, 215)}, color={'cn': (136, 114, 106), 'en': (136, 114, 106), 'jp': (136, 114, 106), 'tw': (136, 114, 106)}, button={'cn': (1152, 167, 1229, 215), 'en': (1152, 167, 1229, 215), 'jp': (1152, 167, 1229, 215), 'tw': (1152, 167, 1229, 215)}, file={'cn': './assets/cn/island/PROJECT_AMAGI_CHAN_CHECK.png', 'en': './assets/cn/island/PROJECT_AMAGI_CHAN_CHECK.png', 'jp': './assets/cn/island/PROJECT_AMAGI_CHAN_CHECK.png', 'tw': './assets/cn/island/PROJECT_AMAGI_CHAN_CHECK.png'}) @@ -57,6 +64,7 @@ TEMPLATE_COTTON = Template(file={'cn': './assets/cn/island/TEMPLATE_COTTON.png', TEMPLATE_FLEX = Template(file={'cn': './assets/cn/island/TEMPLATE_FLEX.png', 'en': './assets/cn/island/TEMPLATE_FLEX.png', 'jp': './assets/cn/island/TEMPLATE_FLEX.png', 'tw': './assets/cn/island/TEMPLATE_FLEX.png'}) TEMPLATE_FRESH_MEAT = Template(file={'cn': './assets/cn/island/TEMPLATE_FRESH_MEAT.png', 'en': './assets/cn/island/TEMPLATE_FRESH_MEAT.png', 'jp': './assets/cn/island/TEMPLATE_FRESH_MEAT.png', 'tw': './assets/cn/island/TEMPLATE_FRESH_MEAT.png'}) TEMPLATE_FRIEDRICH_DER_GROBE = Template(file={'cn': './assets/cn/island/TEMPLATE_FRIEDRICH_DER_GROBE.png', 'en': './assets/cn/island/TEMPLATE_FRIEDRICH_DER_GROBE.png', 'jp': './assets/cn/island/TEMPLATE_FRIEDRICH_DER_GROBE.png', 'tw': './assets/cn/island/TEMPLATE_FRIEDRICH_DER_GROBE.png'}) +TEMPLATE_FRIEND_VISIT = Template(file={'cn': './assets/cn/island/TEMPLATE_FRIEND_VISIT.png', 'en': './assets/cn/island/TEMPLATE_FRIEND_VISIT.png', 'jp': './assets/cn/island/TEMPLATE_FRIEND_VISIT.png', 'tw': './assets/cn/island/TEMPLATE_FRIEND_VISIT.png'}) TEMPLATE_HELENA = Template(file={'cn': './assets/cn/island/TEMPLATE_HELENA.png', 'en': './assets/cn/island/TEMPLATE_HELENA.png', 'jp': './assets/cn/island/TEMPLATE_HELENA.png', 'tw': './assets/cn/island/TEMPLATE_HELENA.png'}) TEMPLATE_ITEM_SATISFIED = Template(file={'cn': './assets/cn/island/TEMPLATE_ITEM_SATISFIED.png', 'en': './assets/cn/island/TEMPLATE_ITEM_SATISFIED.png', 'jp': './assets/cn/island/TEMPLATE_ITEM_SATISFIED.png', 'tw': './assets/cn/island/TEMPLATE_ITEM_SATISFIED.png'}) TEMPLATE_LEMALIN = Template(file={'cn': './assets/cn/island/TEMPLATE_LEMALIN.png', 'en': './assets/cn/island/TEMPLATE_LEMALIN.png', 'jp': './assets/cn/island/TEMPLATE_LEMALIN.png', 'tw': './assets/cn/island/TEMPLATE_LEMALIN.png'}) diff --git a/module/island/pearl_daemon.py b/module/island/pearl_daemon.py new file mode 100644 index 000000000..d83c0c0d4 --- /dev/null +++ b/module/island/pearl_daemon.py @@ -0,0 +1,277 @@ +from rich.table import Table +from rich.text import Text + +from module.base.timer import Timer +from module.island.assets import * +from module.island.ui import IslandUI +from module.logger import logger +from module.map.map_grids import SelectedGrids +from module.ocr.ocr import Digit, Ocr +from module.ui.page import page_island, page_island_phone +from module.ui.scroll import Scroll + + +ISLAND_FRIEND_SCROLL = Scroll(ISLAND_FRIEND_SCROLL, color=(255, 255, 255)) +ISLAND_FRIEND_SCROLL.drag_threshold = 0.05 +ISLAND_FRIEND_SCROLL.edge_threshold = 0.05 + +PEARL_PRICE_OCR = Digit(PEARL_PRICE_OCR, letter=(255, 255, 255), threshold=128) + + +class FriendNameOcr(Ocr): + def after_process(self, result): + result = super().after_process(result) + result = result.replace('/', '').replace('\\', '').replace('`', '').replace('_', '') + return result + + +class IslandFriend: + # If success to parse project + valid: bool + # button to visit + visit_button: Button + # OCR result + name: str + # if visited + visited: bool + # pearl price on this island + pearl_price: int + + def __init__(self, image, visit_button, crop_area): + """ + Args: + image: + visit_button: + crop_area: + """ + self.image = image + self.visit_button = visit_button.move((crop_area[0], crop_area[1])) + self.crop_area = crop_area + self.x1, self.y1, self.x2, self.y2 = self.visit_button.area + self.valid = True + self.visited = False + self.pearl_price = 0 + self.friend_parse() + + def friend_parse(self): + area = (self.x1 - 504, self.y1 - 25, self.x1 - 504 + 168, self.y1 - 25 + 22) + + if area[0] < 360 or area[1] < 98 or area[2] > 593 or area[3] > 693: + self.valid = False + return + + button = Button(area=area, color=(), button=area, name='FRIEND_NAME') + ocr = FriendNameOcr(button, lang='cnocr', letter=(63, 64, 66), threshold=128) + self.name = ocr.ocr(self.image) + if not self.name: + self.valid = False + return + + def __eq__(self, other): + """ + Args: + other (IslandFriend): + + Returns: + bool: + """ + if not isinstance(other, IslandFriend): + return False + if not self.valid or not other.valid: + return False + if self.name != other.name: + return False + + return True + + def __str__(self): + return self.name + + +class IslandPearl(IslandUI): + def pearl_enter(self): + """ + Pages: + in: ISLAND_FRIEND_LEAVE + out: PEARL_CHECK + """ + logger.hr('Pearl Enter') + self.move_up(2.8) + self.move_right(1.6) + self.move_down(1.8) + + for _ in self.loop(): + if self.appear_then_click(PEARL_ENTER, offset=(20, 20), interval=2): + continue + if self.appear(PEARL_CHECK, offset=(20, 20)): + break + + def pearl_price_get(self): + """ + Returns: + int: pearl price ocr result + """ + ocr = 0 + for _ in self.loop(timeout=1.5): + ocr = PEARL_PRICE_OCR.ocr(self.device.image) + if ocr >= 200: + break + + return ocr + + def friend_detect(self): + """ + Get all friends from an image. + + Args: + image (np.ndarray): + + Returns: + SelectedGrids: + """ + self.handle_info_bar() + area = (880, 98, 960, 693) + friends = SelectedGrids( + [IslandFriend(self.device.image, button, area) for button in + TEMPLATE_FRIEND_VISIT.match_multi(self.image_crop(area, copy=False))] + ) + return friends.select(valid=True) + + def _friend_visit(self, friend): + """ + Args: + friend (IslandFriend): + + Returns: + bool: if visited + """ + logger.info(f'Visiting {friend}') + confirm_timer = Timer(1, count=2).start() + for _ in self.loop(): + if self.island_in_friend(interval=5): + self.device.click(friend.visit_button) + continue + + if self.info_bar_count(): + return False + + if self.appear(ISLAND_FRIEND_LEAVE, offset=(20, 20)): + if confirm_timer.reached(): + break + continue + else: + confirm_timer.reset() + return True + + def friend_leave(self): + logger.hr('Friend Leave') + self.island_ui_back() + for _ in self.loop(): + if self.appear_then_click(ISLAND_FRIEND_LEAVE, offset=(20, 20), interval=2): + continue + + if self.ui_page_appear(page_island): + break + if self.ui_page_appear(page_island_phone): + break + self.device.sleep(1.5) + self.device.click_record_clear() + self.ui_ensure_friend_page() + # ISLAND_FRIEND_SCROLL.set_top(main=self) + + def friend_visit(self, current_friends, friends): + """ + Visit a friend's island, get the pearl price then leave + + Args: + current_friends (SelectedGrids): + friends (SelectedGrids): + + Returns: + SelectedGrids: + """ + logger.hr('Friend Visit') + friend: IslandFriend = friends.intersect_by_eq(current_friends).select(visited=False).first_or_none() + if friend is None or friend.name not in current_friends.get('name'): + return friends + + if self._friend_visit(friend): + self.device.sleep(2) + self.pearl_enter() + friend.pearl_price = self.pearl_price_get() + print(friend.name) + self.friend_leave() + friend.visited = True + return friends + + def pearl_run(self): + """ + Visit each friend's island, get the pearl price, and show in the logger + + Returns: + SelectedGrids: + """ + logger.hr('Island Pearl', level=1) + bottom = False + friends = SelectedGrids([]) + if ISLAND_FRIEND_SCROLL.appear(main=self): + ISLAND_FRIEND_SCROLL.set_top(main=self) + count = 0 + for _ in self.loop(): + if count > 2: + break + current_friends = self.friend_detect() + friends = friends.add_by_eq(current_friends) + friends = self.friend_visit(current_friends, friends) + current_friends = friends.intersect_by_eq(current_friends) + + if ISLAND_FRIEND_SCROLL.appear(main=self): + if ISLAND_FRIEND_SCROLL.at_bottom(main=self): + if not bottom: + ISLAND_FRIEND_SCROLL.drag_threshold = 0.01 + ISLAND_FRIEND_SCROLL.edge_threshold = 0.01 + bottom = True + continue + logger.info('Island friend reach bottom, stop') + break + elif not current_friends.select(visited=False): + ISLAND_FRIEND_SCROLL.next_page(main=self) + count += 1 + + return friends + + @staticmethod + def show(data): + """ + +----------+---------------+ + | Player | Pearl Price | + +----------+---------------+ + | a | 200 | + | b | 300 | + | c | 400 | + +----------+---------------+ + """ + table = Table(show_lines=True) + table.add_column( + 'Player', header_style="bright_cyan", style="cyan", no_wrap=True + ) + table.add_column("Pearl Price", style="magenta") + for row in zip(data.get('name'), data.get('pearl_price')): + table.add_row( + row[0], + str(row[1]), + ) + logger.print(table, justify='center') + + def run(self): + self.device.screenshot() + self.ui_ensure_friend_page() + friends = self.pearl_run() + friends = friends.select(visited=True) + self.show(friends) + + +if __name__ == '__main__': + self = IslandPearl('alas') + self.device.screenshot() + self.run() diff --git a/module/island/ui.py b/module/island/ui.py index 08117267d..17b79e4f2 100644 --- a/module/island/ui.py +++ b/module/island/ui.py @@ -28,6 +28,16 @@ class IslandUI(UI): """ return self.match_template_color(ISLAND_TRANSPORT_CHECK, offset=(20, 20), interval=interval) + def island_in_friend(self, interval=0): + """ + Args: + interval (int): + + Returns: + bool: if in page ISLAND_FRIEND_CHECK + """ + return self.appear(ISLAND_FRIEND_CHECK, offset=(20, 20), interval=interval) + def island_management_enter(self): """ Enter island management page. @@ -73,6 +83,27 @@ class IslandUI(UI): ) return True + def island_friend_enter(self): + """ + Enter island friend page. + + Returns: + bool: if success + + Pages: + in: page_island_phone + out: ISLAND_FRIEND_CHECK + """ + logger.info('Island friend enter') + self.ui_click( + click_button=ISLAND_FRIEND, + check_button=self.island_in_friend, + offset=(20, 20), + retry_wait=2, + skip_first_screenshot=True + ) + return True + def island_ui_back(self): """ Pages: @@ -82,7 +113,7 @@ class IslandUI(UI): logger.info('Island UI back') self.ui_click( click_button=SHOP_BACK_ARROW, - check_button=page_island_phone.check_button, + check_button=[page_island_phone.check_button, ISLAND_FRIEND_LEAVE], offset=(20, 20), retry_wait=2, skip_first_screenshot=True @@ -111,6 +142,17 @@ class IslandUI(UI): if self.appear_then_click(ISLAND_MANAGEMENT, offset=(20, 20), interval=2): continue + def ui_ensure_friend_page(self): + """ + Pages: + in: Any + out: ISLAND_FRIEND_CHECK + """ + logger.info('UI ensure friend page') + if not self.island_in_friend(): + self.ui_ensure(page_island_phone) + self.island_friend_enter() + def handle_get_items(self): if self.appear_then_click(GET_ITEMS_ISLAND, offset=(20, 20), interval=2): return True @@ -138,3 +180,23 @@ class IslandUI(UI): return True return super().ui_additional(get_ship=False) + + def move_up(self, hold_time=0): + p1 = (217, 507) + p2 = (217, 507 - 36) + self.device.swipe(p1, p2, hold_time=hold_time) + + def move_down(self, hold_time=0): + p1 = (217, 507) + p2 = (217, 507 + 36) + self.device.swipe(p1, p2, hold_time=hold_time) + + def move_left(self, hold_time=0): + p1 = (217, 507) + p2 = (217 - 36, 507) + self.device.swipe(p1, p2, hold_time=hold_time) + + def move_right(self, hold_time=0): + p1 = (217, 507) + p2 = (217 + 36, 507) + self.device.swipe(p1, p2, hold_time=hold_time) diff --git a/module/submodule/utils.py b/module/submodule/utils.py index cbe65139c..d7a87d916 100644 --- a/module/submodule/utils.py +++ b/module/submodule/utils.py @@ -19,6 +19,7 @@ def get_available_func(): 'OpsiDaemon', 'EventStory', 'BoxDisassemble', + 'IslandPearl', 'AzurLaneUncensored', 'Benchmark', 'GameManager',