Gacha System implementation #1
7 changed files with 1968 additions and 60 deletions
File diff suppressed because it is too large
Load diff
|
@ -126,11 +126,21 @@ pub struct MustGainItem {
|
|||
pub category_tag: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub struct FreeSelectItem {
|
||||
pub milestones: Vec<u32>,
|
||||
pub rarity: u32,
|
||||
pub category_tags: Vec<String>,
|
||||
pub free_select_progress_record_tag: String,
|
||||
pub free_select_usage_record_tag: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub struct DiscountPolicyCollection {
|
||||
pub ten_pull_discount_map: HashMap<String, TenPullDiscount>,
|
||||
pub must_gain_item_map: HashMap<String, MustGainItem>,
|
||||
pub advanced_guarantee_map: HashMap<String, AdvancedGuarantee>,
|
||||
pub free_select_map: HashMap<String, FreeSelectItem>,
|
||||
}
|
||||
|
||||
impl DiscountPolicyCollection {
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
use std::{
|
||||
cmp::min,
|
||||
collections::{
|
||||
hash_map::Entry::{Occupied, Vacant},
|
||||
HashSet,
|
||||
},
|
||||
};
|
||||
|
||||
use data::{
|
||||
gacha::{gacha_config::*, global_gacha_config},
|
||||
tables::{AvatarBaseID, WeaponID},
|
||||
|
@ -24,7 +32,7 @@ pub async fn on_get_gacha_data(
|
|||
Ok(GetGachaDataScRsp {
|
||||
retcode: Retcode::RetSucc.into(),
|
||||
gacha_type: req.gacha_type,
|
||||
gacha_data: Some(generate_all_gacha_info(_player)),
|
||||
gacha_data: Some(generate_all_gacha_info(_player, &Local::now())),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -45,8 +53,12 @@ pub async fn on_do_gacha(
|
|||
&pull_time,
|
||||
);
|
||||
if let None = target_pool {
|
||||
tracing::info!(
|
||||
"refuse gacha because: pool of parent_schedule_id {} not found",
|
||||
req.gacha_parent_schedule_id
|
||||
);
|
||||
return Ok(DoGachaScRsp {
|
||||
retcode: Retcode::RetSucc.into(),
|
||||
retcode: Retcode::RetFail.into(),
|
||||
..Default::default()
|
||||
});
|
||||
};
|
||||
|
@ -72,11 +84,15 @@ pub async fn on_do_gacha(
|
|||
let usage = status_bin.discount_usage_map.get_mut(discount_tag).unwrap();
|
||||
if *usage < discount_policy.use_limit {
|
||||
*usage += 1;
|
||||
cost_count = discount_policy.discounted_prize;
|
||||
// cost_count = discount_policy.discounted_prize;
|
||||
}
|
||||
}
|
||||
}
|
||||
if cost_count != req.cost_item_count {
|
||||
tracing::info!(
|
||||
"refuse gacha because: expected cost item {cost_count}, found {}",
|
||||
req.cost_item_count
|
||||
);
|
||||
return Ok(DoGachaScRsp {
|
||||
retcode: Retcode::RetFail.into(),
|
||||
..Default::default()
|
||||
|
@ -88,7 +104,7 @@ pub async fn on_do_gacha(
|
|||
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();
|
||||
let extra_item_bin = pull_result.extra_item_bin.clone().unwrap();
|
||||
let uid = match GachaAddedItemType::try_from(pull_result.item_type) {
|
||||
Ok(enum_val) => match enum_val {
|
||||
GachaAddedItemType::Weapon => match WeaponID::new(pull_result.obtained_item_id) {
|
||||
|
@ -123,16 +139,236 @@ pub async fn on_do_gacha(
|
|||
..GainItemInfo::default()
|
||||
});
|
||||
pull_count -= 1;
|
||||
gacha_model.gacha_bin.gacha_records.push(pull_result);
|
||||
}
|
||||
|
||||
Ok(DoGachaScRsp {
|
||||
retcode: Retcode::RetSucc.into(),
|
||||
gain_item_list,
|
||||
gacha_data: Some(generate_all_gacha_info(_player)),
|
||||
gacha_data: Some(generate_all_gacha_info(_player, &pull_time)),
|
||||
cost_item_count: req.cost_item_count,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn on_gacha_free_agent(
|
||||
_session: &NetSession,
|
||||
_player: &mut Player,
|
||||
req: GachaFreeAgentCsReq,
|
||||
) -> NetResult<GachaFreeAgentScRsp> {
|
||||
let gachaconf = global_gacha_config();
|
||||
let gacha_model = &mut _player.gacha_model;
|
||||
let role_model = &mut _player.role_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 {
|
||||
tracing::info!(
|
||||
"refuse free agent because: pool of parent_schedule_id {} not found",
|
||||
req.gacha_parent_schedule_id
|
||||
);
|
||||
return Ok(GachaFreeAgentScRsp {
|
||||
retcode: Retcode::RetFail.into(),
|
||||
..Default::default()
|
||||
});
|
||||
};
|
||||
let target_pool = target_pool.unwrap();
|
||||
let gacha_bin = &mut _player.gacha_model.gacha_bin;
|
||||
let sharing_guarantee_category_tag = &target_pool.sharing_guarantee_info_category;
|
||||
let status_bin = gacha_bin
|
||||
.gacha_status_map
|
||||
.get_mut(sharing_guarantee_category_tag)
|
||||
.unwrap();
|
||||
|
||||
let mut free_select_policy: Option<&FreeSelectItem> = None;
|
||||
let mut free_select_progress: u32 = 0;
|
||||
let mut free_select_required_pull: u32 = 0;
|
||||
for discount_policy_tag in target_pool.discount_policy_tags.iter() {
|
||||
if gachaconf
|
||||
.discount_policies
|
||||
.free_select_map
|
||||
.contains_key(discount_policy_tag)
|
||||
{
|
||||
let policy = gachaconf
|
||||
.discount_policies
|
||||
.free_select_map
|
||||
.get(discount_policy_tag)
|
||||
.unwrap();
|
||||
let free_select_demand_idx = usize::try_from(
|
||||
*(status_bin
|
||||
.discount_usage_map
|
||||
.get(&policy.free_select_usage_record_tag)
|
||||
.unwrap()),
|
||||
)
|
||||
.unwrap();
|
||||
if policy.milestones.len() <= free_select_demand_idx {
|
||||
continue;
|
||||
}
|
||||
|
||||
let free_select_actual_progress = status_bin
|
||||
.discount_usage_map
|
||||
.get(&policy.free_select_progress_record_tag)
|
||||
.unwrap();
|
||||
free_select_policy = Some(policy);
|
||||
free_select_required_pull = policy
|
||||
.milestones
|
||||
.get(free_select_demand_idx)
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
free_select_progress = min(free_select_required_pull, *free_select_actual_progress);
|
||||
}
|
||||
}
|
||||
|
||||
if let None = free_select_policy {
|
||||
tracing::info!(
|
||||
"refuse free agent because: pool of parent_schedule_id {} hasn't defined free agent discount yet (or used up chance)",
|
||||
req.gacha_parent_schedule_id
|
||||
);
|
||||
return Ok(GachaFreeAgentScRsp {
|
||||
retcode: Retcode::RetFail.into(),
|
||||
..Default::default()
|
||||
});
|
||||
} else if free_select_progress < free_select_required_pull {
|
||||
tracing::info!(
|
||||
"refuse free agent because: use pulled {free_select_progress} (after last free agent) in parent_schedule_id {}, required {free_select_required_pull}",
|
||||
req.gacha_parent_schedule_id
|
||||
);
|
||||
return Ok(GachaFreeAgentScRsp {
|
||||
retcode: Retcode::RetFail.into(),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
let free_select_policy = free_select_policy.unwrap();
|
||||
let mut has_demanded_item = false;
|
||||
for rarity_items in target_pool.gacha_items.iter() {
|
||||
if rarity_items.rarity != free_select_policy.rarity {
|
||||
continue;
|
||||
}
|
||||
for (category_tag, category) in rarity_items.categories.iter() {
|
||||
if !free_select_policy.category_tags.contains(category_tag) {
|
||||
continue;
|
||||
}
|
||||
has_demanded_item |= category.item_ids.contains(&req.avatar_id);
|
||||
}
|
||||
}
|
||||
if !has_demanded_item {
|
||||
tracing::info!(
|
||||
"refuse free agent because: pool of parent_schedule_id {} doesn't have demanded item {}",
|
||||
req.gacha_parent_schedule_id, req.avatar_id
|
||||
);
|
||||
return Ok(GachaFreeAgentScRsp {
|
||||
retcode: Retcode::RetFail.into(),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
|
||||
role_model.add_avatar(AvatarBaseID::new(req.avatar_id).unwrap());
|
||||
(*status_bin
|
||||
.discount_usage_map
|
||||
.get_mut(&free_select_policy.free_select_usage_record_tag)
|
||||
.unwrap()) += 1;
|
||||
(*status_bin
|
||||
.discount_usage_map
|
||||
.get_mut(&free_select_policy.free_select_progress_record_tag)
|
||||
.unwrap()) -= free_select_required_pull;
|
||||
|
||||
Ok(GachaFreeAgentScRsp {
|
||||
retcode: Retcode::RetSucc.into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn on_choose_gacha_up(
|
||||
_session: &NetSession,
|
||||
_player: &mut Player,
|
||||
req: ChooseGachaUpCsReq,
|
||||
) -> NetResult<ChooseGachaUpScRsp> {
|
||||
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(ChooseGachaUpScRsp {
|
||||
retcode: Retcode::RetFail.into(),
|
||||
..Default::default()
|
||||
});
|
||||
};
|
||||
let target_pool = target_pool.unwrap();
|
||||
|
||||
for rarity_items in target_pool.gacha_items.iter() {
|
||||
if rarity_items.rarity != gachaconf.common_properties.s_item_rarity {
|
||||
continue;
|
||||
}
|
||||
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();
|
||||
if !category_guarantee_policy.chooseable {
|
||||
continue;
|
||||
}
|
||||
let mut up_category: Option<&String> = None;
|
||||
for (category_tag, category) in rarity_items.categories.iter() {
|
||||
if category.item_ids.contains(&req.item_id) {
|
||||
up_category = Some(category_tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let None = up_category {
|
||||
return Ok(ChooseGachaUpScRsp {
|
||||
retcode: Retcode::RetFail.into(),
|
||||
..Default::default()
|
||||
});
|
||||
};
|
||||
let up_category = up_category.unwrap();
|
||||
|
||||
let progress_bin = gacha_model
|
||||
.gacha_bin
|
||||
.gacha_status_map
|
||||
.get_mut(&target_pool.sharing_guarantee_info_category)
|
||||
.unwrap()
|
||||
.rarity_status_map
|
||||
.get_mut(&rarity_items.rarity)
|
||||
.unwrap();
|
||||
match progress_bin
|
||||
.categories_chosen_guarantee_item_map
|
||||
.entry(guarantee_policy_tag.clone())
|
||||
{
|
||||
Occupied(mut occupied_entry) => {
|
||||
occupied_entry.insert(req.item_id);
|
||||
}
|
||||
Vacant(vacant_entry) => {
|
||||
vacant_entry.insert(req.item_id);
|
||||
}
|
||||
};
|
||||
match progress_bin
|
||||
.categories_chosen_guarantee_category_map
|
||||
.entry(up_category.clone())
|
||||
{
|
||||
Occupied(mut occupied_entry) => {
|
||||
occupied_entry.insert(up_category.clone());
|
||||
}
|
||||
Vacant(vacant_entry) => {
|
||||
vacant_entry.insert(up_category.clone());
|
||||
}
|
||||
};
|
||||
return Ok(ChooseGachaUpScRsp {
|
||||
retcode: Retcode::RetSucc.into(),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(ChooseGachaUpScRsp {
|
||||
retcode: Retcode::RetFail.into(),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
fn generate_gacha_info_from_pool(
|
||||
gacha_bin: &GachaModelBin,
|
||||
target_pool: &CharacterGachaPool,
|
||||
|
@ -154,34 +390,13 @@ fn generate_gacha_info_from_pool(
|
|||
.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;
|
||||
let mut free_select_progress: u32 = 0;
|
||||
let mut free_select_required_pull: u32 = 0;
|
||||
let mut free_select_policy: Option<&FreeSelectItem> = None;
|
||||
for discount_policy_tag in target_pool.discount_policy_tags.iter() {
|
||||
if common_properties.newcomer_advanced_s_tag == *discount_policy_tag {
|
||||
let policy = gachaconf
|
||||
|
@ -211,6 +426,123 @@ fn generate_gacha_info_from_pool(
|
|||
discount_ten_roll_prize = policy.discounted_prize;
|
||||
discount_avaliable_num = policy.use_limit - discount_usage;
|
||||
}
|
||||
} else if gachaconf
|
||||
.discount_policies
|
||||
.free_select_map
|
||||
.contains_key(discount_policy_tag)
|
||||
{
|
||||
let policy = gachaconf
|
||||
.discount_policies
|
||||
.free_select_map
|
||||
.get(discount_policy_tag)
|
||||
.unwrap();
|
||||
let free_select_demand_idx = usize::try_from(
|
||||
*(status_bin
|
||||
.discount_usage_map
|
||||
.get(&policy.free_select_usage_record_tag)
|
||||
.unwrap()),
|
||||
)
|
||||
.unwrap();
|
||||
if policy.milestones.len() <= free_select_demand_idx {
|
||||
continue;
|
||||
}
|
||||
|
||||
let free_select_actual_progress = status_bin
|
||||
.discount_usage_map
|
||||
.get(&policy.free_select_progress_record_tag)
|
||||
.unwrap();
|
||||
free_select_policy = Some(policy);
|
||||
free_select_required_pull = policy
|
||||
.milestones
|
||||
.get(free_select_demand_idx)
|
||||
.unwrap()
|
||||
.to_owned();
|
||||
free_select_progress = min(free_select_required_pull, *free_select_actual_progress);
|
||||
}
|
||||
}
|
||||
|
||||
let mut up_s_item_list: Vec<u32> = vec![];
|
||||
let mut up_a_item_list: Vec<u32> = vec![];
|
||||
let mut free_select_item_list: Vec<u32> = vec![];
|
||||
let mut chooseable_up_list: Vec<u32> = vec![];
|
||||
let mut chosen_up_item: u32 = 0;
|
||||
let mut s_guarantee: u32 = 0;
|
||||
let mut a_guarantee: u32 = 0;
|
||||
for rarity_items in target_pool.gacha_items.iter() {
|
||||
let mut chooseable_up_included_category_tags: Option<&HashSet<String>> = None;
|
||||
let mut chooseable_policy_tag: Option<&String> = None;
|
||||
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();
|
||||
if !category_guarantee_policy.chooseable {
|
||||
continue;
|
||||
}
|
||||
chooseable_policy_tag = Some(guarantee_policy_tag);
|
||||
chooseable_up_included_category_tags =
|
||||
Some(&category_guarantee_policy.included_category_tags);
|
||||
if let Some(item) = status_bin
|
||||
.rarity_status_map
|
||||
.get(&rarity_items.rarity)
|
||||
.unwrap()
|
||||
.categories_chosen_guarantee_item_map
|
||||
.get(guarantee_policy_tag)
|
||||
{
|
||||
chosen_up_item = item.clone();
|
||||
}
|
||||
}
|
||||
|
||||
for (category_tag, category) in rarity_items.categories.iter() {
|
||||
let probability_model = gachaconf
|
||||
.probability_model_map
|
||||
.get(&rarity_items.probability_model_tag)
|
||||
.unwrap();
|
||||
let maximum_pity = &probability_model.maximum_guarantee_pity;
|
||||
if rarity_items.rarity == common_properties.s_item_rarity {
|
||||
if category.is_promotional_items {
|
||||
up_s_item_list = category.item_ids.clone();
|
||||
}
|
||||
// tracing::info!("pity_s: {pity_s}");
|
||||
// thread 'tokio-runtime-worker' panicked at nap_gameserver\src\handlers\gacha.rs:369:31:
|
||||
// attempt to subtract with overflow
|
||||
s_guarantee = maximum_pity - min(pity_s, maximum_pity.clone()) + 1;
|
||||
}
|
||||
if rarity_items.rarity == common_properties.a_item_rarity {
|
||||
if category.is_promotional_items {
|
||||
up_a_item_list = category.item_ids.clone();
|
||||
}
|
||||
// tracing::info!("pity_a: {pity_a}");
|
||||
a_guarantee = maximum_pity - min(pity_a, maximum_pity.clone()) + 1;
|
||||
}
|
||||
|
||||
if let Some(val) = free_select_policy {
|
||||
if val.rarity == rarity_items.rarity && val.category_tags.contains(category_tag) {
|
||||
free_select_item_list.append(&mut category.item_ids.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(tags) = chooseable_up_included_category_tags {
|
||||
if tags.contains(category_tag) {
|
||||
chooseable_up_list.append(&mut category.item_ids.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(priority_policy_tag) = chooseable_policy_tag {
|
||||
// if let Some(item) = status_bin
|
||||
// .rarity_status_map
|
||||
// .get(&rarity_items.rarity)
|
||||
// .unwrap()
|
||||
// .categories_chosen_guarantee_item_map
|
||||
// .get(priority_policy_tag)
|
||||
// {
|
||||
if rarity_items.rarity == gachaconf.common_properties.s_item_rarity {
|
||||
up_s_item_list = vec![];
|
||||
} else if rarity_items.rarity == gachaconf.common_properties.a_item_rarity {
|
||||
up_a_item_list = vec![];
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,7 +551,7 @@ fn generate_gacha_info_from_pool(
|
|||
need_item_count: 1,
|
||||
}];
|
||||
|
||||
Gacha {
|
||||
let mut result = 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,
|
||||
|
@ -227,34 +559,46 @@ fn generate_gacha_info_from_pool(
|
|||
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,
|
||||
lpklhoobkbh: target_pool.gacha_parent_schedule_id,
|
||||
nammdglepbk: 593,
|
||||
hgmcofcjmbg: 101,
|
||||
free_select_progress,
|
||||
free_select_required_pull,
|
||||
free_select_item_list,
|
||||
chosen_up_item,
|
||||
// nammdglepbk: 563,
|
||||
// hgmcofcjmbg: 101,
|
||||
// akggbhgkifd: chooseable_up_list.clone(),
|
||||
chooseable_up_list,
|
||||
..Gacha::default()
|
||||
};
|
||||
if up_s_item_list.len() > 0 {
|
||||
result.up_s_item_list = up_s_item_list;
|
||||
}
|
||||
if up_a_item_list.len() > 0 {
|
||||
result.up_a_item_list = up_a_item_list;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn generate_all_gacha_info(_player: &Player) -> GachaData {
|
||||
fn generate_all_gacha_info(_player: &Player, now: &DateTime<Local>) -> 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() {
|
||||
if target_pool.is_still_open(now) {
|
||||
gacha_list.push(generate_gacha_info_from_pool(
|
||||
&gacha_bin,
|
||||
target_pool,
|
||||
&gachaconf.common_properties,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// tracing::info!("gacha_list: {:?}", gacha_list);
|
||||
GachaData {
|
||||
random_number: 0,
|
||||
random_number: 6167,
|
||||
gacha_pool: Some(GachaPool { gacha_list }),
|
||||
..GachaData::default()
|
||||
}
|
||||
|
|
|
@ -120,6 +120,8 @@ req_handlers! {
|
|||
quest::BeginArchiveBattleQuest;
|
||||
quest::FinishArchiveQuest;
|
||||
gacha::DoGacha;
|
||||
gacha::ChooseGachaUp;
|
||||
gacha::GachaFreeAgent;
|
||||
}
|
||||
|
||||
notify_handlers! {
|
||||
|
|
|
@ -52,12 +52,44 @@ impl GachaModel {
|
|||
&mut progress_bin.categories_progress_map,
|
||||
&category_guarantee_policy_tag,
|
||||
);
|
||||
|
||||
let guarantee_policy = gachaconf
|
||||
.category_guarantee_policy_map
|
||||
.get(category_guarantee_policy_tag)
|
||||
.unwrap();
|
||||
if !guarantee_policy.chooseable {
|
||||
continue;
|
||||
}
|
||||
get_or_add(
|
||||
&mut progress_bin.categories_chosen_guarantee_progress_map,
|
||||
&category_guarantee_policy_tag,
|
||||
);
|
||||
}
|
||||
}
|
||||
for discount_policy_tag in gacha_pool.discount_policy_tags.iter() {
|
||||
if gachaconf
|
||||
.discount_policies
|
||||
.free_select_map
|
||||
.contains_key(discount_policy_tag)
|
||||
{
|
||||
let policy = gachaconf
|
||||
.discount_policies
|
||||
.free_select_map
|
||||
.get(discount_policy_tag)
|
||||
.unwrap();
|
||||
get_or_add(
|
||||
&mut status_bin.discount_usage_map,
|
||||
&policy.free_select_progress_record_tag,
|
||||
);
|
||||
get_or_add(
|
||||
&mut status_bin.discount_usage_map,
|
||||
&policy.free_select_usage_record_tag,
|
||||
);
|
||||
} else {
|
||||
get_or_add(&mut status_bin.discount_usage_map, &discount_policy_tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -70,8 +102,14 @@ impl GachaModel {
|
|||
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);
|
||||
let result = determine_gacha_result(
|
||||
pull_time,
|
||||
category,
|
||||
target_pool,
|
||||
status_bin,
|
||||
progress_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);
|
||||
|
@ -196,9 +234,27 @@ fn determine_category<'bin, 'conf>(
|
|||
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
|
||||
// it is SELECTED then we MUST give that category's item.
|
||||
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();
|
||||
if !category_guarantee_policy.chooseable {
|
||||
continue;
|
||||
}
|
||||
// As we found a policy defined chooseable, we
|
||||
// should head to look whether the user chose
|
||||
// the category he want.
|
||||
if let Some(category_tag) = progress_bin
|
||||
.categories_chosen_guarantee_category_map
|
||||
.get(guarantee_policy_tag)
|
||||
{
|
||||
// User chose a category; our work are done here.
|
||||
category_tag_result.insert(category_tag.clone());
|
||||
category_tag_inited = true;
|
||||
}
|
||||
}
|
||||
// Then we should take a look at MustGainItem.
|
||||
if !category_tag_inited {
|
||||
for discount_policy_tag in target_pool.discount_policy_tags.iter() {
|
||||
|
@ -262,14 +318,49 @@ fn determine_gacha_result<'bin, 'conf>(
|
|||
category: &'conf GachaCategoryInfo,
|
||||
target_pool: &'conf CharacterGachaPool,
|
||||
status_bin: &'bin GachaStatusBin,
|
||||
progress_bin: &'bin GachaProgressBin,
|
||||
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
|
||||
let mut item_id: Option<&u32> = None;
|
||||
// We should see whether user's search priority exists.
|
||||
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();
|
||||
if !category_guarantee_policy.chooseable {
|
||||
continue;
|
||||
}
|
||||
// Firstly, judge whether the user failed enough times.
|
||||
// The user is limited to get only this category's item,
|
||||
// so we should record the user's failure to get his
|
||||
// selected item elsewhere.
|
||||
if progress_bin
|
||||
.categories_chosen_guarantee_progress_map
|
||||
.get(guarantee_policy_tag)
|
||||
.unwrap()
|
||||
< &category_guarantee_policy.trigger_on_failure_times
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// We directly look whether user chose an UP item.
|
||||
if let Some(item) = progress_bin
|
||||
.categories_chosen_guarantee_item_map
|
||||
.get(guarantee_policy_tag)
|
||||
{
|
||||
item_id = Some(item);
|
||||
}
|
||||
}
|
||||
|
||||
let item_id = match item_id {
|
||||
Some(val) => val,
|
||||
None => category
|
||||
.item_ids
|
||||
.get(rand::thread_rng().gen_range(0..item_pool_len) as usize)
|
||||
.unwrap();
|
||||
.unwrap(),
|
||||
};
|
||||
let mut extra_item_id: u32 = 0;
|
||||
let mut extra_item_count: u32 = 0;
|
||||
|
||||
|
@ -279,6 +370,7 @@ fn determine_gacha_result<'bin, 'conf>(
|
|||
.get(extra_items_policy_tag)
|
||||
.unwrap();
|
||||
// TODO: apply_on_owned_count in a context with bag
|
||||
// TODO: That's what RoleModel should do, not me.
|
||||
if extra_items_policy.apply_on_owned_count == 0 {
|
||||
extra_item_id = extra_items_policy.id;
|
||||
extra_item_count = extra_items_policy.count;
|
||||
|
@ -396,4 +488,18 @@ fn update_discount<'bin, 'conf>(
|
|||
*usage += 1;
|
||||
}
|
||||
}
|
||||
for (policy_tag, policy) in gachaconf.discount_policies.free_select_map.iter() {
|
||||
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 progress = status_bin
|
||||
.discount_usage_map
|
||||
.get_mut(&policy.free_select_progress_record_tag)
|
||||
.unwrap();
|
||||
*progress += 1;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6844,7 +6844,7 @@ pub struct Pofhbffcjap {
|
|||
#[derive(proto_gen::XorFields)]
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Nhffnnompfh {
|
||||
pub struct ChooseGachaUpScRsp {
|
||||
#[xor(4109)]
|
||||
#[prost(int32, tag = "2")]
|
||||
pub retcode: i32,
|
||||
|
@ -17992,7 +17992,7 @@ pub mod gbcnocdacgf {
|
|||
#[prost(message, tag = "12")]
|
||||
Summonee(super::Endadpkgkid),
|
||||
#[prost(message, tag = "13")]
|
||||
BuddyId(super::Ecjcmfjjgdp),
|
||||
Buddy(super::Ecjcmfjjgdp),
|
||||
#[prost(message, tag = "14")]
|
||||
DropItem(super::Mfbjkggafmo),
|
||||
#[prost(message, tag = "15")]
|
||||
|
@ -22295,7 +22295,7 @@ pub struct Gacha {
|
|||
pub gacha_info_list_webview: ::prost::alloc::string::String,
|
||||
#[xor(9009)]
|
||||
#[prost(uint32, tag = "419")]
|
||||
pub ekjlhhdekka: u32,
|
||||
pub chosen_up_item: u32,
|
||||
#[prost(string, tag = "923")]
|
||||
pub fjohnbicmce: ::prost::alloc::string::String,
|
||||
#[xor(1379)]
|
||||
|
@ -24558,7 +24558,7 @@ pub struct Obpccjhnbpe {
|
|||
#[derive(proto_gen::XorFields)]
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Pahjnbjogon {
|
||||
pub struct ChooseGachaUpCsReq {
|
||||
#[xor(3180)]
|
||||
#[prost(uint32, tag = "6")]
|
||||
pub item_id: u32,
|
||||
|
@ -27531,7 +27531,7 @@ pub struct TipsInfo {
|
|||
#[derive(proto_gen::XorFields)]
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Ieimfkpmegp {
|
||||
pub struct GachaFreeAgentCsReq {
|
||||
#[xor(378)]
|
||||
#[prost(uint32, tag = "12")]
|
||||
pub gacha_parent_schedule_id: u32,
|
||||
|
@ -31687,7 +31687,7 @@ pub struct Amhlhmjgcpk {
|
|||
#[derive(proto_gen::XorFields)]
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct Idonpiailid {
|
||||
pub struct GachaFreeAgentScRsp {
|
||||
#[xor(3883)]
|
||||
#[prost(int32, tag = "3")]
|
||||
pub retcode: i32,
|
||||
|
|
|
@ -117,6 +117,7 @@ pub struct MainCityModelBin {
|
|||
#[prost(uint32, tag = "3")]
|
||||
pub section_id: u32,
|
||||
}
|
||||
/// The progress record of a specified rarity. All maps' keys are category_guarantee_policy_tag.
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct GachaProgressBin {
|
||||
|
@ -129,12 +130,24 @@ pub struct GachaProgressBin {
|
|||
::prost::alloc::string::String,
|
||||
u32,
|
||||
>,
|
||||
/// The selected priority for a Chooseable category.
|
||||
/// The selected priority (category) for a Chooseable category.
|
||||
#[prost(map = "string, string", tag = "3")]
|
||||
pub category_chosen_guarantee_map: ::std::collections::HashMap<
|
||||
pub categories_chosen_guarantee_category_map: ::std::collections::HashMap<
|
||||
::prost::alloc::string::String,
|
||||
::prost::alloc::string::String,
|
||||
>,
|
||||
/// The selectedpriority (a specified item) for a Chooseable category.
|
||||
#[prost(map = "string, uint32", tag = "4")]
|
||||
pub categories_chosen_guarantee_item_map: ::std::collections::HashMap<
|
||||
::prost::alloc::string::String,
|
||||
u32,
|
||||
>,
|
||||
/// The failure times for selected priority (a specified item).
|
||||
#[prost(map = "string, uint32", tag = "5")]
|
||||
pub categories_chosen_guarantee_progress_map: ::std::collections::HashMap<
|
||||
::prost::alloc::string::String,
|
||||
u32,
|
||||
>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
|
|
Loading…
Reference in a new issue