From 1137dfab8d3d8c8cf469e36438bd9b9abdc9f680 Mon Sep 17 00:00:00 2001 From: dragonheart107 Date: Mon, 23 Jun 2025 06:23:19 +0200 Subject: [PATCH 1/6] Fix: dropscreen for autosearch menu --- module/combat/auto_search_combat.py | 73 +++++++++++++++-------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/module/combat/auto_search_combat.py b/module/combat/auto_search_combat.py index 6d9643027e..ebd5a66e4e 100644 --- a/module/combat/auto_search_combat.py +++ b/module/combat/auto_search_combat.py @@ -175,41 +175,46 @@ def auto_search_moving(self, skip_first_screenshot=True): checked_fleet = False checked_oil = False checked_coin = False - while 1: - if skip_first_screenshot: - skip_first_screenshot = False - else: - self.device.screenshot() - - if self.is_auto_search_running(): - checked_fleet = self.auto_search_watch_fleet(checked_fleet) - if not checked_oil or not checked_coin: - checked_oil = self.auto_search_watch_oil(checked_oil) - checked_coin = self.auto_search_watch_coin(checked_coin) - if self.handle_retirement(): - self.map_offensive_auto_search() - # Map offensive ends at is_combat_loading - break - if self.handle_auto_search_map_option(): - continue - if self.handle_combat_low_emotion(): - self._auto_search_status_confirm = True - continue - if self.handle_story_skip(): - continue - if self.handle_map_cat_attack(): - continue - if self.handle_vote_popup(): - continue + with self.stat.new( + genre=self.config.campaign_name, method=self.config.DropRecord_CombatRecord + ) as drop: + while 1: + if skip_first_screenshot: + skip_first_screenshot = False + else: + self.device.screenshot() + + if self.is_auto_search_running(): + checked_fleet = self.auto_search_watch_fleet(checked_fleet) + if not checked_oil or not checked_coin: + checked_oil = self.auto_search_watch_oil(checked_oil) + checked_coin = self.auto_search_watch_coin(checked_coin) + if self.handle_retirement(): + self.map_offensive_auto_search() + # Map offensive ends at is_combat_loading + break + if self.handle_auto_search_map_option(): + continue + if self.handle_combat_low_emotion(): + self._auto_search_status_confirm = True + continue + if self.handle_story_skip(): + continue + if self.handle_map_cat_attack(): + continue + if self.handle_vote_popup(): + continue - # End - if self.is_combat_loading(): - break - if self.is_combat_executing(): - logger.info('is_combat_executing') - break - if self.is_in_auto_search_menu() or self._handle_auto_search_menu_missing(): - raise CampaignEnd + # End + if self.is_combat_loading(): + break + if self.is_combat_executing(): + logger.info('is_combat_executing') + break + if self.is_in_auto_search_menu() or self._handle_auto_search_menu_missing(): + if drop and self.is_in_auto_search_menu(): + drop.handle_add(main=self, before=4) + raise CampaignEnd def auto_search_combat_execute(self, emotion_reduce, fleet_index): """ From 0038f3ac3a7e4fe5917a1f8e2cb5b216906c7531 Mon Sep 17 00:00:00 2001 From: dragonheart107 Date: Sat, 2 Aug 2025 02:16:59 +0200 Subject: [PATCH 2/6] Merge Main fix retire assets EN replace sleep for reward screen --- assets/en/retire/DOCK_AMOUNT.png | Bin 5234 -> 4270 bytes assets/en/retire/EMPTY_ENHANCE_SLOT_PLUS.png | Bin 4811 -> 3132 bytes module/combat/auto_search_combat.py | 99 ++++++----- module/handler/assets.py | 2 +- module/private_quarters/assets.py | 4 +- module/retire/assets.py | 4 +- module/statistics/campaign_bonus.py | 167 ++++++++++++++++++- 7 files changed, 227 insertions(+), 49 deletions(-) diff --git a/assets/en/retire/DOCK_AMOUNT.png b/assets/en/retire/DOCK_AMOUNT.png index a597226aedccb991f81665491187af73d8267a50..1e3f8e4db8852962644b12070305e68c330c581c 100644 GIT binary patch delta 1669 zcmV;0273AOD6S!pBt`*qQb$4o*~u(_00004XF*Lt006O%3;baP0000WV@Og>004R> z004l5008;`004mK004C`008P>0026e000+ooVrmwks%j<5En^AK~#9!?cIBfTvZ(h z@N@6onOUXT4Fi}^D%%&4wiwx^NPGbef(Vv^CSswj5|t+k&+Zd zUOrF|kkVj`1R<#GZd=$EOk{W2vOD|iwy@jTnS1?X+NG?8Kcs4f&*#7S<(!k;bN;yb z&bjy85C8y^lL|F|fPE}71px0_2TyN$XJ6pJ)=sMo08ENa0RR910x$po00_VU001BW z0{{Sk01N;C00J-o008_4)R_VR_H^bMi@&w%vRvJ+8^kkz{o93?TzdPOTjm}4fhdYX z2-|n;IQg{2J9n0EyzbhMe&o0~ibDv)BO^;!oL4IKXIVCXIn+suPF!}*+2>zy@lSv9 zqYoduFpi=S!szzx%f9rLmwS3{zVW(a7w#&)Ha2$t1s6Tvv3ak>J;ww95P$*LH>OQH zpuc}$<>jl&O+yH^M4Hd#`}#_qon2X$g%Bl4vgbH|N6ekOVE(*IS6x}HR&)7WUvFP$ zS67y0Sr(GIBt%gzpYQD}b#`@USvE0IsjI6CA?&pX03ZMZuTzE~{IY--A~bZBUJ+vsQr;ndHZ zIXpa^rs;9VE{x-7U|?W!kcNhaC1;$zboqIeN+pC)sZ`!mUmrzLuCA^wNh+19rxq3ue|(< zmwUEO#@f#=S-fH6rhmNjQmt~s>YKiM^;LJ>eeb6~b;^@ZJ-unua{@2`AOHifzc{9) z`PsI%G|NH=XP>(wj^hx*^g|E*-Tl9)JrNy3Xl!UW^^{L;eWmy5XZ}*DOw>BYalHJ0 z+@;t2`1;yeSs{dn9)9$sMJKLUeokXU!zGuk+P-6l=>-7(`+Nr&fW3&KsHOSn*4Fmg zsaFF7gQZe`snkC(I2c7yh@x7vZKI=Kzi4G|U*Fm_w=_03){4h}{9{oRJ^9pAwZ0*Q zn^#}o+Sa~w`FU4e{rw+)?>k4#ofGYU9svMA00!WHY1Yh{(++s=^BtROA@+j{=GWKP zPieDY{=E7^p|`K^2iIIXtEp+u?Af(gyJFeWU;g?xV`Jl!EsvT%@AvE0_xAR!du)9^ zmzy!;Faa0<5P$*LFSIm&sAJ0uwdb5e2zTCo+svlJr=-@kYi^l4cTNalbaZ=v6i4}7 zE{dYrvu3t5AASD=4^D3Xh7gh@sU0G{vs_NIEQu2{3;+ng0PGizX=!e4ZLjVQu?H24 z|N2SZV)4LSUEP$e+;hJ0`E~0b9~v5V%oPA200Z!UU|~!1vu$lz_WCYv9LIG@GG+5N zPLd?4OOjp1Y10n)+*wQRy63)s+S$=>Xc@;zl1#R&NnS5TZ(Rfcc>A0J4Zu{5jSaJB z&uVRJPt#qyxa}J^-E-&Kw3^0A+}YK=bLY+wLi>h|cieVswOXyU>3+Fq@#&{++4ABG zFaCYX_HX;fP51r$j#|rHF4wblYnEm08#exI?V4((8bZkD^IcutZ&tK_A^^ZX;m>t| zcdvt|H|@0^$MO3PIi%FzKlvVSu~;nRbG5t1L}g-da45^N;(-Si3i(QmMS5dzKCj4Npu=?6vrBU*N#jPOA(62*3cmX#jr5 z5P$)IJ$VNh000017ytkc00dwF000nx0RR9%00y&>3djkQpb8ckcpLr+M;xwGHXEH9 P00000NkvXXu0mjf5=$!} delta 2688 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 Z-tIcNS3K3ZVV~ldBRzfLLmT2d{s#0g(oz5b diff --git a/module/combat/auto_search_combat.py b/module/combat/auto_search_combat.py index ebd5a66e4e..191bbd26d7 100644 --- a/module/combat/auto_search_combat.py +++ b/module/combat/auto_search_combat.py @@ -6,9 +6,10 @@ from module.handler.assets import AUTO_SEARCH_MAP_OPTION_ON from module.logger import logger from module.map.map_operation import MapOperation +from module.statistics.campaign_bonus import CampaignBonusStatistics -class AutoSearchCombat(MapOperation, Combat, CampaignStatus): +class AutoSearchCombat(MapOperation, Combat, CampaignStatus, CampaignBonusStatistics): _auto_search_in_stage_timer = Timer(3, count=6) _auto_search_status_confirm = False auto_search_oil_limit_triggered = False @@ -213,7 +214,14 @@ def auto_search_moving(self, skip_first_screenshot=True): break if self.is_in_auto_search_menu() or self._handle_auto_search_menu_missing(): if drop and self.is_in_auto_search_menu(): - drop.handle_add(main=self, before=4) + while 1: + if self.wait_until_reward_stable() is True: + drop.handle_add(main=self) + else: + logger.info('area stable failed, this should not have happened, taking fallback screenshot') + drop.handle_add(main=self) + raise CampaignEnd + raise CampaignEnd def auto_search_combat_execute(self, emotion_reduce, fleet_index): @@ -260,46 +268,57 @@ def auto_search_combat_execute(self, emotion_reduce, fleet_index): if emotion_reduce: self.emotion.reduce(fleet_index) auto = self.config.Fleet_Fleet1Mode if fleet_index == 1 else self.config.Fleet_Fleet2Mode + with self.stat.new( + genre=self.config.campaign_name, method=self.config.DropRecord_CombatRecord + ) as drop: + while 1: + self.device.screenshot() - while 1: - self.device.screenshot() - - if self.handle_submarine_call(submarine_mode): - continue - if self.handle_combat_auto(auto): - continue - if self.handle_combat_manual(auto): - continue - if auto != 'combat_auto' and self.auto_mode_checked and self.is_combat_executing(): - if self.handle_combat_weapon_release(): + if self.handle_submarine_call(submarine_mode): continue - # bunch of popup handlers - if self.handle_popup_confirm('AUTO_SEARCH_COMBAT_EXECUTE'): - continue - if self.handle_urgent_commission(): - continue - if self.handle_story_skip(): - continue - if self.handle_guild_popup_cancel(): - continue - if self.handle_vote_popup(): - continue - if self.handle_mission_popup_ack(): - continue - - # End - if self.is_in_auto_search_menu() or self._handle_auto_search_menu_missing(): - self.device.screenshot_interval_set() - raise CampaignEnd - if self.is_combat_executing(): - continue - if self.handle_get_ship(): - continue - if self.appear(BATTLE_STATUS_S) or self.appear(BATTLE_STATUS_A) or self.appear(BATTLE_STATUS_B) \ - or self.appear(EXP_INFO_S) or self.appear(EXP_INFO_A) or self.appear(EXP_INFO_B) \ - or self.is_auto_search_running(): - self.device.screenshot_interval_set() - break + if self.handle_combat_auto(auto): + continue + if self.handle_combat_manual(auto): + continue + if auto != 'combat_auto' and self.auto_mode_checked and self.is_combat_executing(): + if self.handle_combat_weapon_release(): + continue + # bunch of popup handlers + if self.handle_popup_confirm('AUTO_SEARCH_COMBAT_EXECUTE'): + continue + if self.handle_urgent_commission(): + continue + if self.handle_story_skip(): + continue + if self.handle_guild_popup_cancel(): + continue + if self.handle_vote_popup(): + continue + if self.handle_mission_popup_ack(): + continue + # needed drop here too, since it fails proper leaving if handle_get_ship triggers (even if that was false) + # End + if self.is_in_auto_search_menu() or self._handle_auto_search_menu_missing(): + self.device.screenshot_interval_set() + if drop and self.is_in_auto_search_menu(): + while 1: + if self.wait_until_reward_stable() is True: + drop.handle_add(main=self) + else: + logger.info('area stable failed, this should not have happened, taking fallback screenshot') + drop.handle_add(main=self) + raise CampaignEnd + + raise CampaignEnd + if self.is_combat_executing(): + continue + if self.handle_get_ship(): + continue + if self.appear(BATTLE_STATUS_S) or self.appear(BATTLE_STATUS_A) or self.appear(BATTLE_STATUS_B) \ + or self.appear(EXP_INFO_S) or self.appear(EXP_INFO_A) or self.appear(EXP_INFO_B) \ + or self.is_auto_search_running(): + self.device.screenshot_interval_set() + break def auto_search_combat_status(self, skip_first_screenshot=True): """ diff --git a/module/handler/assets.py b/module/handler/assets.py index 916681a032..309b409288 100644 --- a/module/handler/assets.py +++ b/module/handler/assets.py @@ -4,6 +4,7 @@ # This file was automatically generated by dev_tools/button_extract.py. # Don't modify it manually. +ANDROID_NO_RESPOND = Button(area={'cn': (341, 433, 391, 472), 'en': (341, 433, 391, 472), 'jp': (341, 433, 391, 472), 'tw': (341, 433, 391, 472)}, color={'cn': (217, 237, 235), 'en': (217, 237, 235), 'jp': (217, 237, 235), 'tw': (217, 237, 235)}, button={'cn': (341, 433, 391, 472), 'en': (341, 433, 391, 472), 'jp': (341, 433, 391, 472), 'tw': (341, 433, 391, 472)}, file={'cn': './assets/cn/handler/ANDROID_NO_RESPOND.png', 'en': './assets/en/handler/ANDROID_NO_RESPOND.png', 'jp': './assets/jp/handler/ANDROID_NO_RESPOND.png', 'tw': './assets/tw/handler/ANDROID_NO_RESPOND.png'}) AUTO_SEARCH_MAP_OPTION_OFF = Button(area={'cn': (1205, 549, 1275, 566), 'en': (1203, 552, 1277, 564), 'jp': (1204, 547, 1276, 568), 'tw': (1205, 546, 1275, 567)}, color={'cn': (196, 169, 169), 'en': (151, 132, 138), 'jp': (179, 153, 156), 'tw': (153, 132, 137)}, button={'cn': (1205, 549, 1275, 566), 'en': (1203, 552, 1277, 564), 'jp': (1204, 547, 1276, 568), 'tw': (1205, 546, 1275, 567)}, file={'cn': './assets/cn/handler/AUTO_SEARCH_MAP_OPTION_OFF.png', 'en': './assets/en/handler/AUTO_SEARCH_MAP_OPTION_OFF.png', 'jp': './assets/jp/handler/AUTO_SEARCH_MAP_OPTION_OFF.png', 'tw': './assets/tw/handler/AUTO_SEARCH_MAP_OPTION_OFF.png'}) AUTO_SEARCH_MAP_OPTION_ON = Button(area={'cn': (1205, 549, 1275, 566), 'en': (1203, 552, 1277, 564), 'jp': (1203, 547, 1276, 568), 'tw': (1204, 546, 1276, 567)}, color={'cn': (149, 176, 193), 'en': (113, 135, 157), 'jp': (132, 158, 177), 'tw': (110, 133, 156)}, button={'cn': (1205, 549, 1275, 566), 'en': (1203, 552, 1277, 564), 'jp': (1203, 547, 1276, 568), 'tw': (1204, 546, 1276, 567)}, file={'cn': './assets/cn/handler/AUTO_SEARCH_MAP_OPTION_ON.png', 'en': './assets/en/handler/AUTO_SEARCH_MAP_OPTION_ON.png', 'jp': './assets/jp/handler/AUTO_SEARCH_MAP_OPTION_ON.png', 'tw': './assets/tw/handler/AUTO_SEARCH_MAP_OPTION_ON.png'}) AUTO_SEARCH_MENU_CONTINUE = Button(area={'cn': (789, 610, 903, 638), 'en': (781, 612, 910, 634), 'jp': (859, 610, 973, 638), 'tw': (790, 610, 903, 638)}, color={'cn': (147, 182, 224), 'en': (159, 187, 224), 'jp': (139, 173, 218), 'tw': (148, 181, 222)}, button={'cn': (773, 598, 919, 646), 'en': (773, 598, 919, 647), 'jp': (845, 597, 990, 646), 'tw': (776, 601, 919, 645)}, file={'cn': './assets/cn/handler/AUTO_SEARCH_MENU_CONTINUE.png', 'en': './assets/en/handler/AUTO_SEARCH_MENU_CONTINUE.png', 'jp': './assets/jp/handler/AUTO_SEARCH_MENU_CONTINUE.png', 'tw': './assets/tw/handler/AUTO_SEARCH_MENU_CONTINUE.png'}) @@ -50,7 +51,6 @@ LOGIN_ANNOUNCE_2 = Button(area={'cn': (1193, 83, 1215, 105), 'en': (1193, 83, 1215, 105), 'jp': (1193, 83, 1215, 105), 'tw': (1193, 83, 1215, 105)}, color={'cn': (158, 170, 175), 'en': (158, 170, 175), 'jp': (158, 170, 175), 'tw': (158, 170, 175)}, button={'cn': (1171, 83, 1193, 105), 'en': (1171, 83, 1193, 105), 'jp': (1171, 83, 1193, 105), 'tw': (1171, 83, 1193, 105)}, file={'cn': './assets/cn/handler/LOGIN_ANNOUNCE_2.png', 'en': './assets/en/handler/LOGIN_ANNOUNCE_2.png', 'jp': './assets/jp/handler/LOGIN_ANNOUNCE_2.png', 'tw': './assets/tw/handler/LOGIN_ANNOUNCE_2.png'}) LOGIN_CHECK = Button(area={'cn': (1214, 653, 1268, 709), 'en': (1214, 653, 1268, 709), 'jp': (1214, 653, 1268, 709), 'tw': (1214, 653, 1268, 709)}, color={'cn': (203, 215, 230), 'en': (203, 215, 230), 'jp': (203, 215, 230), 'tw': (203, 215, 230)}, button={'cn': (416, 294, 534, 400), 'en': (1078, 591, 1168, 635), 'jp': (416, 294, 534, 400), 'tw': (416, 294, 534, 400)}, file={'cn': './assets/cn/handler/LOGIN_CHECK.png', 'en': './assets/en/handler/LOGIN_CHECK.png', 'jp': './assets/jp/handler/LOGIN_CHECK.png', 'tw': './assets/tw/handler/LOGIN_CHECK.png'}) LOGIN_GAME_UPDATE = Button(area={'cn': (700, 471, 873, 529), 'en': (726, 474, 850, 519), 'jp': (704, 475, 867, 525), 'tw': (706, 477, 866, 528)}, color={'cn': (238, 170, 78), 'en': (241, 169, 73), 'jp': (234, 167, 77), 'tw': (235, 169, 80)}, button={'cn': (700, 471, 873, 529), 'en': (726, 474, 850, 519), 'jp': (704, 475, 867, 525), 'tw': (706, 477, 866, 528)}, file={'cn': './assets/cn/handler/LOGIN_GAME_UPDATE.png', 'en': './assets/en/handler/LOGIN_GAME_UPDATE.png', 'jp': './assets/jp/handler/LOGIN_GAME_UPDATE.png', 'tw': './assets/tw/handler/LOGIN_GAME_UPDATE.png'}) -ANDROID_NO_RESPOND = Button(area={'cn': (341, 433, 391, 472), 'en': (341, 433, 391, 472), 'jp': (341, 433, 391, 472), 'tw': (341, 433, 391, 472)}, color={'cn': (217, 237, 235), 'en': (217, 237, 235), 'jp': (217, 237, 235), 'tw': (217, 237, 235)}, button={'cn': (341, 433, 391, 472), 'en': (341, 433, 391, 472), 'jp': (341, 433, 391, 472), 'tw': (341, 433, 391, 472)}, file={'cn': './assets/cn/handler/ANDROID_NO_RESPOND.png', 'en': './assets/en/handler/ANDROID_NO_RESPOND.png', 'jp': './assets/jp/handler/ANDROID_NO_RESPOND.png', 'tw': './assets/tw/handler/ANDROID_NO_RESPOND.png'}) LOGIN_RETURN_INFO = Button(area={'cn': (960, 123, 1158, 333), 'en': (960, 123, 1158, 333), 'jp': (960, 123, 1158, 333), 'tw': (960, 123, 1158, 333)}, color={'cn': (170, 185, 166), 'en': (170, 185, 166), 'jp': (170, 185, 166), 'tw': (170, 185, 166)}, button={'cn': (960, 123, 1158, 333), 'en': (960, 123, 1158, 333), 'jp': (960, 123, 1158, 333), 'tw': (960, 123, 1158, 333)}, file={'cn': './assets/cn/handler/LOGIN_RETURN_INFO.png', 'en': './assets/en/handler/LOGIN_RETURN_INFO.png', 'jp': './assets/jp/handler/LOGIN_RETURN_INFO.png', 'tw': './assets/tw/handler/LOGIN_RETURN_INFO.png'}) LOGIN_RETURN_SIGN = Button(area={'cn': (1, 7, 104, 47), 'en': (1, 7, 118, 39), 'jp': (1, 7, 104, 47), 'tw': (1, 7, 104, 47)}, color={'cn': (158, 214, 229), 'en': (176, 223, 236), 'jp': (158, 214, 229), 'tw': (158, 214, 229)}, button={'cn': (1, 7, 104, 47), 'en': (1, 7, 118, 39), 'jp': (1, 7, 104, 47), 'tw': (1, 7, 104, 47)}, file={'cn': './assets/cn/handler/LOGIN_RETURN_SIGN.png', 'en': './assets/en/handler/LOGIN_RETURN_SIGN.png', 'jp': './assets/jp/handler/LOGIN_RETURN_SIGN.png', 'tw': './assets/tw/handler/LOGIN_RETURN_SIGN.png'}) MAINTENANCE_ANNOUNCE = Button(area={'cn': (923, 141, 990, 186), 'en': (923, 141, 990, 186), 'jp': (923, 141, 990, 186), 'tw': (923, 141, 990, 186)}, color={'cn': (207, 95, 91), 'en': (207, 95, 91), 'jp': (207, 95, 91), 'tw': (207, 95, 91)}, button={'cn': (923, 141, 990, 186), 'en': (923, 141, 990, 186), 'jp': (923, 141, 990, 186), 'tw': (923, 141, 990, 186)}, file={'cn': './assets/cn/handler/MAINTENANCE_ANNOUNCE.png', 'en': './assets/en/handler/MAINTENANCE_ANNOUNCE.png', 'jp': './assets/jp/handler/MAINTENANCE_ANNOUNCE.png', 'tw': './assets/tw/handler/MAINTENANCE_ANNOUNCE.png'}) diff --git a/module/private_quarters/assets.py b/module/private_quarters/assets.py index fe326220b5..b54d89a00c 100644 --- a/module/private_quarters/assets.py +++ b/module/private_quarters/assets.py @@ -28,8 +28,8 @@ PRIVATE_QUARTERS_SHOP_CHECK = Button(area={'cn': (124, 18, 218, 48), 'en': (129, 25, 284, 40), 'jp': (126, 20, 260, 44), 'tw': (129, 25, 284, 40)}, color={'cn': (86, 92, 98), 'en': (142, 147, 155), 'jp': (82, 88, 94), 'tw': (142, 147, 155)}, button={'cn': (124, 18, 218, 48), 'en': (129, 25, 284, 40), 'jp': (126, 20, 260, 44), 'tw': (129, 25, 284, 40)}, file={'cn': './assets/cn/private_quarters/PRIVATE_QUARTERS_SHOP_CHECK.png', 'en': './assets/en/private_quarters/PRIVATE_QUARTERS_SHOP_CHECK.png', 'jp': './assets/jp/private_quarters/PRIVATE_QUARTERS_SHOP_CHECK.png', 'tw': './assets/tw/private_quarters/PRIVATE_QUARTERS_SHOP_CHECK.png'}) PRIVATE_QUARTERS_SHOP_CONFIRM_AMOUNT = Button(area={'cn': (790, 574, 842, 601), 'en': (773, 580, 858, 595), 'jp': (789, 577, 841, 600), 'tw': (773, 580, 858, 595)}, color={'cn': (94, 195, 252), 'en': (139, 214, 255), 'jp': (92, 195, 252), 'tw': (139, 214, 255)}, button={'cn': (651, 566, 981, 611), 'en': (731, 573, 901, 603), 'jp': (731, 573, 901, 603), 'tw': (731, 573, 901, 603)}, file={'cn': './assets/cn/private_quarters/PRIVATE_QUARTERS_SHOP_CONFIRM_AMOUNT.png', 'en': './assets/en/private_quarters/PRIVATE_QUARTERS_SHOP_CONFIRM_AMOUNT.png', 'jp': './assets/jp/private_quarters/PRIVATE_QUARTERS_SHOP_CONFIRM_AMOUNT.png', 'tw': './assets/tw/private_quarters/PRIVATE_QUARTERS_SHOP_CONFIRM_AMOUNT.png'}) PRIVATE_QUARTERS_SHOP_ENTER = Button(area={'cn': (1028, 641, 1078, 691), 'en': (1028, 641, 1078, 691), 'jp': (1028, 641, 1078, 691), 'tw': (1028, 641, 1078, 691)}, color={'cn': (243, 227, 213), 'en': (243, 227, 213), 'jp': (243, 227, 213), 'tw': (243, 227, 213)}, button={'cn': (1028, 641, 1078, 691), 'en': (1028, 641, 1078, 691), 'jp': (1028, 641, 1078, 691), 'tw': (1028, 641, 1078, 691)}, file={'cn': './assets/cn/private_quarters/PRIVATE_QUARTERS_SHOP_ENTER.png', 'en': './assets/en/private_quarters/PRIVATE_QUARTERS_SHOP_ENTER.png', 'jp': './assets/jp/private_quarters/PRIVATE_QUARTERS_SHOP_ENTER.png', 'tw': './assets/tw/private_quarters/PRIVATE_QUARTERS_SHOP_ENTER.png'}) -PRIVATE_QUARTERS_SHOP_GEMS = Button(area={'cn': (1142, 25, 1227, 49), 'en': (1142, 25, 1227, 49), 'jp': (1142, 25, 1227, 49), 'tw': (1142, 25, 1227, 49)}, color={'cn': (168, 179, 162), 'en': (168, 179, 162), 'jp': (168, 179, 162), 'tw': (168, 179, 162)}, button={'cn': (1142, 25, 1227, 49), 'en': (1142, 25, 1227, 49), 'jp': (1142, 25, 1227, 49), 'tw': (1142, 25, 1227, 49)}, file={'cn': './assets/cn/private_quarters/PRIVATE_QUARTERS_SHOP_GEMS.png', 'en': './assets/cn/private_quarters/PRIVATE_QUARTERS_SHOP_GEMS.png', 'jp': './assets/cn/private_quarters/PRIVATE_QUARTERS_SHOP_GEMS.png', 'tw': './assets/cn/private_quarters/PRIVATE_QUARTERS_SHOP_GEMS.png'}) +PRIVATE_QUARTERS_SHOP_GEMS = Button(area={'cn': (1142, 25, 1227, 49), 'en': (1142, 25, 1227, 49), 'jp': (1142, 25, 1227, 49), 'tw': (1142, 25, 1227, 49)}, color={'cn': (168, 179, 162), 'en': (168, 179, 162), 'jp': (168, 179, 162), 'tw': (168, 179, 162)}, button={'cn': (1142, 25, 1227, 49), 'en': (1142, 25, 1227, 49), 'jp': (1142, 25, 1227, 49), 'tw': (1142, 25, 1227, 49)}, file={'cn': './assets/cn/private_quarters/PRIVATE_QUARTERS_SHOP_GEMS.png', 'en': './assets/en/private_quarters/PRIVATE_QUARTERS_SHOP_GEMS.png', 'jp': './assets/jp/private_quarters/PRIVATE_QUARTERS_SHOP_GEMS.png', 'tw': './assets/tw/private_quarters/PRIVATE_QUARTERS_SHOP_GEMS.png'}) PRIVATE_QUARTERS_SHOP_GOLD_COINS = Button(area={'cn': (963, 25, 1055, 47), 'en': (963, 25, 1055, 47), 'jp': (973, 25, 1051, 48), 'tw': (963, 25, 1055, 47)}, color={'cn': (186, 197, 204), 'en': (186, 197, 204), 'jp': (169, 181, 185), 'tw': (186, 197, 204)}, button={'cn': (963, 25, 1055, 47), 'en': (963, 25, 1055, 47), 'jp': (973, 25, 1051, 48), 'tw': (963, 25, 1055, 47)}, file={'cn': './assets/cn/private_quarters/PRIVATE_QUARTERS_SHOP_GOLD_COINS.png', 'en': './assets/en/private_quarters/PRIVATE_QUARTERS_SHOP_GOLD_COINS.png', 'jp': './assets/jp/private_quarters/PRIVATE_QUARTERS_SHOP_GOLD_COINS.png', 'tw': './assets/tw/private_quarters/PRIVATE_QUARTERS_SHOP_GOLD_COINS.png'}) -PRIVATE_QUARTERS_SHOP_WEEKLY_CAKES_GET = Button(area={'cn': (591, 339, 688, 420), 'en': (591, 339, 688, 420), 'jp': (591, 339, 688, 420), 'tw': (591, 339, 688, 420)}, color={'cn': (192, 180, 172), 'en': (192, 180, 172), 'jp': (192, 180, 172), 'tw': (192, 180, 172)}, button={'cn': (591, 339, 688, 420), 'en': (591, 339, 688, 420), 'jp': (591, 339, 688, 420), 'tw': (591, 339, 688, 420)}, file={'cn': './assets/cn/private_quarters/PRIVATE_QUARTERS_SHOP_WEEKLY_CAKES_GET.png', 'en': './assets/cn/private_quarters/PRIVATE_QUARTERS_SHOP_WEEKLY_CAKES_GET.png', 'jp': './assets/cn/private_quarters/PRIVATE_QUARTERS_SHOP_WEEKLY_CAKES_GET.png', 'tw': './assets/cn/private_quarters/PRIVATE_QUARTERS_SHOP_WEEKLY_CAKES_GET.png'}) +PRIVATE_QUARTERS_SHOP_WEEKLY_CAKES_GET = Button(area={'cn': (591, 339, 688, 420), 'en': (591, 339, 688, 420), 'jp': (591, 339, 688, 420), 'tw': (591, 339, 688, 420)}, color={'cn': (192, 180, 172), 'en': (192, 180, 172), 'jp': (192, 180, 172), 'tw': (192, 180, 172)}, button={'cn': (591, 339, 688, 420), 'en': (591, 339, 688, 420), 'jp': (591, 339, 688, 420), 'tw': (591, 339, 688, 420)}, file={'cn': './assets/cn/private_quarters/PRIVATE_QUARTERS_SHOP_WEEKLY_CAKES_GET.png', 'en': './assets/en/private_quarters/PRIVATE_QUARTERS_SHOP_WEEKLY_CAKES_GET.png', 'jp': './assets/jp/private_quarters/PRIVATE_QUARTERS_SHOP_WEEKLY_CAKES_GET.png', 'tw': './assets/tw/private_quarters/PRIVATE_QUARTERS_SHOP_WEEKLY_CAKES_GET.png'}) PRIVATE_QUARTERS_SHOP_WEEKLY_ROSES_CHECK = Button(area={'cn': (968, 489, 1028, 539), 'en': (968, 489, 1028, 539), 'jp': (968, 489, 1028, 539), 'tw': (968, 489, 1028, 539)}, color={'cn': (131, 109, 120), 'en': (131, 109, 120), 'jp': (131, 109, 120), 'tw': (131, 109, 120)}, button={'cn': (968, 489, 1028, 539), 'en': (968, 489, 1028, 539), 'jp': (968, 489, 1028, 539), 'tw': (968, 489, 1028, 539)}, file={'cn': './assets/cn/private_quarters/PRIVATE_QUARTERS_SHOP_WEEKLY_ROSES_CHECK.png', 'en': './assets/en/private_quarters/PRIVATE_QUARTERS_SHOP_WEEKLY_ROSES_CHECK.png', 'jp': './assets/jp/private_quarters/PRIVATE_QUARTERS_SHOP_WEEKLY_ROSES_CHECK.png', 'tw': './assets/tw/private_quarters/PRIVATE_QUARTERS_SHOP_WEEKLY_ROSES_CHECK.png'}) PRIVATE_QUARTERS_SHOP_WEEKLY_ROSES_GET = Button(area={'cn': (598, 335, 683, 420), 'en': (598, 335, 683, 420), 'jp': (598, 335, 683, 420), 'tw': (598, 335, 683, 420)}, color={'cn': (151, 130, 137), 'en': (151, 130, 137), 'jp': (151, 130, 137), 'tw': (151, 130, 137)}, button={'cn': (598, 335, 683, 420), 'en': (598, 335, 683, 420), 'jp': (598, 335, 683, 420), 'tw': (598, 335, 683, 420)}, file={'cn': './assets/cn/private_quarters/PRIVATE_QUARTERS_SHOP_WEEKLY_ROSES_GET.png', 'en': './assets/en/private_quarters/PRIVATE_QUARTERS_SHOP_WEEKLY_ROSES_GET.png', 'jp': './assets/jp/private_quarters/PRIVATE_QUARTERS_SHOP_WEEKLY_ROSES_GET.png', 'tw': './assets/tw/private_quarters/PRIVATE_QUARTERS_SHOP_WEEKLY_ROSES_GET.png'}) diff --git a/module/retire/assets.py b/module/retire/assets.py index c7f78ce3de..d88f2fe30c 100644 --- a/module/retire/assets.py +++ b/module/retire/assets.py @@ -6,7 +6,7 @@ COMMON_SHIP_FILTER_DISABLE = Button(area={'cn': (714, 13, 757, 41), 'en': (706, 15, 758, 41), 'jp': (711, 13, 759, 42), 'tw': (712, 12, 759, 42)}, color={'cn': (127, 143, 183), 'en': (86, 99, 138), 'jp': (119, 131, 170), 'tw': (117, 132, 173)}, button={'cn': (714, 13, 757, 41), 'en': (706, 15, 758, 41), 'jp': (711, 13, 759, 42), 'tw': (712, 12, 759, 42)}, file={'cn': './assets/cn/retire/COMMON_SHIP_FILTER_DISABLE.png', 'en': './assets/en/retire/COMMON_SHIP_FILTER_DISABLE.png', 'jp': './assets/jp/retire/COMMON_SHIP_FILTER_DISABLE.png', 'tw': './assets/tw/retire/COMMON_SHIP_FILTER_DISABLE.png'}) COMMON_SHIP_FILTER_ENABLE = Button(area={'cn': (713, 12, 758, 41), 'en': (707, 16, 758, 40), 'jp': (712, 13, 758, 42), 'tw': (712, 11, 759, 42)}, color={'cn': (214, 174, 130), 'en': (193, 149, 106), 'jp': (212, 171, 129), 'tw': (211, 170, 127)}, button={'cn': (713, 12, 758, 41), 'en': (707, 16, 758, 40), 'jp': (712, 13, 758, 42), 'tw': (712, 11, 759, 42)}, file={'cn': './assets/cn/retire/COMMON_SHIP_FILTER_ENABLE.png', 'en': './assets/en/retire/COMMON_SHIP_FILTER_ENABLE.png', 'jp': './assets/jp/retire/COMMON_SHIP_FILTER_ENABLE.png', 'tw': './assets/tw/retire/COMMON_SHIP_FILTER_ENABLE.png'}) -DOCK_AMOUNT = Button(area={'cn': (473, 17, 611, 41), 'en': (476, 9, 611, 44), 'jp': (473, 14, 584, 42), 'tw': (473, 17, 611, 41)}, color={'cn': (71, 74, 78), 'en': (50, 54, 58), 'jp': (46, 53, 61), 'tw': (71, 74, 78)}, button={'cn': (473, 17, 611, 41), 'en': (476, 9, 611, 44), 'jp': (473, 14, 584, 42), 'tw': (473, 17, 611, 41)}, file={'cn': './assets/cn/retire/DOCK_AMOUNT.png', 'en': './assets/en/retire/DOCK_AMOUNT.png', 'jp': './assets/jp/retire/DOCK_AMOUNT.png', 'tw': './assets/tw/retire/DOCK_AMOUNT.png'}) +DOCK_AMOUNT = Button(area={'cn': (473, 17, 611, 41), 'en': (513, 15, 588, 42), 'jp': (473, 14, 584, 42), 'tw': (473, 17, 611, 41)}, color={'cn': (71, 74, 78), 'en': (44, 49, 54), 'jp': (46, 53, 61), 'tw': (71, 74, 78)}, button={'cn': (473, 17, 611, 41), 'en': (513, 15, 588, 42), 'jp': (473, 14, 584, 42), 'tw': (473, 17, 611, 41)}, file={'cn': './assets/cn/retire/DOCK_AMOUNT.png', 'en': './assets/en/retire/DOCK_AMOUNT.png', 'jp': './assets/jp/retire/DOCK_AMOUNT.png', 'tw': './assets/tw/retire/DOCK_AMOUNT.png'}) DOCK_CHECK = Button(area={'cn': (121, 14, 175, 39), 'en': (121, 17, 189, 39), 'jp': (232, 12, 308, 41), 'tw': (121, 13, 176, 39)}, color={'cn': (156, 171, 207), 'en': (112, 125, 162), 'jp': (88, 96, 128), 'tw': (149, 164, 200)}, button={'cn': (121, 14, 175, 39), 'en': (121, 17, 189, 39), 'jp': (232, 12, 308, 41), 'tw': (121, 13, 176, 39)}, file={'cn': './assets/cn/retire/DOCK_CHECK.png', 'en': './assets/en/retire/DOCK_CHECK.png', 'jp': './assets/jp/retire/DOCK_CHECK.png', 'tw': './assets/tw/retire/DOCK_CHECK.png'}) DOCK_EMPTY = Button(area={'cn': (95, 347, 388, 378), 'en': (95, 318, 264, 339), 'jp': (96, 347, 252, 376), 'tw': (94, 347, 390, 379)}, color={'cn': (160, 154, 159), 'en': (106, 99, 106), 'jp': (159, 152, 156), 'tw': (163, 157, 162)}, button={'cn': (95, 347, 388, 378), 'en': (95, 318, 264, 339), 'jp': (96, 347, 252, 376), 'tw': (94, 347, 390, 379)}, file={'cn': './assets/cn/retire/DOCK_EMPTY.png', 'en': './assets/en/retire/DOCK_EMPTY.png', 'jp': './assets/jp/retire/DOCK_EMPTY.png', 'tw': './assets/tw/retire/DOCK_EMPTY.png'}) DOCK_FILTER = Button(area={'cn': (1099, 5, 1193, 48), 'en': (1098, 4, 1194, 49), 'jp': (1101, 6, 1192, 46), 'tw': (1099, 6, 1193, 47)}, color={'cn': (70, 87, 127), 'en': (73, 90, 128), 'jp': (67, 84, 125), 'tw': (78, 96, 137)}, button={'cn': (1099, 5, 1193, 48), 'en': (1098, 4, 1194, 49), 'jp': (1101, 6, 1192, 46), 'tw': (1099, 6, 1193, 47)}, file={'cn': './assets/cn/retire/DOCK_FILTER.png', 'en': './assets/en/retire/DOCK_FILTER.png', 'jp': './assets/jp/retire/DOCK_FILTER.png', 'tw': './assets/tw/retire/DOCK_FILTER.png'}) @@ -14,7 +14,7 @@ DOCK_FIRST_NPC = Button(area={'cn': (96, 111, 123, 123), 'en': (96, 111, 123, 123), 'jp': (96, 111, 123, 123), 'tw': (96, 111, 123, 123)}, color={'cn': (184, 150, 150), 'en': (184, 150, 150), 'jp': (184, 150, 150), 'tw': (184, 150, 150)}, button={'cn': (96, 111, 123, 123), 'en': (96, 111, 123, 123), 'jp': (96, 111, 123, 123), 'tw': (96, 111, 123, 123)}, file={'cn': './assets/cn/retire/DOCK_FIRST_NPC.png', 'en': './assets/cn/retire/DOCK_FIRST_NPC.png', 'jp': './assets/cn/retire/DOCK_FIRST_NPC.png', 'tw': './assets/cn/retire/DOCK_FIRST_NPC.png'}) DOCK_SCROLL = Button(area={'cn': (1239, 76, 1248, 641), 'en': (1239, 76, 1248, 641), 'jp': (1237, 78, 1250, 628), 'tw': (1239, 76, 1248, 641)}, color={'cn': (47, 46, 37), 'en': (47, 46, 37), 'jp': (180, 156, 66), 'tw': (47, 46, 37)}, button={'cn': (1239, 76, 1248, 641), 'en': (1239, 76, 1248, 641), 'jp': (1237, 78, 1250, 628), 'tw': (1239, 76, 1248, 641)}, file={'cn': './assets/cn/retire/DOCK_SCROLL.png', 'en': './assets/en/retire/DOCK_SCROLL.png', 'jp': './assets/jp/retire/DOCK_SCROLL.png', 'tw': './assets/tw/retire/DOCK_SCROLL.png'}) DOCK_SELECTED = Button(area={'cn': (582, 662, 647, 685), 'en': (597, 658, 655, 691), 'jp': (603, 662, 655, 685), 'tw': (582, 662, 647, 685)}, color={'cn': (75, 75, 83), 'en': (64, 62, 73), 'jp': (84, 83, 92), 'tw': (75, 75, 83)}, button={'cn': (582, 662, 647, 685), 'en': (597, 658, 655, 691), 'jp': (603, 662, 655, 685), 'tw': (582, 662, 647, 685)}, file={'cn': './assets/cn/retire/DOCK_SELECTED.png', 'en': './assets/en/retire/DOCK_SELECTED.png', 'jp': './assets/jp/retire/DOCK_SELECTED.png', 'tw': './assets/tw/retire/DOCK_SELECTED.png'}) -EMPTY_ENHANCE_SLOT_PLUS = Button(area={'cn': (737, 402, 773, 437), 'en': (737, 402, 773, 437), 'jp': (737, 402, 773, 437), 'tw': (737, 402, 773, 437)}, color={'cn': (46, 46, 46), 'en': (46, 46, 46), 'jp': (46, 46, 46), 'tw': (46, 46, 46)}, button={'cn': (737, 402, 773, 437), 'en': (737, 402, 773, 437), 'jp': (737, 402, 773, 437), 'tw': (737, 402, 773, 437)}, file={'cn': './assets/cn/retire/EMPTY_ENHANCE_SLOT_PLUS.png', 'en': './assets/en/retire/EMPTY_ENHANCE_SLOT_PLUS.png', 'jp': './assets/jp/retire/EMPTY_ENHANCE_SLOT_PLUS.png', 'tw': './assets/tw/retire/EMPTY_ENHANCE_SLOT_PLUS.png'}) +EMPTY_ENHANCE_SLOT_PLUS = Button(area={'cn': (737, 402, 773, 437), 'en': (747, 402, 782, 437), 'jp': (737, 402, 773, 437), 'tw': (737, 402, 773, 437)}, color={'cn': (46, 46, 46), 'en': (38, 38, 38), 'jp': (46, 46, 46), 'tw': (46, 46, 46)}, button={'cn': (737, 402, 773, 437), 'en': (747, 402, 782, 437), 'jp': (737, 402, 773, 437), 'tw': (737, 402, 773, 437)}, file={'cn': './assets/cn/retire/EMPTY_ENHANCE_SLOT_PLUS.png', 'en': './assets/en/retire/EMPTY_ENHANCE_SLOT_PLUS.png', 'jp': './assets/jp/retire/EMPTY_ENHANCE_SLOT_PLUS.png', 'tw': './assets/tw/retire/EMPTY_ENHANCE_SLOT_PLUS.png'}) ENHANCE_CONFIRM = Button(area={'cn': (1126, 602, 1256, 645), 'en': (1226, 605, 1262, 641), 'jp': (1126, 601, 1257, 646), 'tw': (1215, 606, 1253, 641)}, color={'cn': (203, 149, 81), 'en': (212, 167, 100), 'jp': (189, 139, 78), 'tw': (216, 169, 99)}, button={'cn': (1126, 602, 1256, 645), 'en': (1226, 605, 1262, 641), 'jp': (1126, 601, 1257, 646), 'tw': (1215, 606, 1253, 641)}, file={'cn': './assets/cn/retire/ENHANCE_CONFIRM.png', 'en': './assets/en/retire/ENHANCE_CONFIRM.png', 'jp': './assets/jp/retire/ENHANCE_CONFIRM.png', 'tw': './assets/tw/retire/ENHANCE_CONFIRM.png'}) ENHANCE_FILLED = Button(area={'cn': (728, 440, 781, 454), 'en': (728, 440, 781, 454), 'jp': (722, 387, 789, 400), 'tw': (728, 440, 781, 454)}, color={'cn': (156, 138, 127), 'en': (156, 138, 127), 'jp': (146, 153, 211), 'tw': (156, 138, 127)}, button={'cn': (728, 440, 781, 454), 'en': (728, 440, 781, 454), 'jp': (722, 387, 789, 400), 'tw': (728, 440, 781, 454)}, file={'cn': './assets/cn/retire/ENHANCE_FILLED.png', 'en': './assets/en/retire/ENHANCE_FILLED.png', 'jp': './assets/jp/retire/ENHANCE_FILLED.png', 'tw': './assets/tw/retire/ENHANCE_FILLED.png'}) ENHANCE_RECOMMEND = Button(area={'cn': (959, 602, 1089, 645), 'en': (1057, 606, 1091, 640), 'jp': (965, 605, 1085, 641), 'tw': (1050, 607, 1082, 639)}, color={'cn': (87, 134, 194), 'en': (106, 151, 215), 'jp': (86, 136, 201), 'tw': (106, 151, 212)}, button={'cn': (959, 602, 1089, 645), 'en': (1057, 606, 1091, 640), 'jp': (965, 605, 1085, 641), 'tw': (1050, 607, 1082, 639)}, file={'cn': './assets/cn/retire/ENHANCE_RECOMMEND.png', 'en': './assets/en/retire/ENHANCE_RECOMMEND.png', 'jp': './assets/jp/retire/ENHANCE_RECOMMEND.png', 'tw': './assets/tw/retire/ENHANCE_RECOMMEND.png'}) diff --git a/module/statistics/campaign_bonus.py b/module/statistics/campaign_bonus.py index f228c6100f..97769af9e2 100644 --- a/module/statistics/campaign_bonus.py +++ b/module/statistics/campaign_bonus.py @@ -1,18 +1,22 @@ from module.base.button import ButtonGrid from module.base.utils import * -from module.handler.assets import AUTO_SEARCH_MENU_EXIT +from module.handler.assets import AUTO_SEARCH_MENU_EXIT, AUTO_SEARCH_MENU_CONTINUE from module.statistics.assets import CAMPAIGN_BONUS from module.statistics.get_items import ITEM_GROUP, GetItemsStatistics -from module.statistics.item import Item +from module.statistics.item import Item, AmountOcr from module.statistics.utils import * - +from module.logger import logger +from module.base.timer import Timer +import time +import os +import cv2 class BonusItem(Item): def predict_valid(self): return np.mean(rgb2gray(self.image) > 160) > 0.1 -class CampaignBonusStatistics(GetItemsStatistics): +class CampaignBonusStatistics(GetItemsStatistics,ButtonGrid,Timer): def appear_on(self, image): if AUTO_SEARCH_MENU_EXIT.match(image, offset=(200, 20)) \ and CAMPAIGN_BONUS.match(image, offset=(20, 500)): @@ -62,3 +66,158 @@ def revise_item(self, item): item.amount *= 10 return item + + def wait_until_reward_stable(self, timeout=8.6, required_consecutive_matches=3, similarity_threshold=0.999): + """ + checks if all rewards have appeared, via first doing a predicted grid, 3 types to account for the Ui + change due to Meta xp, then verifies via amount ocr, after that waits till the reward area has no changes + e.g all drops appeared + + timeout: may or may not need adjustment, after gathering data\\ + required_consecutive_matches: 3 seems a good compromise\\ + similarity_threshold: in testing the similarity ranged in 0.002- 0.01 ranges so opted for 0.999 + """ + logger.info('wait until area stable') + + try: + # 1. Setup debug directories + os.makedirs('./screenshots/debug', exist_ok=True) + os.makedirs('./screenshots/dropstats', exist_ok=True) + + # 2. Image handling separation, ocr uses non modified, area stable check uses modified + self.device.screenshot() + original_bgr = self.device.image.copy() + gray_image = cv2.cvtColor(original_bgr, cv2.COLOR_BGR2GRAY) + + # 3. Debugging/Reference visualization (BGR) + exit_btn = AUTO_SEARCH_MENU_EXIT._button + continue_btn = AUTO_SEARCH_MENU_CONTINUE._button + debug_img = original_bgr.copy() + cv2.rectangle(debug_img, (exit_btn[0], exit_btn[1]), (exit_btn[2], exit_btn[3]), (255,0,0), 2) + cv2.rectangle(debug_img, (continue_btn[0], continue_btn[1]), (continue_btn[2], continue_btn[3]), (0,0,255), 2) + cv2.imwrite('./screenshots/debug/reference_points.png', debug_img) + + # 4. Grid setup with single row, tested with EN, CN is fine due to same button, + #continue button shift less of an issue, but alot of the measures depend on exit + #TW might have grid too low and ocr area might miss the numbers area + #compared to EN/CN: exit width -42, height -9, continue width -3, height -5 + #JP gonna have issues due to not using a default exit asset, ui moved due to meta xp sidebar + #compared to EN/CN: exit width -1, height -1, but further on the left by 68px + #continue width -1, height the same, but further on the right side by 72px + GRID_PARAMS = { + 'button_shape': (64, 64), + 'grid_shape': (7, 1), # need to only check first row + 'delta': (72 + 2/3, 0), + 'name': 'REWARD_GRID' + } + exit_x, exit_y = exit_btn[0], exit_btn[1] + GRID_ORIGINS = [ + (exit_x + 22, exit_y - 417), # Primary + (exit_x + 22 - 72, exit_y - 417), # Fallback 1 + (exit_x + 22 - 129, exit_y - 417) # Fallback 2 + ] + + # 5. Grid verification with early exit + for i, (grid_x, grid_y) in enumerate(GRID_ORIGINS): + grid = ButtonGrid(origin=(grid_x, grid_y), **GRID_PARAMS) + + # Process all buttons but exit on first match + matched = self._verify_grid_via_ocr(grid, i, original_bgr) + if matched: + logger.info(f"Grid {i} validated") + continue_btn = AUTO_SEARCH_MENU_CONTINUE._button + monitor_area = (grid_x, grid_y, grid_x + (7 * (72 + 2/3)), continue_btn[1]) + self.device.image = gray_image + return self.reward_area_stable_check(monitor_area,timeout,required_consecutive_matches,similarity_threshold) + + logger.warning("All grid positions failed") + return False + + except Exception as e: + logger.error(f'Stabilization failure: {str(e)}') + return False + + def _verify_grid_via_ocr(self, grid, bgr_image): + """ + double checks if grid correct via the item amount on the bottom right of grid buttons + """ + amount_ocr = AmountOcr([], threshold=96, name='Amount_ocr') + + for i, btn in enumerate(grid.buttons): + try: + # 1. Get button image (keep as BGR) + button_img = crop(bgr_image, btn.area) + + # 2. area should be big enough to catch random strays + #mainly looking at JP since they used an exit button as reference where a meta ship gained xp on the side + #whereas CN/EN/TW used the default one, + h, w = button_img.shape[:2] + amount_area = (int(w*0.6), int(h*0.8),w,h) + amount_img = crop(button_img, amount_area) + + # 3. Ensure keep BGR format for OCR + if len(amount_img.shape) == 2: + amount_img = cv2.cvtColor(amount_img, cv2.COLOR_GRAY2BGR) + + # 4. OCR just like the one in ItemGrid in item.py + amount = amount_ocr.ocr([amount_img], direct_ocr=True)[0] + logger.info(f"amount: {amount}") + if amount > 0: + return True + + except Exception as e: + logger.warning(f"Button {i} check failed: {str(e)}") + continue + + return False + + def reward_area_stable_check(self, monitor_area, timeout, required_matches, similarity_threshold): + """ + checks if the previously assigned area has any changes + + returns True for being stable after 3 same consecutive matches, while similarity was met or beaten + """ + start_time = time.time() + stabilization_start = None + consecutive_matches = 0 + check_count = 0 + + last_luma = crop(self.device.image, monitor_area) + cv2.imwrite('./screenshots/dropstats/0_initial.png', last_luma) + + while True: + current_time = time.time() + elapsed = current_time - start_time + + if elapsed >= timeout: + logger.warning(f'Timeout after {elapsed:.2f}s (never stabilized)') + return False + + if current_time - last_check >= 0.2: + check_count += 1 + last_check = current_time + + self.device.screenshot() + current_luma = cv2.cvtColor(self.device.image, cv2.COLOR_BGR2GRAY) + current_luma = crop(current_luma, monitor_area) + cv2.imwrite(f'./screenshots/dropstats/{check_count}_check.png', current_luma) + + res = cv2.matchTemplate(last_luma, current_luma, cv2.TM_CCOEFF_NORMED) + similarity = cv2.minMaxLoc(res)[1] + + if similarity >= similarity_threshold: + consecutive_matches += 1 + if stabilization_start is None: + stabilization_start = time.time() + + if consecutive_matches >= required_matches: + stabilization_time = time.time() - stabilization_start + total_time = time.time() - start_time + logger.info(f'Area stabilized after {total_time:.2f}s (took {stabilization_time:.2f}s to confirm)') + cv2.imwrite('./screenshots/dropstats/final_stable.png', current_luma) + return True + else: + consecutive_matches = 0 + stabilization_start = None + + last_luma = current_luma From cb8d66c351ea1b791a0e454800494284250cdd60 Mon Sep 17 00:00:00 2001 From: dragonheart107 Date: Sun, 3 Aug 2025 19:22:31 +0200 Subject: [PATCH 3/6] Upd: removed imwrite+ own file small tweaks --- assets/en/retire/DOCK_AMOUNT.png | Bin 4270 -> 5234 bytes assets/en/retire/EMPTY_ENHANCE_SLOT_PLUS.png | Bin 3132 -> 4811 bytes module/combat/auto_search_combat.py | 4 +- module/retire/assets.py | 4 +- module/statistics/autosearch_reward.py | 231 +++++++++++++++++++ module/statistics/campaign_bonus.py | 167 +------------- 6 files changed, 239 insertions(+), 167 deletions(-) create mode 100644 module/statistics/autosearch_reward.py diff --git a/assets/en/retire/DOCK_AMOUNT.png b/assets/en/retire/DOCK_AMOUNT.png index 1e3f8e4db8852962644b12070305e68c330c581c..a597226aedccb991f81665491187af73d8267a50 100644 GIT binary patch delta 2688 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 P00000NkvXXu0mjf5=$!} diff --git a/assets/en/retire/EMPTY_ENHANCE_SLOT_PLUS.png b/assets/en/retire/EMPTY_ENHANCE_SLOT_PLUS.png index 4c4b5602e3c0942056097e28fe1c936dfa4bbcb2..8676745e020764aa6abefc210556e780da4ea9de 100644 GIT binary patch literal 4811 zcmeHJYitx%6u#Rn6-sH0kfsqZlff98GCQw*bVk+!yM=CWOKn%FnwY&ib7yzb-I-x# z+THdCu1GW*HU24@z>g9FeGw2%%yuni%lTzFbIo==z7G zlikV8x!*nKJKsI`+Ng0cbj+#)j;n=!2r9XFO2W6UdXx7#Iz}+U}eIrD0?f36xZ|AbH`~G)bs( zklgBvGEpOhQflwGi3Z2}V$%4qB*+0}T0kE{uXf?*1Tr!zz2+Qsn#c7lUnG+2!)8%TUAZgvsOn99EG8cf+WXar5XGS#Mmy8!)xa6WV%F# zDM6#UtSV!*&@PJQW2_*uUeU*11;qs;LmS={EK8!In;D4HRWmS&XhTbqL{&sFq^ETg z2gdC9T6-iE>Nj;oP2&M;VABR7(i8Htg5OVZ9=1p~8Wkg&WkXFuk#La2(s)!=7FmCy zOW|dPVi>PPc_n-kBt@bU_~Ll%K}g_0#q|=yx-{YxfHS|e7i3++KK`L6vpkDr$WmRr zz)_s+^;1ysvy_(+1QvN^9}ZUWw#8KOc7*9#uMR62d+ZWqc`Vj5mN6(ifltiU7R| zWCey7Dv4iBoa17+CHeT8n*I00%aTYbs3j5Ztu*=HQ@OR8EOD$&WzOyQf2Be@4YgET z^tEMI9II)*coB;x+|ixK>QRDUs)rHMuu3LAYS=ICTMfYCW0COsSZ>exZ{Iw$d)>0e z{>jAR%UAf0BS$mOhdU1b9QpM(scm-8i643wE^&YPL|gG_tLGMf#Sf8yT4)X|;y?A)BH(|^6wiPtc{ znGbihg5#eoY6<+<_{?&L<+Jpte*ae!k-WZb>F^5A-qRoKDHu{;yxwrP^ZfkghhINC z(dTlX`pgC58=Kof!!iam9y|jI`;K3l#H;#!1OOVo(>XQiBG$Rw>+%gvw{ybt-0M>y zH!}Oy`2NAd5bh@5{W)uc_Lm!)@?b#=aFvS3aBX?&^hCurEP|O8$?i*6roaB?rP-x0 zZnWX!|_ zx_fFLdJixMsFh9@hvL8u0JFf|4nV%6y#NaQxik1f2O`jcp`*NT?eit5{X*~JMh2+z1`6n+nduy<+761y%TH@Gr;Mx$_Hh;}4P@_OW Z-tIcNS3K3ZVV~ldBRzfLLmT2d{s#0g(oz5b delta 588 zcmX@Dx<_JyOg)2xycZ(_0|R4mkh>G(&67IyKn_c~qpu?a!^VE@KZ&eBzCyA`kS_y6 zl^O#>Lkk1LFQ8Dv3kHT#0|tgy2@DKYGZ+}e3+C(!v;j(R7I;J!Gca%qgD@k*tT_@u z#!^og$B>F!Z|@rV9S#t1y(oEvFJQvW|BqzEIlq0-)KO9pQj|Soy}To6$1Jsdv)=FE z+N{X)k$Lk4&S#AE3A4v3@eGVPEDR1#4Doi19Zxw-7zC~yU{C?c8odfkGKGi%gTR2{07uz=29Ej!1_x$lg$70jwoGLPfdg(J&oi(w%sB8% zoWY)*;lTba@1L)Vdi||=@87qt`@h@O?p(5Z^{&@n_RKUpAaReGhoR@|(z@S$tNH(# zb2~6FGyIvlFLr;|&DX#7cHNDezx%qRWy4B)83u;KZ-4*X@$zPzOeWijRG_vq<-b41 zGqBVH9WsIC+I|M68ypM`KYy;SWBecobQ3$+O>S4$|6%2L4)UY$ejy2k*}Mh}6NC 1: + logger.info("Grid 0 validated, checking Grid 1 for potentially more rewards...") + grid1_x, grid1_y = GRID_ORIGINS[1] + grid1 = ButtonGrid(origin=(grid1_x, grid1_y), **GRID_PARAMS) + grid1_matched = self._verify_grid_via_ocr(grid1, 1, original_bgr, check_first_button_only=True) + + if grid1_matched: + logger.info("Grid 1 also validated - using Grid 1 for wider coverage") + validated_grids.append((1, grid1_x, grid1_y)) + else: + logger.info("Grid 1 failed validation - sticking with Grid 0") + + break # Found at least one valid grid, proceed + + # 5. Select the best grid (prefer Grid 1 over Grid 0 if both validate) + if not validated_grids: + logger.warning("All grid positions failed") + return False + + # Choose Grid 1 if available (better coverage), otherwise use the first validated grid + best_grid = None + for grid_info in validated_grids: + grid_idx, grid_x, grid_y = grid_info + if grid_idx == 1: # Prefer Grid 1 + best_grid = grid_info + break + + if best_grid is None: + best_grid = validated_grids[0] # Use first validated grid + + selected_idx, selected_x, selected_y = best_grid + logger.info(f"Using Grid {selected_idx} for monitoring (x={selected_x}, y={selected_y})") + + # 6. Set up monitoring area with selected grid + continue_btn = AUTO_SEARCH_MENU_CONTINUE._button + monitor_area = ( + int(selected_x), + int(selected_y), + int(selected_x + (7 * 72.67)), + int(continue_btn[1]) + ) + + logger.info(f"DEBUG - Selected Grid {selected_idx}: x={selected_x}, y={selected_y}") + logger.info(f"DEBUG - Monitor area: {monitor_area}") + + self.device.image = gray_image + return self.reward_area_stable_check(monitor_area, timeout, required_consecutive_matches, similarity_threshold) + + except Exception as e: + logger.error(f'Stabilization failure: {str(e)}') + return False + + def _verify_grid_via_ocr(self, grid, grid_idx, bgr_image, check_first_button_only=False): + """ + double checks if grid correct via the item amount on the bottom right of grid buttons + special handling for grid0 vs grid1 + """ + amount_ocr = AmountOcr([], threshold=96, name='Amount_ocr') + detected_amounts = [] + first_button_has_amount = False + + for btn_idx, btn in enumerate(grid.buttons): + try: + # 1. Get button image (keep as BGR) + button_img = crop(bgr_image, btn.area) + if button_img is None or button_img.size == 0: + continue + + # 2. Extract amount area (bottom-right portion) + h, w = button_img.shape[:2] + amount_area = (int(w*0.6), int(h*0.8), w, h) + amount_img = crop(button_img, amount_area) + + # 3. Ensure BGR format for OCR + if len(amount_img.shape) == 2: + amount_img = cv2.cvtColor(amount_img, cv2.COLOR_GRAY2BGR) + + # 4. OCR just like the one in ItemGrid in item.py + amount = amount_ocr.ocr([amount_img], direct_ocr=True)[0] + if amount > 0: + detected_amounts.append(amount) + logger.info(f"Grid {grid_idx}, Button {btn_idx}: amount={amount}") + + # Track if first button (index 0) has amount + if btn_idx == 0: + first_button_has_amount = True + + except Exception as e: + logger.warning(f"Grid {grid_idx}, Button {btn_idx} check failed: {str(e)}") + continue + + # Determine validation result + total_detected = len(detected_amounts) + + if check_first_button_only: + # For Grid 1 validation: only valid if first button has amount + if first_button_has_amount: + logger.info(f"Grid {grid_idx} summary: First button validated with amount - Grid 1 captures additional reward!") + return True + else: + logger.info(f"Grid {grid_idx} summary: First button has no amount - Grid 1 offers no advantage") + return False + else: + # Standard validation: any button with amount is sufficient + if total_detected > 0: + logger.info(f"Grid {grid_idx} summary: {total_detected} buttons with amounts detected") + return True + else: + logger.info(f"Grid {grid_idx} summary: No amounts detected") + return False + + def reward_area_stable_check(self, monitor_area, timeout, required_matches, similarity_threshold): + """ + checks if the previously assigned area has any changes + returns True for being stable after 3 same consecutive matches, while similarity was met or beaten + """ + start_time = time.time() + last_check = start_time + stabilization_start = None + consecutive_matches = 0 + check_count = 0 + + last_luma = crop(self.device.image, monitor_area) + self._save(last_luma, 'debug/dropstats', '0_initial.png') + + while True: + current_time = time.time() + elapsed = current_time - start_time + + if elapsed >= timeout: + logger.warning(f'Timeout after {elapsed:.2f}s (never stabilized)') + return False + + if current_time - last_check >= 0.2: + check_count += 1 + last_check = current_time + + self.device.screenshot() + current_luma = cv2.cvtColor(self.device.image, cv2.COLOR_BGR2GRAY) + current_luma = crop(current_luma, monitor_area) + self._save(current_luma, 'debug/dropstats', f'{check_count}_check.png') + + res = cv2.matchTemplate(last_luma, current_luma, cv2.TM_CCOEFF_NORMED) + similarity = cv2.minMaxLoc(res)[1] + + if similarity >= similarity_threshold: + consecutive_matches += 1 + if stabilization_start is None: + stabilization_start = time.time() + + if consecutive_matches >= required_matches: + stabilization_time = time.time() - stabilization_start + total_time = time.time() - start_time + logger.info(f'Area stabilized after {total_time:.2f}s (took {stabilization_time:.2f}s to confirm)') + self._save(current_luma, 'debug/dropstats', 'final_stable.png') + return True + else: + consecutive_matches = 0 + stabilization_start = None + + last_luma = current_luma diff --git a/module/statistics/campaign_bonus.py b/module/statistics/campaign_bonus.py index 97769af9e2..f228c6100f 100644 --- a/module/statistics/campaign_bonus.py +++ b/module/statistics/campaign_bonus.py @@ -1,22 +1,18 @@ from module.base.button import ButtonGrid from module.base.utils import * -from module.handler.assets import AUTO_SEARCH_MENU_EXIT, AUTO_SEARCH_MENU_CONTINUE +from module.handler.assets import AUTO_SEARCH_MENU_EXIT from module.statistics.assets import CAMPAIGN_BONUS from module.statistics.get_items import ITEM_GROUP, GetItemsStatistics -from module.statistics.item import Item, AmountOcr +from module.statistics.item import Item from module.statistics.utils import * -from module.logger import logger -from module.base.timer import Timer -import time -import os -import cv2 + class BonusItem(Item): def predict_valid(self): return np.mean(rgb2gray(self.image) > 160) > 0.1 -class CampaignBonusStatistics(GetItemsStatistics,ButtonGrid,Timer): +class CampaignBonusStatistics(GetItemsStatistics): def appear_on(self, image): if AUTO_SEARCH_MENU_EXIT.match(image, offset=(200, 20)) \ and CAMPAIGN_BONUS.match(image, offset=(20, 500)): @@ -66,158 +62,3 @@ def revise_item(self, item): item.amount *= 10 return item - - def wait_until_reward_stable(self, timeout=8.6, required_consecutive_matches=3, similarity_threshold=0.999): - """ - checks if all rewards have appeared, via first doing a predicted grid, 3 types to account for the Ui - change due to Meta xp, then verifies via amount ocr, after that waits till the reward area has no changes - e.g all drops appeared - - timeout: may or may not need adjustment, after gathering data\\ - required_consecutive_matches: 3 seems a good compromise\\ - similarity_threshold: in testing the similarity ranged in 0.002- 0.01 ranges so opted for 0.999 - """ - logger.info('wait until area stable') - - try: - # 1. Setup debug directories - os.makedirs('./screenshots/debug', exist_ok=True) - os.makedirs('./screenshots/dropstats', exist_ok=True) - - # 2. Image handling separation, ocr uses non modified, area stable check uses modified - self.device.screenshot() - original_bgr = self.device.image.copy() - gray_image = cv2.cvtColor(original_bgr, cv2.COLOR_BGR2GRAY) - - # 3. Debugging/Reference visualization (BGR) - exit_btn = AUTO_SEARCH_MENU_EXIT._button - continue_btn = AUTO_SEARCH_MENU_CONTINUE._button - debug_img = original_bgr.copy() - cv2.rectangle(debug_img, (exit_btn[0], exit_btn[1]), (exit_btn[2], exit_btn[3]), (255,0,0), 2) - cv2.rectangle(debug_img, (continue_btn[0], continue_btn[1]), (continue_btn[2], continue_btn[3]), (0,0,255), 2) - cv2.imwrite('./screenshots/debug/reference_points.png', debug_img) - - # 4. Grid setup with single row, tested with EN, CN is fine due to same button, - #continue button shift less of an issue, but alot of the measures depend on exit - #TW might have grid too low and ocr area might miss the numbers area - #compared to EN/CN: exit width -42, height -9, continue width -3, height -5 - #JP gonna have issues due to not using a default exit asset, ui moved due to meta xp sidebar - #compared to EN/CN: exit width -1, height -1, but further on the left by 68px - #continue width -1, height the same, but further on the right side by 72px - GRID_PARAMS = { - 'button_shape': (64, 64), - 'grid_shape': (7, 1), # need to only check first row - 'delta': (72 + 2/3, 0), - 'name': 'REWARD_GRID' - } - exit_x, exit_y = exit_btn[0], exit_btn[1] - GRID_ORIGINS = [ - (exit_x + 22, exit_y - 417), # Primary - (exit_x + 22 - 72, exit_y - 417), # Fallback 1 - (exit_x + 22 - 129, exit_y - 417) # Fallback 2 - ] - - # 5. Grid verification with early exit - for i, (grid_x, grid_y) in enumerate(GRID_ORIGINS): - grid = ButtonGrid(origin=(grid_x, grid_y), **GRID_PARAMS) - - # Process all buttons but exit on first match - matched = self._verify_grid_via_ocr(grid, i, original_bgr) - if matched: - logger.info(f"Grid {i} validated") - continue_btn = AUTO_SEARCH_MENU_CONTINUE._button - monitor_area = (grid_x, grid_y, grid_x + (7 * (72 + 2/3)), continue_btn[1]) - self.device.image = gray_image - return self.reward_area_stable_check(monitor_area,timeout,required_consecutive_matches,similarity_threshold) - - logger.warning("All grid positions failed") - return False - - except Exception as e: - logger.error(f'Stabilization failure: {str(e)}') - return False - - def _verify_grid_via_ocr(self, grid, bgr_image): - """ - double checks if grid correct via the item amount on the bottom right of grid buttons - """ - amount_ocr = AmountOcr([], threshold=96, name='Amount_ocr') - - for i, btn in enumerate(grid.buttons): - try: - # 1. Get button image (keep as BGR) - button_img = crop(bgr_image, btn.area) - - # 2. area should be big enough to catch random strays - #mainly looking at JP since they used an exit button as reference where a meta ship gained xp on the side - #whereas CN/EN/TW used the default one, - h, w = button_img.shape[:2] - amount_area = (int(w*0.6), int(h*0.8),w,h) - amount_img = crop(button_img, amount_area) - - # 3. Ensure keep BGR format for OCR - if len(amount_img.shape) == 2: - amount_img = cv2.cvtColor(amount_img, cv2.COLOR_GRAY2BGR) - - # 4. OCR just like the one in ItemGrid in item.py - amount = amount_ocr.ocr([amount_img], direct_ocr=True)[0] - logger.info(f"amount: {amount}") - if amount > 0: - return True - - except Exception as e: - logger.warning(f"Button {i} check failed: {str(e)}") - continue - - return False - - def reward_area_stable_check(self, monitor_area, timeout, required_matches, similarity_threshold): - """ - checks if the previously assigned area has any changes - - returns True for being stable after 3 same consecutive matches, while similarity was met or beaten - """ - start_time = time.time() - stabilization_start = None - consecutive_matches = 0 - check_count = 0 - - last_luma = crop(self.device.image, monitor_area) - cv2.imwrite('./screenshots/dropstats/0_initial.png', last_luma) - - while True: - current_time = time.time() - elapsed = current_time - start_time - - if elapsed >= timeout: - logger.warning(f'Timeout after {elapsed:.2f}s (never stabilized)') - return False - - if current_time - last_check >= 0.2: - check_count += 1 - last_check = current_time - - self.device.screenshot() - current_luma = cv2.cvtColor(self.device.image, cv2.COLOR_BGR2GRAY) - current_luma = crop(current_luma, monitor_area) - cv2.imwrite(f'./screenshots/dropstats/{check_count}_check.png', current_luma) - - res = cv2.matchTemplate(last_luma, current_luma, cv2.TM_CCOEFF_NORMED) - similarity = cv2.minMaxLoc(res)[1] - - if similarity >= similarity_threshold: - consecutive_matches += 1 - if stabilization_start is None: - stabilization_start = time.time() - - if consecutive_matches >= required_matches: - stabilization_time = time.time() - stabilization_start - total_time = time.time() - start_time - logger.info(f'Area stabilized after {total_time:.2f}s (took {stabilization_time:.2f}s to confirm)') - cv2.imwrite('./screenshots/dropstats/final_stable.png', current_luma) - return True - else: - consecutive_matches = 0 - stabilization_start = None - - last_luma = current_luma From 6a553b6ec568f848c9a8e5913baf37dcc84bb1bc Mon Sep 17 00:00:00 2001 From: dragonheart107 Date: Mon, 4 Aug 2025 08:27:03 +0200 Subject: [PATCH 4/6] small tweaks --- module/statistics/autosearch_reward.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/module/statistics/autosearch_reward.py b/module/statistics/autosearch_reward.py index 194e067efe..4b5e67ff65 100644 --- a/module/statistics/autosearch_reward.py +++ b/module/statistics/autosearch_reward.py @@ -1,5 +1,6 @@ import time import cv2 +import datetime import numpy as np from module.base.button import ButtonGrid from module.base.timer import Timer @@ -16,13 +17,14 @@ def wait_until_reward_stable(self, timeout=8.6, required_consecutive_matches=3, change due to Meta xp, then verifies via amount ocr, after that waits till the reward area has no changes e.g all drops appeared - timeout: may or may not need adjustment, after gathering data\\ + timeout: may or may not need adjustment, after gathering data; 5.23s longest atm\\ required_consecutive_matches: 3 seems a good compromise\\ similarity_threshold: in testing the similarity ranged in 0.002- 0.01 ranges so opted for 0.999 """ logger.info('wait until area stable') try: + timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") # 1. Image handling separation, ocr uses non modified, area stable check uses modified self.device.screenshot() original_bgr = self.device.image.copy() @@ -34,7 +36,7 @@ def wait_until_reward_stable(self, timeout=8.6, required_consecutive_matches=3, debug_img = original_bgr.copy() cv2.rectangle(debug_img, (exit_btn[0], exit_btn[1]), (exit_btn[2], exit_btn[3]), (255,0,0), 2) cv2.rectangle(debug_img, (continue_btn[0], continue_btn[1]), (continue_btn[2], continue_btn[3]), (0,0,255), 2) - self._save(debug_img, 'debug', 'reference_points.png') + self._save(debug_img, 'debug', f'{timestamp}_reference_points.png') # 3. Grid setup with single row, tested with EN, CN is fine due to same button, #continue button shift less of an issue, but alot of the measures depend on exit @@ -114,7 +116,7 @@ def wait_until_reward_stable(self, timeout=8.6, required_consecutive_matches=3, logger.info(f"DEBUG - Monitor area: {monitor_area}") self.device.image = gray_image - return self.reward_area_stable_check(monitor_area, timeout, required_consecutive_matches, similarity_threshold) + return self.reward_area_stable_check(monitor_area, timeout, required_consecutive_matches, similarity_threshold, timestamp) except Exception as e: logger.error(f'Stabilization failure: {str(e)}') @@ -179,7 +181,7 @@ def _verify_grid_via_ocr(self, grid, grid_idx, bgr_image, check_first_button_onl logger.info(f"Grid {grid_idx} summary: No amounts detected") return False - def reward_area_stable_check(self, monitor_area, timeout, required_matches, similarity_threshold): + def reward_area_stable_check(self, monitor_area, timeout, required_matches, similarity_threshold, timestamp=""): """ checks if the previously assigned area has any changes returns True for being stable after 3 same consecutive matches, while similarity was met or beaten @@ -191,7 +193,7 @@ def reward_area_stable_check(self, monitor_area, timeout, required_matches, simi check_count = 0 last_luma = crop(self.device.image, monitor_area) - self._save(last_luma, 'debug/dropstats', '0_initial.png') + self._save(last_luma, 'debug/dropstats', f'{timestamp}_0_initial.png') while True: current_time = time.time() @@ -208,7 +210,7 @@ def reward_area_stable_check(self, monitor_area, timeout, required_matches, simi self.device.screenshot() current_luma = cv2.cvtColor(self.device.image, cv2.COLOR_BGR2GRAY) current_luma = crop(current_luma, monitor_area) - self._save(current_luma, 'debug/dropstats', f'{check_count}_check.png') + self._save(current_luma, 'debug/dropstats', f'{timestamp}_{check_count}_check.png') res = cv2.matchTemplate(last_luma, current_luma, cv2.TM_CCOEFF_NORMED) similarity = cv2.minMaxLoc(res)[1] @@ -222,7 +224,7 @@ def reward_area_stable_check(self, monitor_area, timeout, required_matches, simi stabilization_time = time.time() - stabilization_start total_time = time.time() - start_time logger.info(f'Area stabilized after {total_time:.2f}s (took {stabilization_time:.2f}s to confirm)') - self._save(current_luma, 'debug/dropstats', 'final_stable.png') + self._save(current_luma, 'debug/dropstats', f'{timestamp}_final_stable.png') return True else: consecutive_matches = 0 From 14e0b297fca3fb59fcdc3b851cb939b80bcede25 Mon Sep 17 00:00:00 2001 From: dragonheart107 Date: Fri, 8 Aug 2025 02:29:14 +0200 Subject: [PATCH 5/6] simplified --- assets/cn/statistics/REWARD_AREA.png | Bin 0 -> 3439 bytes assets/cn/statistics/REWARD_HEADER2.png | Bin 0 -> 2938 bytes module/statistics/assets.py | 2 + module/statistics/autosearch_reward.py | 206 +++++------------------- 4 files changed, 46 insertions(+), 162 deletions(-) create mode 100644 assets/cn/statistics/REWARD_AREA.png create mode 100644 assets/cn/statistics/REWARD_HEADER2.png diff --git a/assets/cn/statistics/REWARD_AREA.png b/assets/cn/statistics/REWARD_AREA.png new file mode 100644 index 0000000000000000000000000000000000000000..8e1acbcf5065b33f5880db6db01bdd9cc94e8b88 GIT binary patch literal 3439 zcmeAS@N?(olHy`uVBq!ia0y~yULkk1LFQ8Dv3kHT#0|tgy2@DKYGZ+}e3+C(!v;j(R z7I;J!Gca%qgD@k*tT_@43_LeHT^vIyZoR$k$je~B!@RNVk7c^fva(HzPo}O0Ibsxy zhQOc=fd>5`cuj0U)8!0L)Jg z49vhh!oY^iB1Q|EW&&EU*@s&TPA6j3g5Ou@T8Ib;BrU{-9asxE6p=LJcNikz31}G& zyWx;jhhDk^C(z-Z%^MiZK#dAwV~0jrd_)8_B#>!Sq7L_DO3ScARQ|w>Fbala2pAk; X)n#V0c(gEcGRS+Lu6{1-oD!MLkk1LFQ8Dv3kHT#0|tgy2@DKYGZ+}e3+C(!v;j(R z7I;J!Gca%qgD@k*tT_@43|x;rT^vIyZoR#;k+Z>pr|sgL9sgyfUE?cCc&xQ?n}{g$ z_j?@m-vvEbfci(lXb9j8fjbkzYwdHG7!C+e<^(DdH~=IR8i487fq@yACK=d(d`2E1 z!30co3@pI($-t3-U^NKM0%{XDa6p1pr;!m|JBmR+oMr*_G4k+C*#Ao$*-#~=G+_o) z-PhOFFfbgbj%jGzb?Lb_Q0XWb4S~@R7!85Z5Eu=C(GVDEA@Hw=sklJmz{FRv`$1m! MboFyt=akR{08$E3K>z>% literal 0 HcmV?d00001 diff --git a/module/statistics/assets.py b/module/statistics/assets.py index 5698ea92a4..5cbce79e04 100644 --- a/module/statistics/assets.py +++ b/module/statistics/assets.py @@ -7,3 +7,5 @@ CAMPAIGN_BONUS = Button(area={'cn': (404, 149, 439, 166), 'en': (406, 150, 477, 162), 'jp': (404, 150, 476, 167), 'tw': (404, 149, 439, 166)}, color={'cn': (188, 195, 207), 'en': (199, 204, 212), 'jp': (207, 211, 218), 'tw': (188, 195, 207)}, button={'cn': (404, 149, 439, 166), 'en': (406, 150, 477, 162), 'jp': (404, 150, 476, 167), 'tw': (404, 149, 439, 166)}, file={'cn': './assets/cn/statistics/CAMPAIGN_BONUS.png', 'en': './assets/en/statistics/CAMPAIGN_BONUS.png', 'jp': './assets/jp/statistics/CAMPAIGN_BONUS.png', 'tw': './assets/cn/statistics/CAMPAIGN_BONUS.png'}) ENEMY_NAME = Button(area={'cn': (781, 283, 965, 322), 'en': (781, 283, 965, 322), 'jp': (781, 283, 965, 322), 'tw': (781, 283, 965, 322)}, color={'cn': (92, 102, 119), 'en': (92, 102, 119), 'jp': (92, 102, 119), 'tw': (92, 102, 119)}, button={'cn': (781, 283, 965, 322), 'en': (781, 283, 965, 322), 'jp': (781, 283, 965, 322), 'tw': (781, 283, 965, 322)}, file={'cn': './assets/cn/statistics/ENEMY_NAME.png', 'en': './assets/en/statistics/ENEMY_NAME.png', 'jp': './assets/jp/statistics/ENEMY_NAME.png', 'tw': './assets/tw/statistics/ENEMY_NAME.png'}) GET_ITEMS_ODD = Button(area={'cn': (628, 294, 653, 397), 'en': (628, 294, 653, 397), 'jp': (628, 294, 653, 397), 'tw': (628, 294, 653, 397)}, color={'cn': (98, 103, 121), 'en': (98, 103, 121), 'jp': (98, 103, 121), 'tw': (98, 103, 121)}, button={'cn': (628, 294, 653, 397), 'en': (628, 294, 653, 397), 'jp': (628, 294, 653, 397), 'tw': (628, 294, 653, 397)}, file={'cn': './assets/cn/statistics/GET_ITEMS_ODD.png', 'en': './assets/en/statistics/GET_ITEMS_ODD.png', 'jp': './assets/jp/statistics/GET_ITEMS_ODD.png', 'tw': './assets/tw/statistics/GET_ITEMS_ODD.png'}) +REWARD_AREA = Button(area={'cn': (395, 181, 900, 593), 'en': (395, 181, 900, 593), 'jp': (395, 181, 900, 593), 'tw': (395, 181, 900, 593)}, color={'cn': (255, 255, 255), 'en': (255, 255, 255), 'jp': (255, 255, 255), 'tw': (255, 255, 255)}, button={'cn': (395, 181, 900, 593), 'en': (395, 181, 900, 593), 'jp': (395, 181, 900, 593), 'tw': (395, 181, 900, 593)}, file={'cn': './assets/cn/statistics/REWARD_AREA.png', 'en': './assets/cn/statistics/REWARD_AREA.png', 'jp': './assets/cn/statistics/REWARD_AREA.png', 'tw': './assets/cn/statistics/REWARD_AREA.png'}) +REWARD_HEADER2 = Button(area={'cn': (394, 146, 400, 170), 'en': (394, 146, 400, 170), 'jp': (394, 146, 400, 170), 'tw': (394, 146, 400, 170)}, color={'cn': (242, 243, 244), 'en': (242, 243, 244), 'jp': (242, 243, 244), 'tw': (242, 243, 244)}, button={'cn': (394, 146, 400, 170), 'en': (394, 146, 400, 170), 'jp': (394, 146, 400, 170), 'tw': (394, 146, 400, 170)}, file={'cn': './assets/cn/statistics/REWARD_HEADER2.png', 'en': './assets/cn/statistics/REWARD_HEADER2.png', 'jp': './assets/cn/statistics/REWARD_HEADER2.png', 'tw': './assets/cn/statistics/REWARD_HEADER2.png'}) diff --git a/module/statistics/autosearch_reward.py b/module/statistics/autosearch_reward.py index 4b5e67ff65..5d51a15515 100644 --- a/module/statistics/autosearch_reward.py +++ b/module/statistics/autosearch_reward.py @@ -2,185 +2,63 @@ import cv2 import datetime import numpy as np -from module.base.button import ButtonGrid +from module.base.button import Button from module.base.timer import Timer from module.logger import logger from module.base.utils import crop -from module.handler.assets import AUTO_SEARCH_MENU_EXIT, AUTO_SEARCH_MENU_CONTINUE -from module.statistics.item import AmountOcr +from module.base.base import ModuleBase +from module.statistics.assets import REWARD_HEADER2, REWARD_AREA from module.statistics.azurstats import AzurStats -class AutosearchReward(ButtonGrid, Timer, AzurStats): - def wait_until_reward_stable(self, timeout=8.6, required_consecutive_matches=3, similarity_threshold=0.999): +class AutosearchReward(Button, Timer, AzurStats, ModuleBase): + + def __init__(self, config, device=None, **kwargs): """ - checks if all rewards have appeared, via first doing a predicted grid, 3 types to account for the Ui - change due to Meta xp, then verifies via amount ocr, after that waits till the reward area has no changes - e.g all drops appeared + ModuleBase needed this init + """ + ModuleBase.__init__(self, config, device) + + AzurStats.__init__(self, config) - timeout: may or may not need adjustment, after gathering data; 5.23s longest atm\\ - required_consecutive_matches: 3 seems a good compromise\\ - similarity_threshold: in testing the similarity ranged in 0.002- 0.01 ranges so opted for 0.999 + def wait_until_reward_stable(self, timeout=6.6, required_consecutive_matches=3): """ - logger.info('wait until area stable') + checks if all rewards have appeared, via offset matching header 2, after that waits till the reward area has no changes + e.g all drops appeared + timeout: may need adjustment after gathering data; 5.84s longest atm\\ + required_consecutive_matches: 3 seems a good compromise + """ try: timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") - # 1. Image handling separation, ocr uses non modified, area stable check uses modified + self.device.screenshot() - original_bgr = self.device.image.copy() - gray_image = cv2.cvtColor(original_bgr, cv2.COLOR_BGR2GRAY) - - # 2. Debugging/Reference visualization (BGR) - exit_btn = AUTO_SEARCH_MENU_EXIT._button - continue_btn = AUTO_SEARCH_MENU_CONTINUE._button - debug_img = original_bgr.copy() - cv2.rectangle(debug_img, (exit_btn[0], exit_btn[1]), (exit_btn[2], exit_btn[3]), (255,0,0), 2) - cv2.rectangle(debug_img, (continue_btn[0], continue_btn[1]), (continue_btn[2], continue_btn[3]), (0,0,255), 2) - self._save(debug_img, 'debug', f'{timestamp}_reference_points.png') - - # 3. Grid setup with single row, tested with EN, CN is fine due to same button, - #continue button shift less of an issue, but alot of the measures depend on exit - #TW might have grid too low and ocr area might miss the numbers area - #compared to EN/CN: exit width -42, height -9, continue width -3, height -5 - #JP gonna have issues due to not using a default exit asset, ui moved due to meta xp sidebar - #compared to EN/CN: exit width -1, height -1, but further on the left by 68px - #continue width -1, height the same, but further on the right side by 72px - GRID_PARAMS = { - 'button_shape': (64, 64), - 'grid_shape': (7, 1), # need to only check first row - 'delta': (72 + 2/3, 0), - 'name': 'REWARD_GRID' - } - exit_x, exit_y = exit_btn[0], exit_btn[1] - GRID_ORIGINS = [ - (exit_x + 22, exit_y - 417), # Grid 0 - Primary - (exit_x + 22 - 72, exit_y - 417), # Grid 1 - Fallback 1 (one button left) - (exit_x + 22 - 129, exit_y - 417) # Grid 2 - Fallback 2 - ] - - # 4. Enhanced grid verification logic - validated_grids = [] - - for i, (grid_x, grid_y) in enumerate(GRID_ORIGINS): - grid = ButtonGrid(origin=(grid_x, grid_y), **GRID_PARAMS) - matched = self._verify_grid_via_ocr(grid, i, original_bgr) - - if matched: - logger.info(f"Grid {i} validated") - validated_grids.append((i, grid_x, grid_y)) - - # Special case: If Grid 0 validates, also check Grid 1 - if i == 0 and len(GRID_ORIGINS) > 1: - logger.info("Grid 0 validated, checking Grid 1 for potentially more rewards...") - grid1_x, grid1_y = GRID_ORIGINS[1] - grid1 = ButtonGrid(origin=(grid1_x, grid1_y), **GRID_PARAMS) - grid1_matched = self._verify_grid_via_ocr(grid1, 1, original_bgr, check_first_button_only=True) - - if grid1_matched: - logger.info("Grid 1 also validated - using Grid 1 for wider coverage") - validated_grids.append((1, grid1_x, grid1_y)) - else: - logger.info("Grid 1 failed validation - sticking with Grid 0") - - break # Found at least one valid grid, proceed - # 5. Select the best grid (prefer Grid 1 over Grid 0 if both validate) - if not validated_grids: - logger.warning("All grid positions failed") + # 1. Detect header2 + if not self.appear(REWARD_HEADER2, offset=(200,0)): + logger.warning("Header not found within search area") return False - # Choose Grid 1 if available (better coverage), otherwise use the first validated grid - best_grid = None - for grid_info in validated_grids: - grid_idx, grid_x, grid_y = grid_info - if grid_idx == 1: # Prefer Grid 1 - best_grid = grid_info - break - - if best_grid is None: - best_grid = validated_grids[0] # Use first validated grid - - selected_idx, selected_x, selected_y = best_grid - logger.info(f"Using Grid {selected_idx} for monitoring (x={selected_x}, y={selected_y})") + # 2. Apply detected offset to reward area + REWARD_AREA.load_offset(REWARD_HEADER2) + logger.info(f"Applied offset: {REWARD_HEADER2._button_offset}") - # 6. Set up monitoring area with selected grid - continue_btn = AUTO_SEARCH_MENU_CONTINUE._button - monitor_area = ( - int(selected_x), - int(selected_y), - int(selected_x + (7 * 72.67)), - int(continue_btn[1]) - ) + # 3. Get adjusted coordinates + adjusted_area = REWARD_AREA.button - logger.info(f"DEBUG - Selected Grid {selected_idx}: x={selected_x}, y={selected_y}") - logger.info(f"DEBUG - Monitor area: {monitor_area}") - - self.device.image = gray_image - return self.reward_area_stable_check(monitor_area, timeout, required_consecutive_matches, similarity_threshold, timestamp) + # 4. Validate (prevent negative width) + if adjusted_area[0] >= adjusted_area[2]: + logger.warning(f"Invalid area after offset: {adjusted_area}") + return False + + # 5. stable area check + # similarity_threshold: in testing the similarity ranged in 0.002- 0.01 ranges so opted for 0.999 + return self.reward_area_stable_check(monitor_area=adjusted_area, timeout=timeout, required_matches=required_consecutive_matches,similarity_threshold=0.999, timestamp=timestamp) + except Exception as e: - logger.error(f'Stabilization failure: {str(e)}') + logger.error(f"Offset application failed: {e}") return False - def _verify_grid_via_ocr(self, grid, grid_idx, bgr_image, check_first_button_only=False): - """ - double checks if grid correct via the item amount on the bottom right of grid buttons - special handling for grid0 vs grid1 - """ - amount_ocr = AmountOcr([], threshold=96, name='Amount_ocr') - detected_amounts = [] - first_button_has_amount = False - - for btn_idx, btn in enumerate(grid.buttons): - try: - # 1. Get button image (keep as BGR) - button_img = crop(bgr_image, btn.area) - if button_img is None or button_img.size == 0: - continue - - # 2. Extract amount area (bottom-right portion) - h, w = button_img.shape[:2] - amount_area = (int(w*0.6), int(h*0.8), w, h) - amount_img = crop(button_img, amount_area) - - # 3. Ensure BGR format for OCR - if len(amount_img.shape) == 2: - amount_img = cv2.cvtColor(amount_img, cv2.COLOR_GRAY2BGR) - - # 4. OCR just like the one in ItemGrid in item.py - amount = amount_ocr.ocr([amount_img], direct_ocr=True)[0] - if amount > 0: - detected_amounts.append(amount) - logger.info(f"Grid {grid_idx}, Button {btn_idx}: amount={amount}") - - # Track if first button (index 0) has amount - if btn_idx == 0: - first_button_has_amount = True - - except Exception as e: - logger.warning(f"Grid {grid_idx}, Button {btn_idx} check failed: {str(e)}") - continue - - # Determine validation result - total_detected = len(detected_amounts) - - if check_first_button_only: - # For Grid 1 validation: only valid if first button has amount - if first_button_has_amount: - logger.info(f"Grid {grid_idx} summary: First button validated with amount - Grid 1 captures additional reward!") - return True - else: - logger.info(f"Grid {grid_idx} summary: First button has no amount - Grid 1 offers no advantage") - return False - else: - # Standard validation: any button with amount is sufficient - if total_detected > 0: - logger.info(f"Grid {grid_idx} summary: {total_detected} buttons with amounts detected") - return True - else: - logger.info(f"Grid {grid_idx} summary: No amounts detected") - return False - def reward_area_stable_check(self, monitor_area, timeout, required_matches, similarity_threshold, timestamp=""): """ checks if the previously assigned area has any changes @@ -192,8 +70,10 @@ def reward_area_stable_check(self, monitor_area, timeout, required_matches, simi consecutive_matches = 0 check_count = 0 - last_luma = crop(self.device.image, monitor_area) - self._save(last_luma, 'debug/dropstats', f'{timestamp}_0_initial.png') + initial_gray = cv2.cvtColor(self.device.image, cv2.COLOR_BGR2GRAY) + last_luma = crop(initial_gray, monitor_area) + #visual debug + #self._save(last_luma, 'debug/dropstats', f'{timestamp}_0_initial.png') while True: current_time = time.time() @@ -210,7 +90,8 @@ def reward_area_stable_check(self, monitor_area, timeout, required_matches, simi self.device.screenshot() current_luma = cv2.cvtColor(self.device.image, cv2.COLOR_BGR2GRAY) current_luma = crop(current_luma, monitor_area) - self._save(current_luma, 'debug/dropstats', f'{timestamp}_{check_count}_check.png') + #visual debug + #self._save(current_luma, 'debug/dropstats', f'{timestamp}_{check_count}_check.png') res = cv2.matchTemplate(last_luma, current_luma, cv2.TM_CCOEFF_NORMED) similarity = cv2.minMaxLoc(res)[1] @@ -224,7 +105,8 @@ def reward_area_stable_check(self, monitor_area, timeout, required_matches, simi stabilization_time = time.time() - stabilization_start total_time = time.time() - start_time logger.info(f'Area stabilized after {total_time:.2f}s (took {stabilization_time:.2f}s to confirm)') - self._save(current_luma, 'debug/dropstats', f'{timestamp}_final_stable.png') + #visual debug + #self._save(current_luma, 'debug/dropstats', f'{timestamp}_final_stable.png') return True else: consecutive_matches = 0 From 86a0e742c6a058c857ec3fdb13bf27bbfe9cdc40 Mon Sep 17 00:00:00 2001 From: dragonheart107 Date: Thu, 21 Aug 2025 21:49:32 +0200 Subject: [PATCH 6/6] Fix: dropstats --- module/statistics/autosearch_reward.py | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/module/statistics/autosearch_reward.py b/module/statistics/autosearch_reward.py index 5d51a15515..a82217faac 100644 --- a/module/statistics/autosearch_reward.py +++ b/module/statistics/autosearch_reward.py @@ -1,24 +1,11 @@ import time import cv2 import datetime -import numpy as np -from module.base.button import Button -from module.base.timer import Timer from module.logger import logger from module.base.utils import crop -from module.base.base import ModuleBase from module.statistics.assets import REWARD_HEADER2, REWARD_AREA -from module.statistics.azurstats import AzurStats -class AutosearchReward(Button, Timer, AzurStats, ModuleBase): - - def __init__(self, config, device=None, **kwargs): - """ - ModuleBase needed this init - """ - ModuleBase.__init__(self, config, device) - - AzurStats.__init__(self, config) +class AutosearchReward(): def wait_until_reward_stable(self, timeout=6.6, required_consecutive_matches=3): """ @@ -72,8 +59,6 @@ def reward_area_stable_check(self, monitor_area, timeout, required_matches, simi initial_gray = cv2.cvtColor(self.device.image, cv2.COLOR_BGR2GRAY) last_luma = crop(initial_gray, monitor_area) - #visual debug - #self._save(last_luma, 'debug/dropstats', f'{timestamp}_0_initial.png') while True: current_time = time.time() @@ -90,8 +75,6 @@ def reward_area_stable_check(self, monitor_area, timeout, required_matches, simi self.device.screenshot() current_luma = cv2.cvtColor(self.device.image, cv2.COLOR_BGR2GRAY) current_luma = crop(current_luma, monitor_area) - #visual debug - #self._save(current_luma, 'debug/dropstats', f'{timestamp}_{check_count}_check.png') res = cv2.matchTemplate(last_luma, current_luma, cv2.TM_CCOEFF_NORMED) similarity = cv2.minMaxLoc(res)[1] @@ -105,8 +88,7 @@ def reward_area_stable_check(self, monitor_area, timeout, required_matches, simi stabilization_time = time.time() - stabilization_start total_time = time.time() - start_time logger.info(f'Area stabilized after {total_time:.2f}s (took {stabilization_time:.2f}s to confirm)') - #visual debug - #self._save(current_luma, 'debug/dropstats', f'{timestamp}_final_stable.png') + return True else: consecutive_matches = 0