PamGram/modules/gacha/system.py
2022-10-15 17:13:19 +08:00

157 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import secrets
from typing import Tuple, List
from modules.gacha.banner import GachaBanner
from modules.gacha.error import GachaInvalidTimes, GachaIllegalArgument
from modules.gacha.player.info import PlayerGachaBannerInfo
from modules.gacha.player.info import PlayerGachaInfo
from modules.gacha.pool import BannerPool
class BannerSystem:
fallback_items5_pool2_default: Tuple[int] = (11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502)
fallback_items4_pool2_default: Tuple[int] = (
11401,
11402,
11403,
11405,
12401,
12402,
12403,
12405,
13401,
13407,
14401,
14402,
14403,
14409,
15401,
15402,
15403,
15405,
)
def do_pulls(self, player_gacha_info: PlayerGachaInfo, banner: GachaBanner, times: int) -> List[int]:
item_list: List[int] = []
if times not in (10, 1):
raise GachaInvalidTimes
gacha_info = player_gacha_info.get_banner_info(banner)
gacha_info.add_total_pulls(times)
pools = BannerPool(banner)
for _ in range(times):
item_id = self.do_pull(banner, gacha_info, pools)
item_list.append(item_id)
return item_list
def do_pull(self, banner: GachaBanner, gacha_info: PlayerGachaBannerInfo, pools: BannerPool) -> int:
gacha_info.inc_pity_all()
# 对玩家卡池信息的计数全部加1方便计算
# 就这么说吧如果你加之前比已经四星9发没出那么这个能让你下次权重必定让你出四星的角色
# 而不是使用 if gacha_info.pity4 + 1 >= 10 的形式计算
weights = [banner.get_weight(5, gacha_info.pity5), banner.get_weight(4, gacha_info.pity4), 10000]
leval_won = 5 - self.draw_roulette(weights, 10000)
# 根据权重信息获得当前所抽到的星级
if leval_won == 5:
# print(f"已经获得五星,当前五星权重为{weights[0]}")
gacha_info.pity5 = 0
return self.do_rare_pull(
pools.rate_up_items5, pools.fallback_items5_pool1, pools.fallback_items5_pool2, 5, banner, gacha_info
)
elif leval_won == 4:
gacha_info.pity4 = 0
return self.do_rare_pull(
pools.rate_up_items4, pools.fallback_items4_pool1, pools.fallback_items4_pool2, 4, banner, gacha_info
)
else:
return self.get_random(banner.fallback_items3)
@staticmethod
def draw_roulette(weights, cutoff: int) -> int:
total = 0
for weight in weights:
if weight < 0:
raise GachaIllegalArgument("Weights must be non-negative!")
total += weight
secrets_random = secrets.SystemRandom()
roll = int(secrets_random.random() * min(total, cutoff))
sub_total = 0
for index, value in enumerate(weights):
sub_total += value
if roll < sub_total:
return index
return 0
def do_rare_pull(
self,
featured: List[int],
fallback1: List[int],
fallback2: List[int],
rarity: int,
banner: GachaBanner,
gacha_info: PlayerGachaBannerInfo,
) -> int:
# 以下是防止点炒饭
epitomized = (banner.has_epitomized()) and (rarity == 5) and (gacha_info.wish_item_id != 0) # 判断定轨信息是否正确
pity_epitomized = gacha_info.failed_chosen_item_pulls >= banner.wish_max_progress # 判断定轨值
pity_featured = gacha_info.get_failed_featured_item_pulls(rarity) >= 1 # 通过UP值判断当前是否为UP
roll_featured = self.random_range(1, 100) <= banner.get_event_chance(rarity) # 随机判断当前是否为UP
pull_featured = pity_featured or roll_featured # 获得最终是否为 UP
if epitomized and pity_epitomized: # 给武器用的定轨代码
gacha_info.set_failed_featured_item_pulls(rarity, 0)
item_id = gacha_info.wish_item_id
elif pull_featured and featured: # 是UP角色
gacha_info.set_failed_featured_item_pulls(rarity, 0)
item_id = self.get_random(featured)
else: # 寄
gacha_info.add_failed_featured_item_pulls(rarity, 1)
item_id = self.do_fallback_rare_pull(fallback1, fallback2, rarity, banner, gacha_info)
if epitomized:
if item_id == gacha_info.wish_item_id: # 判断当前UP是否为定轨的UP
gacha_info.failed_chosen_item_pulls = 0 # 是的话清除定轨
else:
gacha_info.add_failed_chosen_item_pulls(1)
return item_id
def do_fallback_rare_pull(
self,
fallback1: List[int],
fallback2: List[int],
rarity: int,
banner: GachaBanner,
gacha_info: PlayerGachaBannerInfo,
) -> int:
if len(fallback1) < 1:
if len(fallback2) < 1:
return self.get_random(
self.fallback_items5_pool2_default if rarity == 5 else self.fallback_items4_pool2_default
)
else:
return self.get_random(fallback2)
elif len(fallback2) < 1:
return self.get_random(fallback1)
else:
pity_pool1 = banner.get_pool_balance_weight(rarity, gacha_info.get_pity_pool(rarity, 1))
pity_pool2 = banner.get_pool_balance_weight(rarity, gacha_info.get_pity_pool(rarity, 2))
if pity_pool1 >= pity_pool2:
chosen_pool = 1 + self.draw_roulette((pity_pool1, pity_pool2), 10000)
else:
chosen_pool = 2 - self.draw_roulette((pity_pool2, pity_pool1), 10000)
if chosen_pool == 1:
gacha_info.set_pity_pool(rarity, 1, 0)
return self.get_random(fallback1)
gacha_info.set_pity_pool(rarity, 2, 0)
return self.get_random(fallback2)
@staticmethod
def get_random(items) -> int:
secrets_random = secrets.SystemRandom()
roll = int(secrets_random.random() * len(items))
return items[roll]
@staticmethod
def random_range(_mix: int, _max: int) -> int:
secrets_random = secrets.SystemRandom()
return int(secrets_random.uniform(_mix, _max))