Gacha System implementation #1
4 changed files with 603 additions and 8 deletions
|
@ -41,3 +41,4 @@ tracing-bunyan-formatter.workspace = true
|
|||
common.workspace = true
|
||||
data.workspace = true
|
||||
proto.workspace = true
|
||||
chrono = { version = "0.4.38", features = ["serde"] }
|
||||
|
|
|
@ -1,13 +1,234 @@
|
|||
use data::gacha::{gacha_config::*, global_gacha_config};
|
||||
|
||||
use proto::*;
|
||||
use super::*;
|
||||
use chrono::{DateTime, Local};
|
||||
|
||||
pub async fn on_get_gacha_data(
|
||||
_session: &NetSession,
|
||||
_player: &mut Player,
|
||||
req: GetGachaDataCsReq,
|
||||
) -> NetResult<GetGachaDataScRsp> {
|
||||
Ok(GetGachaDataScRsp {
|
||||
if req.gacha_type != 3 {
|
||||
// tracing::info!("non-supported gacha type {}", body.gacha_type);
|
||||
Ok(GetGachaDataScRsp {
|
||||
retcode: Retcode::RetSucc.into(),
|
||||
gacha_type: req.gacha_type,
|
||||
gacha_data: Some(GachaData::default()),
|
||||
})
|
||||
} else {
|
||||
// tracing::info!("construct gacha info");
|
||||
Ok(GetGachaDataScRsp {
|
||||
retcode: Retcode::RetSucc.into(),
|
||||
gacha_type: req.gacha_type,
|
||||
gacha_data: Some(generate_all_gacha_info(_player)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn on_do_gacha(
|
||||
_session: &NetSession,
|
||||
_player: &mut Player,
|
||||
req: DoGachaCsReq,
|
||||
) -> NetResult<DoGachaScRsp> {
|
||||
let gachaconf = global_gacha_config();
|
||||
let gacha_model = &mut _player.gacha_model;
|
||||
let pull_time = Local::now();
|
||||
let target_pool = get_gacha_pool(
|
||||
&gachaconf.character_gacha_pool_list,
|
||||
&req.gacha_parent_schedule_id,
|
||||
&pull_time,
|
||||
);
|
||||
if let None = target_pool {
|
||||
return Ok(DoGachaScRsp {
|
||||
retcode: Retcode::RetSucc.into(),
|
||||
..Default::default()
|
||||
});
|
||||
};
|
||||
let target_pool = target_pool.unwrap();
|
||||
|
||||
// TODO: Validate cost_item_count
|
||||
// tracing::info!("cost_item_count: {}", body.cost_item_count);
|
||||
let mut pull_count = if req.cost_item_count > 1 { 10 } else { 1 };
|
||||
|
||||
if pull_count == 10 {
|
||||
let discount_tag = &gachaconf.common_properties.ten_pull_discount_tag;
|
||||
if target_pool.discount_policy_tags.contains(&discount_tag) {
|
||||
let gacha_bin = &mut gacha_model.gacha_bin;
|
||||
let status_bin = gacha_bin
|
||||
.gacha_status_map
|
||||
.get_mut(&target_pool.sharing_guarantee_info_category)
|
||||
.unwrap();
|
||||
let discount_policy = gachaconf
|
||||
.discount_policies
|
||||
.ten_pull_discount_map
|
||||
.get(discount_tag)
|
||||
.unwrap();
|
||||
let usage = status_bin.discount_usage_map.get_mut(discount_tag).unwrap();
|
||||
if *usage < discount_policy.use_limit {
|
||||
*usage += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut gain_item_list: Vec<GainItemInfo> = vec![];
|
||||
while pull_count > 0 {
|
||||
let pull_result = gacha_model.perform_pull_pool(&pull_time, target_pool);
|
||||
let extra_item_bin = pull_result.extra_item_bin.unwrap();
|
||||
gain_item_list.push(GainItemInfo {
|
||||
item_id: pull_result.obtained_item_id,
|
||||
extra_item_id: extra_item_bin.extra_item_id,
|
||||
extra_item_count: extra_item_bin.extra_item_count,
|
||||
num: 1,
|
||||
..GainItemInfo::default()
|
||||
});
|
||||
pull_count -= 1;
|
||||
}
|
||||
|
||||
Ok(DoGachaScRsp {
|
||||
retcode: Retcode::RetSucc.into(),
|
||||
gacha_type: req.gacha_type,
|
||||
gacha_data: Some(GachaData::default()),
|
||||
gain_item_list,
|
||||
gacha_data: Some(generate_all_gacha_info(_player)),
|
||||
cost_item_count: req.cost_item_count,
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_gacha_info_from_pool(
|
||||
gacha_bin: &GachaModelBin,
|
||||
target_pool: &CharacterGachaPool,
|
||||
common_properties: &GachaCommonProperties,
|
||||
) -> Gacha {
|
||||
let gachaconf = data::gacha::global_gacha_config();
|
||||
let sharing_guarantee_category_tag = &target_pool.sharing_guarantee_info_category;
|
||||
let status_bin = gacha_bin
|
||||
.gacha_status_map
|
||||
.get(sharing_guarantee_category_tag)
|
||||
.unwrap();
|
||||
let pity_s = status_bin
|
||||
.rarity_status_map
|
||||
.get(&common_properties.s_item_rarity)
|
||||
.unwrap()
|
||||
.pity;
|
||||
let pity_a = status_bin
|
||||
.rarity_status_map
|
||||
.get(&common_properties.a_item_rarity)
|
||||
.unwrap()
|
||||
.pity;
|
||||
let mut up_s_item_list: Vec<u32> = vec![];
|
||||
let mut up_a_item_list: Vec<u32> = vec![];
|
||||
let mut s_guarantee: u32 = 0;
|
||||
let mut a_guarantee: u32 = 0;
|
||||
for rarity_items in target_pool.gacha_items.iter() {
|
||||
for category in rarity_items.categories.values() {
|
||||
let probability_model = gachaconf
|
||||
.probability_model_map
|
||||
.get(&rarity_items.probability_model_tag)
|
||||
.unwrap();
|
||||
if rarity_items.rarity == common_properties.s_item_rarity {
|
||||
if category.is_promotional_items {
|
||||
up_s_item_list = category.item_ids.clone();
|
||||
}
|
||||
s_guarantee = probability_model.maximum_guarantee_pity - pity_s + 1;
|
||||
}
|
||||
if rarity_items.rarity == common_properties.a_item_rarity {
|
||||
if category.is_promotional_items {
|
||||
up_a_item_list = category.item_ids.clone();
|
||||
}
|
||||
a_guarantee = probability_model.maximum_guarantee_pity - pity_a + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut discount_ten_roll_prize: u32 = 0;
|
||||
let mut discount_avaliable_num: u32 = 0;
|
||||
let mut advanced_s_guarantee: u32 = 0;
|
||||
for discount_policy_tag in target_pool.discount_policy_tags.iter() {
|
||||
if common_properties.newcomer_advanced_s_tag == *discount_policy_tag {
|
||||
let policy = gachaconf
|
||||
.discount_policies
|
||||
.advanced_guarantee_map
|
||||
.get(discount_policy_tag)
|
||||
.unwrap();
|
||||
if status_bin
|
||||
.discount_usage_map
|
||||
.get(discount_policy_tag)
|
||||
.unwrap()
|
||||
< &policy.use_limit
|
||||
{
|
||||
advanced_s_guarantee = policy.guarantee_pity - pity_s + 1;
|
||||
}
|
||||
} else if common_properties.ten_pull_discount_tag == *discount_policy_tag {
|
||||
let policy = gachaconf
|
||||
.discount_policies
|
||||
.ten_pull_discount_map
|
||||
.get(discount_policy_tag)
|
||||
.unwrap();
|
||||
let discount_usage = status_bin
|
||||
.discount_usage_map
|
||||
.get(discount_policy_tag)
|
||||
.unwrap();
|
||||
if discount_usage < &policy.use_limit {
|
||||
discount_ten_roll_prize = policy.discounted_prize;
|
||||
discount_avaliable_num = policy.use_limit - discount_usage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let need_item_info_list: Vec<NeedItemInfo> = vec![NeedItemInfo {
|
||||
need_item_id: target_pool.cost_item_id,
|
||||
need_item_count: 1,
|
||||
}];
|
||||
|
||||
Gacha {
|
||||
gacha_schedule_id: target_pool.gacha_schedule_id,
|
||||
gacha_parent_schedule_id: target_pool.gacha_parent_schedule_id,
|
||||
gacha_type: target_pool.gacha_type,
|
||||
start_timestamp: target_pool.start_time.timestamp(),
|
||||
end_timestamp: target_pool.end_time.timestamp(),
|
||||
discount_avaliable_num,
|
||||
discount_ten_roll_prize,
|
||||
up_s_item_list,
|
||||
up_a_item_list,
|
||||
advanced_s_guarantee,
|
||||
s_guarantee,
|
||||
a_guarantee,
|
||||
need_item_info_list,
|
||||
// iehkehofjop: target_pool.gacha_parent_schedule_id,
|
||||
// eggcehlgkii: 223,
|
||||
// ijoahiepmfo: 101,
|
||||
..Gacha::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_all_gacha_info(_player: &Player) -> GachaData {
|
||||
let gachaconf = global_gacha_config();
|
||||
let gacha_bin = &_player.gacha_model.gacha_bin;
|
||||
let mut gacha_list: Vec<Gacha> = vec![];
|
||||
for target_pool in gachaconf.character_gacha_pool_list.iter() {
|
||||
gacha_list.push(generate_gacha_info_from_pool(
|
||||
&gacha_bin,
|
||||
target_pool,
|
||||
&gachaconf.common_properties,
|
||||
));
|
||||
}
|
||||
GachaData {
|
||||
random_number: 0,
|
||||
gacha_pool: Some(GachaPool { gacha_list }),
|
||||
..GachaData::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn get_gacha_pool<'conf>(
|
||||
character_gacha_pool_list: &'conf Vec<CharacterGachaPool>,
|
||||
gacha_parent_schedule_id: &u32,
|
||||
pull_time: &DateTime<Local>,
|
||||
) -> Option<&'conf CharacterGachaPool> {
|
||||
for target_pool in character_gacha_pool_list.iter() {
|
||||
if &target_pool.gacha_parent_schedule_id == gacha_parent_schedule_id
|
||||
&& target_pool.is_still_open(pull_time)
|
||||
{
|
||||
return Some(target_pool);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
|
@ -119,6 +119,7 @@ req_handlers! {
|
|||
event_graph::RunEventGraph;
|
||||
quest::BeginArchiveBattleQuest;
|
||||
quest::FinishArchiveQuest;
|
||||
gacha::DoGacha;
|
||||
}
|
||||
|
||||
notify_handlers! {
|
||||
|
|
|
@ -1,4 +1,11 @@
|
|||
use proto::GachaModelBin;
|
||||
use data::gacha;
|
||||
use data::gacha::gacha_config::*;
|
||||
use proto::*;
|
||||
|
||||
use chrono::{DateTime, Local};
|
||||
use rand::{thread_rng, Rng};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct GachaModel {
|
||||
|
@ -7,12 +14,377 @@ pub struct GachaModel {
|
|||
|
||||
impl GachaModel {
|
||||
pub fn from_bin(gacha_bin: GachaModelBin) -> Self {
|
||||
Self {
|
||||
gacha_bin
|
||||
}
|
||||
let result = Self { gacha_bin };
|
||||
result.post_deserialize()
|
||||
}
|
||||
|
||||
pub fn to_bin(&self) -> GachaModelBin {
|
||||
self.gacha_bin.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn post_deserialize(mut self) -> GachaModel {
|
||||
let gachaconf = gacha::global_gacha_config();
|
||||
for gacha_pool in gachaconf.character_gacha_pool_list.iter() {
|
||||
let gacha_comp_bin = &mut self.gacha_bin;
|
||||
let mut gacha_status_map = &mut gacha_comp_bin.gacha_status_map;
|
||||
let status_bin = get_or_add(
|
||||
&mut gacha_status_map,
|
||||
&gacha_pool.sharing_guarantee_info_category,
|
||||
);
|
||||
for rarity_items in gacha_pool.gacha_items.iter() {
|
||||
let progress_bin =
|
||||
get_or_add(&mut status_bin.rarity_status_map, &rarity_items.rarity);
|
||||
if progress_bin.pity <= 0 {
|
||||
progress_bin.pity = 1;
|
||||
}
|
||||
for category_guarantee_policy_tag in
|
||||
rarity_items.category_guarantee_policy_tags.iter()
|
||||
{
|
||||
get_or_add(
|
||||
&mut progress_bin.categories_progress_map,
|
||||
&category_guarantee_policy_tag,
|
||||
);
|
||||
}
|
||||
}
|
||||
for discount_policy_tag in gacha_pool.discount_policy_tags.iter() {
|
||||
get_or_add(&mut status_bin.discount_usage_map, &discount_policy_tag);
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn perform_pull_pool<'bin, 'conf>(
|
||||
&'bin mut self,
|
||||
pull_time: &DateTime<Local>,
|
||||
target_pool: &'conf CharacterGachaPool,
|
||||
) -> GachaRecordBin {
|
||||
let mut gacha_bin = &mut self.gacha_bin;
|
||||
let (rarity_items, progress_bin, status_bin, probability_model) =
|
||||
determine_rarity(&gacha_bin, target_pool);
|
||||
let (category_tag, category) = determine_category(rarity_items, progress_bin, target_pool);
|
||||
let result =
|
||||
determine_gacha_result(pull_time, category, target_pool, status_bin, rarity_items);
|
||||
update_pity(&mut gacha_bin, rarity_items, probability_model, target_pool);
|
||||
update_category_guarantee_info(&mut gacha_bin, rarity_items, &category_tag, target_pool);
|
||||
update_discount(&mut gacha_bin, target_pool, &category_tag, rarity_items);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
fn get_or_add<'a, K: Eq + PartialEq + Hash + Clone, V: Default, S: BuildHasher>(
|
||||
map: &'a mut HashMap<K, V, S>,
|
||||
key: &K,
|
||||
) -> &'a mut V {
|
||||
if !map.contains_key(key) {
|
||||
map.insert(key.clone(), V::default());
|
||||
}
|
||||
map.get_mut(key).unwrap()
|
||||
}
|
||||
|
||||
fn rand_rarity<'bin, 'conf>(
|
||||
target_pool: &'conf CharacterGachaPool,
|
||||
status_bin: &'bin GachaStatusBin,
|
||||
) -> (
|
||||
&'conf GachaAvailableItemsInfo,
|
||||
&'bin GachaProgressBin,
|
||||
&'conf ProbabilityModel,
|
||||
) {
|
||||
let gachaconf = gacha::global_gacha_config();
|
||||
let mut rng = thread_rng();
|
||||
let rarity_status_map = &status_bin.rarity_status_map;
|
||||
// gacha_items is already sorted by rarity descendingly in its post_configure.
|
||||
for rarity_items in target_pool.gacha_items.iter() {
|
||||
// Surely any judgement should be made on the current pity.
|
||||
let progress_bin = rarity_status_map.get(&rarity_items.rarity).unwrap();
|
||||
let pity = progress_bin.pity;
|
||||
let probability_model = gachaconf
|
||||
.probability_model_map
|
||||
.get(&rarity_items.probability_model_tag)
|
||||
.unwrap();
|
||||
if rng.gen_range(0.0..100.0) <= probability_model.get_chance_percent(&pity) {
|
||||
return (rarity_items, progress_bin, probability_model);
|
||||
}
|
||||
}
|
||||
panic!("The user failed to get any items.");
|
||||
}
|
||||
|
||||
fn determine_rarity<'bin, 'conf>(
|
||||
gacha_bin: &'bin GachaModelBin,
|
||||
target_pool: &'conf CharacterGachaPool,
|
||||
) -> (
|
||||
&'conf GachaAvailableItemsInfo,
|
||||
&'bin GachaProgressBin,
|
||||
&'bin GachaStatusBin,
|
||||
&'conf ProbabilityModel,
|
||||
) {
|
||||
let gachaconf = gacha::global_gacha_config();
|
||||
let status_bin = gacha_bin
|
||||
.gacha_status_map
|
||||
.get(&target_pool.sharing_guarantee_info_category)
|
||||
.expect(&format!(
|
||||
"post_deserialize forgot StatusBin/sharing_guarantee_info_category: {}",
|
||||
target_pool.sharing_guarantee_info_category
|
||||
));
|
||||
let (mut rarity_items, mut progress_bin, mut probability_model) =
|
||||
rand_rarity(target_pool, &status_bin);
|
||||
|
||||
// We should take AdvancedGuarantee discount into consideration.
|
||||
for discount_tag in target_pool.discount_policy_tags.iter() {
|
||||
if let Some(discount) = gachaconf
|
||||
.discount_policies
|
||||
.advanced_guarantee_map
|
||||
.get(discount_tag)
|
||||
{
|
||||
if discount.rarity <= rarity_items.rarity {
|
||||
continue;
|
||||
}
|
||||
if status_bin
|
||||
.discount_usage_map
|
||||
.get(discount_tag)
|
||||
.expect(&format!(
|
||||
"post_deserialize forgot StatusBin/discount_usage_map: {}",
|
||||
discount_tag
|
||||
))
|
||||
>= &discount.use_limit
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let higher_progress_bin =
|
||||
status_bin
|
||||
.rarity_status_map
|
||||
.get(&discount.rarity)
|
||||
.expect(&format!(
|
||||
"post_deserialize forgot StatusBin/rarity_status_map: {}",
|
||||
&discount.rarity
|
||||
));
|
||||
if higher_progress_bin.pity >= discount.guarantee_pity {
|
||||
let mut found_rarity_items = false;
|
||||
for gacha_items in target_pool.gacha_items.iter() {
|
||||
if gacha_items.rarity == discount.rarity {
|
||||
rarity_items = gacha_items;
|
||||
probability_model = gachaconf
|
||||
.probability_model_map
|
||||
.get(&gacha_items.probability_model_tag)
|
||||
.unwrap();
|
||||
found_rarity_items = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert!(found_rarity_items, "Handle AdvancedGuarantee Discount ({discount_tag}) error: The target rarity does not exist in this pool.");
|
||||
progress_bin = higher_progress_bin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(rarity_items, progress_bin, status_bin, probability_model)
|
||||
}
|
||||
|
||||
fn determine_category<'bin, 'conf>(
|
||||
rarity_items: &'conf GachaAvailableItemsInfo,
|
||||
progress_bin: &'bin GachaProgressBin,
|
||||
target_pool: &'conf CharacterGachaPool,
|
||||
) -> (String, &'conf GachaCategoryInfo) {
|
||||
let gachaconf = gacha::global_gacha_config();
|
||||
let mut category_tag_inited = false;
|
||||
let mut category_tag_result: HashSet<String> = HashSet::new();
|
||||
// First of all, if there's a chooseable category and
|
||||
// it's can be triggered, then we MUST give that
|
||||
// category's item.
|
||||
// TODO: Only Genshin can do
|
||||
// Then we should take a look at MustGainItem.
|
||||
if !category_tag_inited {
|
||||
for discount_policy_tag in target_pool.discount_policy_tags.iter() {
|
||||
if let Some(discount) = gachaconf
|
||||
.discount_policies
|
||||
.must_gain_item_map
|
||||
.get(discount_policy_tag)
|
||||
{
|
||||
if discount.rarity != rarity_items.rarity {
|
||||
continue;
|
||||
}
|
||||
category_tag_result.insert(discount.category_tag.clone());
|
||||
category_tag_inited = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Otherwise, just select as normal.
|
||||
if !category_tag_inited {
|
||||
for tag in rarity_items.categories.keys() {
|
||||
category_tag_result.insert(tag.clone());
|
||||
}
|
||||
for guarantee_policy_tag in rarity_items.category_guarantee_policy_tags.iter() {
|
||||
let category_guarantee_policy = gachaconf
|
||||
.category_guarantee_policy_map
|
||||
.get(guarantee_policy_tag)
|
||||
.unwrap();
|
||||
let failure_times = progress_bin.categories_progress_map
|
||||
.get(guarantee_policy_tag)
|
||||
.expect(&format!("post_deserialize forgot StatusBin/rarity_status_map[{}]/categories_progress_map: {}", &rarity_items.rarity, guarantee_policy_tag));
|
||||
if failure_times >= &category_guarantee_policy.trigger_on_failure_times {
|
||||
category_tag_result = category_tag_result
|
||||
.intersection(&category_guarantee_policy.included_category_tags)
|
||||
.cloned()
|
||||
.collect();
|
||||
}
|
||||
}
|
||||
// category_tag_inited = true;
|
||||
}
|
||||
|
||||
let mut categories: Vec<(String, &GachaCategoryInfo)> = vec![];
|
||||
let mut weight_sum = 0;
|
||||
for result_tag in category_tag_result {
|
||||
let category = rarity_items.categories.get(&result_tag).unwrap();
|
||||
categories.push((result_tag, category));
|
||||
weight_sum += category.category_weight;
|
||||
}
|
||||
|
||||
let randomnum = rand::thread_rng().gen_range(0..weight_sum);
|
||||
let mut enumerated_ranges_end = 0;
|
||||
for category in categories.into_iter() {
|
||||
if randomnum <= enumerated_ranges_end + category.1.category_weight {
|
||||
return (category.0, category.1);
|
||||
}
|
||||
enumerated_ranges_end += category.1.category_weight;
|
||||
}
|
||||
panic!("No category is chosen.");
|
||||
}
|
||||
|
||||
fn determine_gacha_result<'bin, 'conf>(
|
||||
pull_time: &DateTime<Local>,
|
||||
category: &'conf GachaCategoryInfo,
|
||||
target_pool: &'conf CharacterGachaPool,
|
||||
status_bin: &'bin GachaStatusBin,
|
||||
rarity_items: &'conf GachaAvailableItemsInfo,
|
||||
) -> GachaRecordBin {
|
||||
let gachaconf = gacha::global_gacha_config();
|
||||
let item_pool_len = category.item_ids.len() as u32;
|
||||
let item_id = category
|
||||
.item_ids
|
||||
.get(rand::thread_rng().gen_range(0..item_pool_len) as usize)
|
||||
.unwrap();
|
||||
let mut extra_item_id: u32 = 0;
|
||||
let mut extra_item_count: u32 = 0;
|
||||
|
||||
for extra_items_policy_tag in rarity_items.extra_items_policy_tags.iter() {
|
||||
let extra_items_policy = gachaconf
|
||||
.extra_items_policy_map
|
||||
.get(extra_items_policy_tag)
|
||||
.unwrap();
|
||||
// TODO: apply_on_owned_count in a context with bag
|
||||
if extra_items_policy.apply_on_owned_count == 0 {
|
||||
extra_item_id = extra_items_policy.id;
|
||||
extra_item_count = extra_items_policy.count;
|
||||
}
|
||||
}
|
||||
let extra_item_bin = GachaExtraItemBin {
|
||||
extra_item_id,
|
||||
extra_item_count,
|
||||
currently_gained: 0,
|
||||
};
|
||||
GachaRecordBin {
|
||||
pull_timestamp: pull_time.timestamp(),
|
||||
obtained_item_id: item_id.clone(),
|
||||
gacha_id: target_pool.gacha_schedule_id.clone(),
|
||||
progress_map: status_bin.rarity_status_map.clone(),
|
||||
extra_item_bin: Some(extra_item_bin),
|
||||
}
|
||||
}
|
||||
|
||||
fn update_pity<'bin, 'conf>(
|
||||
gacha_bin: &'bin mut GachaModelBin,
|
||||
rarity_items: &'conf GachaAvailableItemsInfo,
|
||||
probability_model: &'conf ProbabilityModel,
|
||||
target_pool: &'conf CharacterGachaPool,
|
||||
) {
|
||||
let status_bin = gacha_bin
|
||||
.gacha_status_map
|
||||
.get_mut(&target_pool.sharing_guarantee_info_category)
|
||||
.unwrap();
|
||||
for (rarity, rarity_status) in status_bin.rarity_status_map.iter_mut() {
|
||||
if (rarity == &rarity_items.rarity)
|
||||
|| (probability_model.clear_status_on_higher_rarity_pulled
|
||||
&& rarity < &rarity_items.rarity)
|
||||
{
|
||||
rarity_status.pity = 1;
|
||||
} else {
|
||||
rarity_status.pity += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_category_guarantee_info<'bin, 'conf>(
|
||||
gacha_bin: &'bin mut GachaModelBin,
|
||||
rarity_items: &'conf GachaAvailableItemsInfo,
|
||||
category_tag: &String,
|
||||
target_pool: &'conf CharacterGachaPool,
|
||||
) {
|
||||
let gachaconf = gacha::global_gacha_config();
|
||||
let status_bin = gacha_bin
|
||||
.gacha_status_map
|
||||
.get_mut(&target_pool.sharing_guarantee_info_category)
|
||||
.unwrap();
|
||||
let progress_bin = status_bin
|
||||
.rarity_status_map
|
||||
.get_mut(&rarity_items.rarity)
|
||||
.unwrap();
|
||||
for policy_tag in rarity_items.category_guarantee_policy_tags.iter() {
|
||||
let policy = gachaconf
|
||||
.category_guarantee_policy_map
|
||||
.get(policy_tag)
|
||||
.unwrap();
|
||||
// TODO: Chooseable guarantee not implemented
|
||||
let prev_failure = progress_bin
|
||||
.categories_progress_map
|
||||
.get_mut(policy_tag)
|
||||
.expect(&format!(
|
||||
"post_deserialize forgot StatusBin/rarity_status_map[{}]/categories_progress_map: {}",
|
||||
rarity_items.rarity, policy_tag
|
||||
));
|
||||
if policy.included_category_tags.contains(category_tag) {
|
||||
*prev_failure = 0;
|
||||
} else {
|
||||
*prev_failure += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_discount<'bin, 'conf>(
|
||||
gacha_bin: &'bin mut GachaModelBin,
|
||||
target_pool: &'conf CharacterGachaPool,
|
||||
category_tag: &String,
|
||||
rarity_items: &GachaAvailableItemsInfo,
|
||||
) {
|
||||
let gachaconf = gacha::global_gacha_config();
|
||||
for (policy_tag, policy) in gachaconf.discount_policies.must_gain_item_map.iter() {
|
||||
if *category_tag != policy.category_tag {
|
||||
continue;
|
||||
}
|
||||
if !target_pool.discount_policy_tags.contains(policy_tag) {
|
||||
continue;
|
||||
}
|
||||
let status_bin = gacha_bin
|
||||
.gacha_status_map
|
||||
.get_mut(&target_pool.sharing_guarantee_info_category)
|
||||
.unwrap();
|
||||
let usage = status_bin.discount_usage_map.get_mut(policy_tag).unwrap();
|
||||
if *usage < policy.use_limit {
|
||||
*usage += 1;
|
||||
}
|
||||
}
|
||||
for (policy_tag, policy) in gachaconf.discount_policies.advanced_guarantee_map.iter() {
|
||||
if rarity_items.rarity != policy.rarity {
|
||||
continue;
|
||||
}
|
||||
if !target_pool.discount_policy_tags.contains(policy_tag) {
|
||||
continue;
|
||||
}
|
||||
let status_bin = gacha_bin
|
||||
.gacha_status_map
|
||||
.get_mut(&target_pool.sharing_guarantee_info_category)
|
||||
.unwrap();
|
||||
let usage = status_bin.discount_usage_map.get_mut(policy_tag).unwrap();
|
||||
if *usage < policy.use_limit {
|
||||
*usage += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue