-
Notifications
You must be signed in to change notification settings - Fork 1k
Add: event shop #5161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Add: event shop #5161
Conversation
|
seems overkill, almost all shop UIs are the same. I think can almost use think back then event shop were a hassle, but maybe a little bit easier now |
The dedicated buying logic for event shop is to deal with the problem that event pt is precious (meaning one cannot buy all the items easily). Current logic is to buy and stop whenever we cannot afford all. This makes it more possible to successfully buy all the things you want, and make it possible to change EventPtLimit in the future if wanted. However this also make it necessary to scan the whole shop, record all items and then judge.
I don’t think the difficulty drops that much, but of course you can have your logic and implementation. I am not sure how you will implement it but have fun! |
Ah good catch, you are right. I overlooked that. Especially with UR trade in, sometimes you don't need all 300 but that interface just uses the hit max button. to do that haha But that might be exception case where user has to do it themselves.
Haha actually have no idea how the OpSi port shop does its thing and able to memorize positions especially with scroll bar movement in mind. I did implement a version that just relied on buying everything in the last week right before the port shop expired, that has its own flaws too but worked pretty well. I would think the same principle could work for events too just maybe using a custom date to configure then increment refresh up until the end of event. Cool none the less, this is definitely a large feature. Maybe making this its own task might be less confusing and that way its attached to event category and can be enabled or disabled automatically only when the event task is enabled. Or can just rely on user to do so manually. |
Good idea! But let's wait until this pull request makes it way into Alas or Lme asked for it. It is usually the opposite case (at least for me) where he (she) loses interest in the halfway and never reviews the PR any more. |
module/config/argument/argument.yaml
Outdated
| CustomFilter: |- | ||
| EquipUR > EquipSSR > GachaTicket | ||
| > DR > PR > Array > Chip > CatT3 | ||
| > Meta > SkinBox | ||
| > Oil > Coin > FoodT1 | ||
| > AugmentCore > AugmentEnhanceT2 > AugmentChangeT2 > AugmentChangeT1 | ||
| > Cube > Medal > ExpBookT1 | ||
| > CatT2 > CatT1 > PlateGeneralT3 > PlateT3 > BoxT4 | ||
| > ShipSSR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ShipSSR 在未获取的情况下应该是第一个兑换的
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
这是自选过滤器,如果不满意可以修改。
另见下面对购买部分逻辑的说明。
这里也没有写ShipUR,因为这些都是特殊优先处理的。
| while ships_to_buy: | ||
| urpt_needed = sum([item.price for item in ships_to_buy]) | ||
| if current_urpt >= urpt_needed: | ||
| for item in ships_to_buy: | ||
| self.event_shop_buy_item(item) | ||
| logger.info(f"Successfully bought ship items: {[str(item) for item in ships_to_buy]}") | ||
| break |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
筛选部分的代码和购买部分的耦合太严重了。参考大世界商店的实现,先进行一轮完整的物品扫描,然后计算出准备购买的指导列表,记住物品的相对顺序,最后遍历指导列表尝试购买
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
185行进行了完整的物品扫描。
参照191行至206行,这里主要进行准备购买的列表的计算。
有几个问题导致不能直接一次得出所有物品的相对顺序,而是需要动态调整:
- 对于用金pt购买的彩pt,相对顺序就比较难处理:比如说不知道该不该买,应该买多少个,会不会买多了;
- 对于用彩pt购买的金币,只靠过滤器很容易放在中间位置,而实际上一般不应该优先购买,需要特殊处理。
- 对于未解锁的金pt金船,购买金船之后金pt金船的优先级就降低了,所以应当置后;
综上,目前采用了三步走的方式,首先处理彩pt对应的物品(handle_items_related_with_urpt,处理彩船彩pt和金币),其次处理未解锁的金船(handle_unobtained_items,会将买剩下的金船扔回过滤器),最后再按照过滤器过滤剩余商品。
209行开始对物品的指导列表进行遍历和购买。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- 中间代币应该是一个内部状态,不应该存在于过滤器中,脚本的作用是抹平这个故意恶心人的中间代币。进行首轮扫描后就可以计算需要换多少中间代币,然后插入到指导列表中的彩船之前,如果需要的pt不足以兑换指导的中间代币数量,就认为是pt不足,跳过列表中后续所有需要pt的项目
- 角色应该也有variant,类似 ShipUR-unobtained, ShipUR-obtained, ShipUR, ShipSSR也有类似变体,就可以在过滤器中筛选属性了
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
中间代币应该是一个内部状态,不应该存在于过滤器中,脚本的作用是抹平这个故意恶心人的中间代币。
同意,脚本应当抹平中间代币的作用。但目前中间代币并不参与最后的过滤器的筛选。
角色应该也有variant,类似 ShipUR-unobtained, ShipUR-obtained, ShipUR, ShipSSR也有类似变体,就可以在过滤器中筛选属性了
带变体的话那就需要对过滤器做防呆处理了,比如说同种舰船的obtained变体不能出现在unobtained的前面,做这些处理方式的代价我觉得比目前处理的方式要高,不做的话则会导致购买结果和玩家意愿不符。目前采用的方式是类似通用商店购买外观装备箱的方式,在处理unobtained变体时如果当前已经解锁该舰船则直接交给过滤器进行过滤。
进行首轮扫描后就可以计算需要换多少中间代币,然后插入到指导列表中的彩船之前
过滤器无法判断中间代币购买的金币和普通代币购买的金币之间的区别,不做特殊判断的话会出现提前购买中间代币购买的金币的问题。做特殊判断的话那就不如直接利用目前的逻辑进行判断了。
总的来说,目前计算出的指导列表的效果等价于你所设想的全部用过滤器表示的结果:(URpt >) ShipUR > ShipSSR-unobtained > ... > (URpt >) Coin_URpt,这里省略了现有过滤器里面的全部内容。
这种处理方式的优点在于鲁棒性强(都依靠逻辑判断),能减少用户的学习成本。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
不一样,购买物品必须只能有一次循环,每买一个物品都需要重新识别当前页面的物品、重新识别货币数量、重新生成指导列表,然后购买列表中的只第一个物品,这个逻辑已经非常固定了,参考ShopClerk.shop_buy。
不能持有一个固定的列表然后挨个pop然后购买,不能认为物品列表在购买之后是不变的,不能认为货币数量必然会减少预期的数量。
def shop_buy(self):
for _ in range(12):
logger.hr('Shop buy', level=2)
items = self.shop_get_items()
self.shop_currency()
if self._currency <= 0:
logger.warning(f'Current funds: {self._currency}, stopped')
return False
item = self.shop_get_item_to_buy(items)
if item is None:
logger.info('Shop buy finished')
return True
else:
self.shop_buy_execute(item)
continue
logger.warning('Too many items to buy, stopped')
return True第一次扫描会得到一个全局列表,主要用来确定物品的顺序,然后每次购买物品前局部更新物品信息,如果不在当前页面那么滑动,如果发送了滑动重新更新局部物品信息。
ShipUR-obtained 不能在 ShipUR-unobtained,否则打印warning然后忽略,这不需要多少代码
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
购买物品必须只能有一次循环
购买的时候只有一个循环,绝大多数物品也只会被遍历一次。
每买一个物品都需要重新识别当前页面的物品、重新识别货币数量、重新生成指导列表,然后购买列表中的只第一个物品,这个逻辑已经非常固定了,参考ShopClerk.shop_buy。
这是勋章商店的逻辑,只在单页面里面排序和生成指导列表,相当于只看局部,而只看局部相当于假定高价值物品都在第一行,这与实际可能的过滤器不符。目前按照大世界港口商店的逻辑,扫描多页内容后生成完整的指导列表再购买。
不赞同使用勋章商店及现有小卖部其他商店的逻辑。活动商店不是勋章商店,不能假定玩家拥有足够代币来搬空所有需要的物品。
不能持有一个固定的列表然后挨个pop然后购买
我寻思夏克菲当年写港口商店就是这么写的:
AzurLaneAutoScript/module/os_shop/shop.py
Lines 202 to 252 in 7dd3479
| def handle_port_supply_buy(self) -> bool: | |
| """ | |
| Returns: | |
| bool: True if success to buy any or no items found. | |
| False if not enough coins to buy any. | |
| Pages: | |
| in: PORT_SUPPLY_CHECK | |
| """ | |
| items = self.scan_all() | |
| if not len(items): | |
| logger.warning('Empty OS shop.') | |
| return False | |
| items = self.items_filter_in_os_shop(items) | |
| if not len(items): | |
| logger.warning('Nothing to buy.') | |
| return False | |
| self.os_shop_get_coins() | |
| skip_get_coins = True | |
| items.reverse() | |
| count = 0 | |
| while len(items): | |
| logger.hr('OpsiShop buy', level=2) | |
| item = items.pop() | |
| if not skip_get_coins: | |
| self.os_shop_get_coins() | |
| if item.price > self.get_currency_coins(item): | |
| logger.info(f'Not enough coins to buy item: {item.name}, skip.') | |
| if self.is_coins_both_not_enough(): | |
| logger.info('Not enough coins to buy any items, stop.') | |
| break | |
| continue | |
| logger.info(f'Buying item: {item.name}. In shop {item.shop_index + 1}. At pos {item.scroll_pos:.2f}.') | |
| self.os_shop_side_navbar_ensure(upper=item.shop_index + 1) | |
| OS_SHOP_SCROLL.set(item.scroll_pos, main=self, skip_first_screenshot=False) | |
| _item = self.os_shop_get_items_to_buy(name=item.name, price=item.price) | |
| if _item is None: | |
| logger.warning(f'Item {item.name} not found in shop {item.shop_index + 1} at pos {item.scroll_pos:.2f}, skip.') | |
| continue | |
| if not self.check_item_count(_item): | |
| logger.warning(f'Get {_item.name} count error, skip.') | |
| continue | |
| if self.os_shop_buy_execute(_item): | |
| logger.info(f'Bought item: {_item.name}.') | |
| skip_get_coins = False | |
| count += 1 | |
| else: | |
| logger.warning(f'Item {_item.name} cant be bought, skip.') | |
| self.device.click_record.clear() | |
| logger.info(f'Bought {f"{count} items" if count else "nothing"} in port.') | |
| return True |
我是这么写的:
AzurLaneAutoScript/module/shop_event/shop_event.py
Lines 207 to 233 in 8be629a
| self.get_current_pts() | |
| logger.attr("Pt_preserved", self.pt_preserved) | |
| for item in items: | |
| logger.hr(f"Attempting to buy item: {str(item)}", level=3) | |
| affordable_amount = self.calculate_affordable_amount(item) | |
| if affordable_amount <= 0: | |
| logger.warning(f"Cannot afford to buy any of item: {str(item)}.") | |
| if self.is_event_ended: | |
| logger.info("Event is ended, skip this item and continue to try buying other items.") | |
| continue | |
| else: | |
| logger.info("Event is not ended, stopping further purchases to avoid overspending.") | |
| break | |
| elif affordable_amount < item.count: | |
| logger.warning(f"Can only afford to buy {affordable_amount} of item: {str(item)}.") | |
| self.event_shop_buy_item(item, amount=affordable_amount) | |
| if self.is_event_ended: | |
| logger.info("Event is ended, continue to try buying other items.") | |
| self.get_current_pts() | |
| continue | |
| else: | |
| logger.info("Event is not ended, stopping further purchases to avoid overspending.") | |
| break | |
| else: | |
| self.event_shop_buy_item(item) | |
| logger.info(f"Successfully bought item: {str(item)}") | |
| self.get_current_pts() |
不能认为物品列表在购买之后是不变的
局部物品列表的变化只会发生在获取船只的前后,其余情况都是留在原地的,而且也通过ItemNotFoundError的方式判断是否发生变化了,仅在发生变化的时候重新扫描全局物品列表(重新开始任务)
不能认为货币数量必然会减少预期的数量
没有这么认为。每次购买完后(并且还能买下一个时)都会重新检查代币数量。
AzurLaneAutoScript/module/shop_event/shop_event.py
Lines 231 to 233 in 8be629a
| self.event_shop_buy_item(item) | |
| logger.info(f"Successfully bought item: {str(item)}") | |
| self.get_current_pts() |
然后每次购买物品前局部更新物品信息,如果不在当前页面那么滑动,如果发送了滑动重新更新局部物品信息。
没有理解你的意图。目前根据EventShopItem.scroll这一属性决定需要在哪个位置检查物品局部信息,并发送对应的滑动。
ShipUR-obtained 不能在 ShipUR-unobtained,否则打印warning然后忽略,这不需要多少代码
这个写法需要Filter支持tag的处理,而且没办法处理不同代币的物资的区分。
This comment was marked as outdated.
This comment was marked as outdated.
1bd330f to
005c3f0
Compare
|
旧方法已经适配新界面了。 |
288335d to
78a3dad
Compare
a5bf783 to
3047fa1
Compare
|
看上去review已经烂尾了,目前仅供有需要的人自行拉取,选项位于活动-活动商店。 |
尝试适配活动商店。希望这次review别烂尾了。