forked from NewEriduPubSec/JaneDoe-ZS
Implement static config gacha needed
This commit is contained in:
parent
970dde020a
commit
dcb0e49345
12 changed files with 383 additions and 0 deletions
0
assets/GachaConfig/gacha.jsonc
Normal file
0
assets/GachaConfig/gacha.jsonc
Normal file
|
@ -18,3 +18,5 @@ tracing.workspace = true
|
||||||
|
|
||||||
# Internal
|
# Internal
|
||||||
proto.workspace = true
|
proto.workspace = true
|
||||||
|
jsonc-parser = { version = "0.23.0", features = ["serde"] }
|
||||||
|
chrono = { version = "0.4.38", features = ["serde"] }
|
||||||
|
|
229
nap_data/src/gacha/gacha_config.rs
Normal file
229
nap_data/src/gacha/gacha_config.rs
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
|
use chrono::{prelude::Local, DateTime};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use tracing;
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
pub struct ExtraItemsPolicy {
|
||||||
|
pub id: u32,
|
||||||
|
pub count: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub apply_on_owned_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
pub struct ProbabilityPoint {
|
||||||
|
pub start_pity: u32,
|
||||||
|
pub start_chance_percent: f64,
|
||||||
|
#[serde(default)]
|
||||||
|
pub increment_percent: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
pub struct ProbabilityModel {
|
||||||
|
#[serde(default)]
|
||||||
|
pub clear_status_on_higher_rarity_pulled: bool,
|
||||||
|
pub points: Vec<ProbabilityPoint>,
|
||||||
|
// This value is for display only, so it's set when
|
||||||
|
// the maximum guarantee is not equal to the
|
||||||
|
// automatically calculated value (commonly, less than).
|
||||||
|
#[serde(default)]
|
||||||
|
pub maximum_guarantee_pity: u32,
|
||||||
|
|
||||||
|
#[serde(skip_deserializing)]
|
||||||
|
probability_percents: Vec<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProbabilityModel {
|
||||||
|
fn get_maximum_guarantee(&self) -> u32 {
|
||||||
|
self.probability_percents.len() as u32 - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn post_configure(&mut self, tag: &String) {
|
||||||
|
self.points.sort_by_key(|point| point.start_pity);
|
||||||
|
|
||||||
|
let mut probability_percents: Vec<f64> = vec![0.0];
|
||||||
|
for (i, point) in self.points.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
let last_point = &self.points[i - 1];
|
||||||
|
let last_stop_percent = last_point.start_chance_percent
|
||||||
|
+ last_point.increment_percent
|
||||||
|
* (point.start_pity - last_point.start_pity) as f64;
|
||||||
|
if last_stop_percent > point.start_chance_percent {
|
||||||
|
tracing::warn!("Gacha - ProbabilityModel '{tag}': The start chance of '{point:?}' is less than the value inherited from the previous point.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut max_pity = 2000;
|
||||||
|
if i < self.points.len() - 1 {
|
||||||
|
let next_point = &self.points[i + 1];
|
||||||
|
max_pity = next_point.start_pity - 1;
|
||||||
|
let max_probability = point.start_chance_percent
|
||||||
|
+ point.increment_percent
|
||||||
|
* (next_point.start_pity - 1 - point.start_pity) as f64;
|
||||||
|
assert!(max_probability < 100.0, "Gacha - ProbabilityModel '{tag}': Probability already reached 100% in '{point:?}' (though points with higher pity left)");
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut pity = point.start_pity;
|
||||||
|
let mut percent = point.start_chance_percent;
|
||||||
|
while pity <= max_pity {
|
||||||
|
if max_pity >= 2000 && percent >= 100.0 {
|
||||||
|
probability_percents.push(100.0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
probability_percents.push(percent);
|
||||||
|
percent += point.increment_percent;
|
||||||
|
pity += 1;
|
||||||
|
}
|
||||||
|
assert!(pity <= 2000, "Gacha - ProbabilityModel '{tag}' (point {i}): Haven't reached 100% guarantee probability at Pity 2001. The current probability is {percent}%. Crazy.");
|
||||||
|
}
|
||||||
|
|
||||||
|
self.probability_percents = probability_percents;
|
||||||
|
if self.maximum_guarantee_pity <= 0 {
|
||||||
|
self.maximum_guarantee_pity = self.get_maximum_guarantee();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_chance_percent(&self, pity: &u32) -> f64 {
|
||||||
|
// The vec length is 1 bigger than the maximum pity (1-based)
|
||||||
|
let guarantee_pity = self.probability_percents.len() - 1;
|
||||||
|
let idx = *pity as usize;
|
||||||
|
if idx > guarantee_pity {
|
||||||
|
return self.probability_percents[guarantee_pity];
|
||||||
|
}
|
||||||
|
self.probability_percents[idx]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
pub struct CategoryGuaranteePolicy {
|
||||||
|
pub included_category_tags: HashSet<String>,
|
||||||
|
pub trigger_on_failure_times: u32,
|
||||||
|
pub clear_status_on_target_changed: bool,
|
||||||
|
pub chooseable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
pub struct TenPullDiscount {
|
||||||
|
pub use_limit: u32,
|
||||||
|
pub discounted_prize: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
pub struct AdvancedGuarantee {
|
||||||
|
pub use_limit: u32,
|
||||||
|
pub rarity: u32,
|
||||||
|
pub guarantee_pity: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
pub struct MustGainItem {
|
||||||
|
pub use_limit: u32,
|
||||||
|
pub rarity: u32,
|
||||||
|
pub category_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>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiscountPolicyCollection {
|
||||||
|
pub fn post_configure(&mut self) {
|
||||||
|
for (tag, ten_pull_discount) in self.ten_pull_discount_map.iter() {
|
||||||
|
let discounted_prize = ten_pull_discount.discounted_prize;
|
||||||
|
assert!(discounted_prize < 10, "Gacha - DiscountPolicy '{tag}': ten_pull_discount's value should be smaller than 10 (read {discounted_prize}).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
pub struct GachaCategoryInfo {
|
||||||
|
#[serde(default)]
|
||||||
|
pub is_promotional_items: bool,
|
||||||
|
pub item_ids: Vec<u32>,
|
||||||
|
pub category_weight: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
pub struct GachaAvailableItemsInfo {
|
||||||
|
pub rarity: u32,
|
||||||
|
#[serde(default)]
|
||||||
|
pub extra_items_policy_tags: Vec<String>,
|
||||||
|
pub categories: HashMap<String, GachaCategoryInfo>,
|
||||||
|
pub probability_model_tag: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub category_guarantee_policy_tags: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
pub struct CharacterGachaPool {
|
||||||
|
pub gacha_schedule_id: u32,
|
||||||
|
pub gacha_parent_schedule_id: u32,
|
||||||
|
pub comment: String,
|
||||||
|
pub gacha_type: u32,
|
||||||
|
pub cost_item_id: u32,
|
||||||
|
pub start_time: DateTime<Local>,
|
||||||
|
pub end_time: DateTime<Local>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub discount_policy_tags: Vec<String>,
|
||||||
|
pub sharing_guarantee_info_category: String,
|
||||||
|
pub gacha_items: Vec<GachaAvailableItemsInfo>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CharacterGachaPool {
|
||||||
|
pub fn is_still_open(&self, now: &DateTime<Local>) -> bool {
|
||||||
|
self.start_time <= *now && *now <= self.end_time
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn post_configure(&mut self, probability_model_map: &HashMap<String, ProbabilityModel>) {
|
||||||
|
self.gacha_items
|
||||||
|
.sort_by_key(|item_list| u32::MAX - item_list.rarity);
|
||||||
|
for items_info in self.gacha_items.iter_mut() {
|
||||||
|
assert!(probability_model_map.contains_key(&items_info.probability_model_tag), "Gacha - CharacterGachaPool '{}': Specified ProbabilityModel tag '{}' that does not exist.", self.gacha_schedule_id, items_info.probability_model_tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
pub struct GachaCommonProperties {
|
||||||
|
pub up_item_category_tag: String,
|
||||||
|
pub s_item_rarity: u32,
|
||||||
|
pub a_item_rarity: u32,
|
||||||
|
// TODO: PostConfigure check
|
||||||
|
pub ten_pull_discount_tag: String,
|
||||||
|
pub newcomer_advanced_s_tag: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Deserialize)]
|
||||||
|
pub struct GachaConfiguration {
|
||||||
|
pub character_gacha_pool_list: Vec<CharacterGachaPool>,
|
||||||
|
pub probability_model_map: HashMap<String, ProbabilityModel>,
|
||||||
|
pub category_guarantee_policy_map: HashMap<String, CategoryGuaranteePolicy>,
|
||||||
|
pub extra_items_policy_map: HashMap<String, ExtraItemsPolicy>,
|
||||||
|
pub discount_policies: DiscountPolicyCollection,
|
||||||
|
pub common_properties: GachaCommonProperties,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GachaConfiguration {
|
||||||
|
pub fn post_configure(&mut self) {
|
||||||
|
assert!(
|
||||||
|
self.category_guarantee_policy_map
|
||||||
|
.contains_key(&self.common_properties.up_item_category_tag),
|
||||||
|
"The UP category should be valid in policy map."
|
||||||
|
);
|
||||||
|
|
||||||
|
for (tag, policy) in self.probability_model_map.iter_mut() {
|
||||||
|
policy.post_configure(&tag);
|
||||||
|
}
|
||||||
|
self.discount_policies.post_configure();
|
||||||
|
for character_pool in self.character_gacha_pool_list.iter_mut() {
|
||||||
|
character_pool.post_configure(&self.probability_model_map);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
45
nap_data/src/gacha/mod.rs
Normal file
45
nap_data/src/gacha/mod.rs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
use std::sync::OnceLock;
|
||||||
|
|
||||||
|
use gacha_config::GachaConfiguration;
|
||||||
|
use jsonc_parser::{parse_to_serde_value, ParseOptions};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::{action::ActionConfig, DataLoadError};
|
||||||
|
|
||||||
|
pub mod gacha_config;
|
||||||
|
|
||||||
|
const GACHA_CONFIG_NAME: &str = "gacha.jsonc";
|
||||||
|
static GACHACONF: OnceLock<GachaConfiguration> = OnceLock::new();
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct EventGraphConfig {
|
||||||
|
pub event_id: u32,
|
||||||
|
pub actions: Vec<ActionConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn load_gacha_config(path: &str) -> Result<(), DataLoadError> {
|
||||||
|
let jsonc_data = std::fs::read_to_string(format!("{path}/{GACHA_CONFIG_NAME}"))
|
||||||
|
.map_err(|err| DataLoadError::IoError(err))?;
|
||||||
|
|
||||||
|
let json_value = parse_to_serde_value(
|
||||||
|
&jsonc_data,
|
||||||
|
&ParseOptions {
|
||||||
|
allow_comments: true,
|
||||||
|
allow_loose_object_property_names: false,
|
||||||
|
allow_trailing_commas: true,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.map_err(|err| DataLoadError::JsoncParseError(err))?
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut result = serde_json::from_value::<GachaConfiguration>(json_value)
|
||||||
|
.map_err(|err| DataLoadError::FromJsonError(String::from("GachaConfiguration"), err))?;
|
||||||
|
result.post_configure();
|
||||||
|
|
||||||
|
GACHACONF.set(result).unwrap();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn global_gacha_config() -> &'static GachaConfiguration {
|
||||||
|
GACHACONF.get().unwrap()
|
||||||
|
}
|
|
@ -1,9 +1,11 @@
|
||||||
pub mod action;
|
pub mod action;
|
||||||
pub mod event;
|
pub mod event;
|
||||||
|
pub mod gacha;
|
||||||
pub mod tables;
|
pub mod tables;
|
||||||
|
|
||||||
use std::{collections::HashMap, sync::OnceLock};
|
use std::{collections::HashMap, sync::OnceLock};
|
||||||
|
|
||||||
|
use jsonc_parser::errors::ParseError;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
@ -12,6 +14,7 @@ pub struct AssetsConfig {
|
||||||
pub filecfg_path: String,
|
pub filecfg_path: String,
|
||||||
pub event_config_path: String,
|
pub event_config_path: String,
|
||||||
pub usm_keys_path: String,
|
pub usm_keys_path: String,
|
||||||
|
pub gacha_config_path: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
|
@ -20,6 +23,8 @@ pub enum DataLoadError {
|
||||||
IoError(#[from] std::io::Error),
|
IoError(#[from] std::io::Error),
|
||||||
#[error("from_json failed for type {0}, error: {1}")]
|
#[error("from_json failed for type {0}, error: {1}")]
|
||||||
FromJsonError(String, serde_json::Error),
|
FromJsonError(String, serde_json::Error),
|
||||||
|
#[error("jsonc_parser parse as json error: {0}")]
|
||||||
|
JsoncParseError(#[from] ParseError),
|
||||||
}
|
}
|
||||||
|
|
||||||
static USM_KEY_MAP: OnceLock<HashMap<u32, u64>> = OnceLock::new();
|
static USM_KEY_MAP: OnceLock<HashMap<u32, u64>> = OnceLock::new();
|
||||||
|
@ -31,6 +36,7 @@ pub fn init_data(config: &AssetsConfig) -> Result<(), DataLoadError> {
|
||||||
tracing::warn!("failed to load USM keys, in-game cutscenes will not work! Reason: {err}");
|
tracing::warn!("failed to load USM keys, in-game cutscenes will not work! Reason: {err}");
|
||||||
USM_KEY_MAP.set(HashMap::new()).unwrap();
|
USM_KEY_MAP.set(HashMap::new()).unwrap();
|
||||||
}
|
}
|
||||||
|
gacha::load_gacha_config(&config.gacha_config_path)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ impl Default for NapGSConfig {
|
||||||
filecfg_path: String::from("assets/FileCfg"),
|
filecfg_path: String::from("assets/FileCfg"),
|
||||||
event_config_path: String::from("assets/EventConfig"),
|
event_config_path: String::from("assets/EventConfig"),
|
||||||
usm_keys_path: String::from("assets/VideoUSMEncKeys.json"),
|
usm_keys_path: String::from("assets/VideoUSMEncKeys.json"),
|
||||||
|
gacha_config_path: String::from("assets/GachaConfig"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
18
nap_gameserver/src/logic/gacha/gacha_model.rs
Normal file
18
nap_gameserver/src/logic/gacha/gacha_model.rs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
use proto::GachaModelBin;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct GachaModel {
|
||||||
|
pub gacha_bin: GachaModelBin,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GachaModel {
|
||||||
|
pub fn from_bin(gacha_bin: GachaModelBin) -> Self {
|
||||||
|
Self {
|
||||||
|
gacha_bin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_bin(&self) -> GachaModelBin {
|
||||||
|
self.gacha_bin.clone()
|
||||||
|
}
|
||||||
|
}
|
4
nap_gameserver/src/logic/gacha/mod.rs
Normal file
4
nap_gameserver/src/logic/gacha/mod.rs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
mod gacha_model;
|
||||||
|
mod record;
|
||||||
|
|
||||||
|
pub use gacha_model::GachaModel;
|
0
nap_gameserver/src/logic/gacha/record.rs
Normal file
0
nap_gameserver/src/logic/gacha/record.rs
Normal file
|
@ -1,5 +1,6 @@
|
||||||
pub mod battle;
|
pub mod battle;
|
||||||
mod enums;
|
mod enums;
|
||||||
|
pub mod gacha;
|
||||||
pub mod game;
|
pub mod game;
|
||||||
pub mod item;
|
pub mod item;
|
||||||
pub mod math;
|
pub mod math;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use data::tables::{self, AvatarBaseID};
|
use data::tables::{self, AvatarBaseID};
|
||||||
use proto::{ItemStatic, PlayerDataBin, Retcode};
|
use proto::{ItemStatic, PlayerDataBin, Retcode};
|
||||||
|
|
||||||
|
use super::gacha::GachaModel;
|
||||||
use super::game::{FrontendGame, FrontendGameError, GameInstance, LogicError};
|
use super::game::{FrontendGame, FrontendGameError, GameInstance, LogicError};
|
||||||
use super::item::{ItemModel, ItemUID};
|
use super::item::{ItemModel, ItemUID};
|
||||||
use super::main_city_model::MainCityModel;
|
use super::main_city_model::MainCityModel;
|
||||||
|
@ -16,6 +17,7 @@ pub struct Player {
|
||||||
pub role_model: RoleModel,
|
pub role_model: RoleModel,
|
||||||
pub item_model: ItemModel,
|
pub item_model: ItemModel,
|
||||||
pub main_city_model: MainCityModel,
|
pub main_city_model: MainCityModel,
|
||||||
|
pub gacha_model: GachaModel,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Player {
|
impl Player {
|
||||||
|
@ -28,6 +30,7 @@ impl Player {
|
||||||
role_model: Some(self.role_model.to_bin()),
|
role_model: Some(self.role_model.to_bin()),
|
||||||
item_model: Some(self.item_model.to_bin()),
|
item_model: Some(self.item_model.to_bin()),
|
||||||
main_city_model: Some(self.main_city_model.to_bin()),
|
main_city_model: Some(self.main_city_model.to_bin()),
|
||||||
|
gacha_model: Some(self.gacha_model.to_bin()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +47,7 @@ impl Player {
|
||||||
.main_city_model
|
.main_city_model
|
||||||
.map(MainCityModel::from_bin)
|
.map(MainCityModel::from_bin)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
|
gacha_model: bin.gacha_model.map(GachaModel::from_bin).unwrap_or_default(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,6 +119,77 @@ pub struct MainCityModelBin {
|
||||||
}
|
}
|
||||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct GachaProgressBin {
|
||||||
|
/// The pity (counting how many pulls) of this pull (in previous record) or the next pull (in status).
|
||||||
|
#[prost(uint32, tag = "1")]
|
||||||
|
pub pity: u32,
|
||||||
|
/// The failure times of this category.
|
||||||
|
#[prost(map = "string, uint32", tag = "2")]
|
||||||
|
pub categories_progress_map: ::std::collections::HashMap<
|
||||||
|
::prost::alloc::string::String,
|
||||||
|
u32,
|
||||||
|
>,
|
||||||
|
/// The selected priority for a Chooseable category.
|
||||||
|
#[prost(map = "string, string", tag = "3")]
|
||||||
|
pub category_chosen_guarantee_map: ::std::collections::HashMap<
|
||||||
|
::prost::alloc::string::String,
|
||||||
|
::prost::alloc::string::String,
|
||||||
|
>,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct GachaExtraItemBin {
|
||||||
|
#[prost(uint32, tag = "1")]
|
||||||
|
pub extra_item_id: u32,
|
||||||
|
#[prost(uint32, tag = "2")]
|
||||||
|
pub extra_item_count: u32,
|
||||||
|
/// How many objects of the main item obtained in gacha is present in the player's bag (before gacha).
|
||||||
|
/// This is used for something like converting when there're extra characters.
|
||||||
|
#[prost(uint32, tag = "3")]
|
||||||
|
pub currently_gained: u32,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct GachaRecordBin {
|
||||||
|
#[prost(int64, tag = "1")]
|
||||||
|
pub pull_timestamp: i64,
|
||||||
|
#[prost(uint32, tag = "2")]
|
||||||
|
pub obtained_item_id: u32,
|
||||||
|
#[prost(uint32, tag = "3")]
|
||||||
|
pub gacha_id: u32,
|
||||||
|
/// The progress BEFORE this gacha is performed. uint32 is rarity.
|
||||||
|
#[prost(map = "uint32, message", tag = "4")]
|
||||||
|
pub progress_map: ::std::collections::HashMap<u32, GachaProgressBin>,
|
||||||
|
#[prost(message, optional, tag = "5")]
|
||||||
|
pub extra_item_bin: ::core::option::Option<GachaExtraItemBin>,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct GachaStatusBin {
|
||||||
|
#[prost(map = "uint32, message", tag = "1")]
|
||||||
|
pub rarity_status_map: ::std::collections::HashMap<u32, GachaProgressBin>,
|
||||||
|
#[prost(map = "string, uint32", tag = "2")]
|
||||||
|
pub discount_usage_map: ::std::collections::HashMap<
|
||||||
|
::prost::alloc::string::String,
|
||||||
|
u32,
|
||||||
|
>,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
|
pub struct GachaModelBin {
|
||||||
|
/// Gacha Status query. string is sharing_guarantee_info_category.
|
||||||
|
#[prost(map = "string, message", tag = "1")]
|
||||||
|
pub gacha_status_map: ::std::collections::HashMap<
|
||||||
|
::prost::alloc::string::String,
|
||||||
|
GachaStatusBin,
|
||||||
|
>,
|
||||||
|
#[prost(message, repeated, tag = "2")]
|
||||||
|
pub gacha_records: ::prost::alloc::vec::Vec<GachaRecordBin>,
|
||||||
|
#[prost(uint32, tag = "3")]
|
||||||
|
pub random_number: u32,
|
||||||
|
}
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||||
pub struct PlayerDataBin {
|
pub struct PlayerDataBin {
|
||||||
#[prost(message, optional, tag = "1")]
|
#[prost(message, optional, tag = "1")]
|
||||||
pub basic_data_model: ::core::option::Option<BasicDataModelBin>,
|
pub basic_data_model: ::core::option::Option<BasicDataModelBin>,
|
||||||
|
@ -130,4 +201,6 @@ pub struct PlayerDataBin {
|
||||||
pub item_model: ::core::option::Option<ItemModelBin>,
|
pub item_model: ::core::option::Option<ItemModelBin>,
|
||||||
#[prost(message, optional, tag = "5")]
|
#[prost(message, optional, tag = "5")]
|
||||||
pub main_city_model: ::core::option::Option<MainCityModelBin>,
|
pub main_city_model: ::core::option::Option<MainCityModelBin>,
|
||||||
|
#[prost(message, optional, tag = "6")]
|
||||||
|
pub gacha_model: ::core::option::Option<GachaModelBin>,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue