diff --git a/module/config/argument/args.json b/module/config/argument/args.json index 68eea4023..a4604e51e 100644 --- a/module/config/argument/args.json +++ b/module/config/argument/args.json @@ -6959,6 +6959,10 @@ "FortChoreMeowfficer": { "type": "checkbox", "value": true + }, + "OverflowCoins": { + "type": "input", + "value": -1 } }, "MeowfficerTrain": { diff --git a/module/config/argument/argument.yaml b/module/config/argument/argument.yaml index 9594a1377..6bcfb044a 100644 --- a/module/config/argument/argument.yaml +++ b/module/config/argument/argument.yaml @@ -422,6 +422,7 @@ BuyFurniture: Meowfficer: BuyAmount: 1 FortChoreMeowfficer: true + OverflowCoins: -1 MeowfficerTrain: Enable: false Mode: diff --git a/module/config/config_generated.py b/module/config/config_generated.py index 4b41f0a38..feb404576 100644 --- a/module/config/config_generated.py +++ b/module/config/config_generated.py @@ -225,6 +225,7 @@ class GeneratedConfig: # Group `Meowfficer` Meowfficer_BuyAmount = 1 Meowfficer_FortChoreMeowfficer = True + Meowfficer_OverflowCoins = -1 # Group `MeowfficerTrain` MeowfficerTrain_Enable = False diff --git a/module/config/i18n/en-US.json b/module/config/i18n/en-US.json index 4670994ec..1c2123221 100644 --- a/module/config/i18n/en-US.json +++ b/module/config/i18n/en-US.json @@ -1547,6 +1547,10 @@ "FortChoreMeowfficer": { "name": "Do Fort Chores", "help": "Completes all 3 chores; clean, feed, and play with meowfficer to get fort exp" + }, + "OverflowCoins": { + "name": "Buy Boxes On Coin Overflow", + "help": "When coins are above this value, keep buying boxes until coins drop to it or boxes run out; -1 to disable" } }, "MeowfficerTrain": { diff --git a/module/config/i18n/ja-JP.json b/module/config/i18n/ja-JP.json index 0540a637d..15730cff0 100644 --- a/module/config/i18n/ja-JP.json +++ b/module/config/i18n/ja-JP.json @@ -1547,6 +1547,10 @@ "FortChoreMeowfficer": { "name": "Meowfficer.FortChoreMeowfficer.name", "help": "Meowfficer.FortChoreMeowfficer.help" + }, + "OverflowCoins": { + "name": "Meowfficer.OverflowCoins.name", + "help": "Meowfficer.OverflowCoins.help" } }, "MeowfficerTrain": { diff --git a/module/config/i18n/zh-CN.json b/module/config/i18n/zh-CN.json index 182d9d696..20b540822 100644 --- a/module/config/i18n/zh-CN.json +++ b/module/config/i18n/zh-CN.json @@ -1547,6 +1547,10 @@ "FortChoreMeowfficer": { "name": "喵窝互动", "help": "清扫、喂食和逗猫" + }, + "OverflowCoins": { + "name": "金币溢出时购买猫箱", + "help": "当金币高于此值时,继续购买猫箱直至金币降至此值或猫箱售罄;-1 关闭" } }, "MeowfficerTrain": { diff --git a/module/config/i18n/zh-TW.json b/module/config/i18n/zh-TW.json index ca7c61b79..6b286141f 100644 --- a/module/config/i18n/zh-TW.json +++ b/module/config/i18n/zh-TW.json @@ -1547,6 +1547,10 @@ "FortChoreMeowfficer": { "name": "喵窩互動", "help": "清掃、餵食和逗貓" + }, + "OverflowCoins": { + "name": "金幣溢出時購買貓箱", + "help": "當金幣高於此值時,繼續購買貓箱直至金幣降至此值或貓箱售罄;-1 關閉" } }, "MeowfficerTrain": { diff --git a/module/meowfficer/buy.py b/module/meowfficer/buy.py index 1be738351..0b354708a 100644 --- a/module/meowfficer/buy.py +++ b/module/meowfficer/buy.py @@ -13,43 +13,75 @@ MEOWFFICER_COINS = Digit(OCR_MEOWFFICER_COINS, letter=(99, 69, 41), threshold=64 class MeowfficerBuy(MeowfficerBase): - def meow_choose(self, count) -> bool: + def meow_get_buy_count(self) -> int: """ + OCR remaining buys and coins, combine with user configs to decide + how many meowfficer boxes to buy this run. + + Baseline: buy up to Meowfficer_BuyAmount per day. + Overflow: when Meowfficer_OverflowCoins is not -1 and current coins + exceed it, keep buying extra boxes until coins drop to threshold or + today's quota runs out. The 1st box per day is free. + + Pages: + in: page_meowfficer + + Returns: + int: 0 to BUY_MAX, number of boxes to buy now. + """ + self.device.screenshot() + remain, bought, total = MEOWFFICER.ocr(self.device.image) + coins = MEOWFFICER_COINS.ocr(self.device.image) + logger.attr('Meowfficer_remain', remain) + logger.attr('Meowfficer_coins', coins) + + if total != BUY_MAX: + logger.warning(f'Invalid meowfficer buy limit: {total}, revise to {BUY_MAX}') + total = BUY_MAX + bought = total - remain + + today_left = max(0, total - bought) + if today_left <= 0: + logger.info(f'Already bought {bought}/{total} today, stopped') + return 0 + + # Baseline buy + baseline = min(max(0, self.config.Meowfficer_BuyAmount - bought), today_left) + + # Overflow buy + overflow_th = self.config.Meowfficer_OverflowCoins + extra = 0 + if overflow_th != -1 and coins > overflow_th: + extra = -(-(coins - overflow_th) // BUY_PRIZE) + extra = min(extra, today_left - baseline) + extra = max(0, extra) + + count = baseline + extra + + # Cap by affordable coins, the 1st box per day is free + free = 1 if bought == 0 else 0 + affordable = coins // BUY_PRIZE + free + if count > affordable: + logger.info(f'Current coins only afford to buy {affordable}') + count = affordable + + logger.info( + f'Meowfficer buy plan: count={count}, baseline={baseline}, ' + f'overflow={extra}, bought={bought}/{total}, coins={coins}' + ) + return count + + def meow_choose(self, count) -> None: + """ + Navigate to MEOWFFICER_BUY and set buy index to `count`. + Pages: in: page_meowfficer out: MEOWFFICER_BUY Args: - count (int): 0 to 15. - - Returns: - bool: If success. + count (int): 1 to BUY_MAX. """ - remain, bought, total = MEOWFFICER.ocr(self.device.image) - logger.attr('Meowfficer_remain', remain) - - # Check buy status - if total != BUY_MAX: - logger.warning(f'Invalid meowfficer buy limit: {total}, revise to {BUY_MAX}') - total = BUY_MAX - bought = total - remain - if bought > 0: - if bought >= count: - logger.info(f'Already bought {bought} today, stopped') - return False - else: - count -= bought - logger.info(f'Already bought {bought} today, only need to buy {count} more') - - # Check coins - coins = MEOWFFICER_COINS.ocr(self.device.image) - if (coins < BUY_PRIZE) and (remain < total): - logger.info('Not enough coins to buy one, stopped') - return False - elif (count - int(remain == total)) * BUY_PRIZE > coins: - count = coins // BUY_PRIZE + int(remain == total) - logger.info(f'Current coins only enough to buy {count}') - self.meow_enter(MEOWFFICER_BUY_ENTER, check_button=MEOWFFICER_BUY) # info_bar may covers OCR_MEOWFFICER_CHOOSE, @@ -59,7 +91,6 @@ class MeowfficerBuy(MeowfficerBase): self.ui_ensure_index(count, letter=MEOWFFICER_CHOOSE, prev_button=MEOWFFICER_BUY_PREV, next_button=MEOWFFICER_BUY_NEXT, skip_first_screenshot=True) - return True def meow_confirm(self, skip_first_screenshot=True) -> None: """ @@ -68,7 +99,6 @@ class MeowfficerBuy(MeowfficerBase): out: page_meowfficer """ # Here uses a simple click, to avoid clicking MEOWFFICER_BUY multiple times. - # Retry logic is in meow_buy() logger.hr('Meow confirm') executed = False with self.stat.new( @@ -108,19 +138,17 @@ class MeowfficerBuy(MeowfficerBase): if self.match_template_color(MEOWFFICER_BUY_ENTER, offset=(20, 20)): break - def meow_buy(self) -> bool: + def meow_buy(self) -> None: """ + Buy meowfficer boxes according to baseline and optional overflow plan. + Pages: in: page_meowfficer out: page_meowfficer """ logger.hr('Meowfficer buy', level=1) - - for _ in range(3): - if self.meow_choose(count=self.config.Meowfficer_BuyAmount): - self.meow_confirm() - else: - return True - - logger.warning('Too many trial in meowfficer buy, stopped.') - return False + count = self.meow_get_buy_count() + if count <= 0: + return + self.meow_choose(count) + self.meow_confirm() diff --git a/module/meowfficer/meowfficer.py b/module/meowfficer/meowfficer.py index 8a29aa8f4..fb93b97c9 100644 --- a/module/meowfficer/meowfficer.py +++ b/module/meowfficer/meowfficer.py @@ -34,6 +34,7 @@ class RewardMeowfficer(MeowfficerBuy, MeowfficerFort, MeowfficerTrain): out: page_meowfficer """ if self.config.Meowfficer_BuyAmount <= 0 \ + and self.config.Meowfficer_OverflowCoins == -1 \ and not self.config.Meowfficer_FortChoreMeowfficer \ and not self.config.MeowfficerTrain_Enable: self.config.Scheduler_Enable = False @@ -42,7 +43,8 @@ class RewardMeowfficer(MeowfficerBuy, MeowfficerFort, MeowfficerTrain): self.ui_ensure(page_meowfficer) self.wait_meowfficer_buttons() # Wait for the ui to load fully - if self.config.Meowfficer_BuyAmount > 0: + if self.config.Meowfficer_BuyAmount > 0 \ + or self.config.Meowfficer_OverflowCoins != -1: self.meow_buy() if self.config.Meowfficer_FortChoreMeowfficer: self.meow_fort()