forked from NewEriduPubSec/JaneDoe-ZS
Separate Gacha logic & DTO, Fix items issue
This commit is contained in:
parent
b2401d566b
commit
bb058b451d
12 changed files with 63267 additions and 826 deletions
62086
assets/FileCfg/ItemTemplateTb.json
Normal file
62086
assets/FileCfg/ItemTemplateTb.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -168,6 +168,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:Bangboo": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_BANGBOO",
|
||||
"item_ids": [
|
||||
54002, // 阿全
|
||||
54005, // 艾米莉安
|
||||
|
@ -180,6 +181,7 @@
|
|||
"category_weight": 1
|
||||
},
|
||||
"Event-Exclusive:Bangboo": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_BANGBOO",
|
||||
"item_ids": [
|
||||
54004, // 巴特勒
|
||||
],
|
||||
|
@ -199,6 +201,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:Bangboo": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_BANGBOO",
|
||||
"item_ids": [
|
||||
53001, // 企鹅布
|
||||
53003, // 寻宝布
|
||||
|
@ -224,6 +227,7 @@
|
|||
"probability_model_tag": "get-B",
|
||||
"categories": {
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
12001, // 「月相」-望
|
||||
12002, // 「月相」-晦
|
||||
|
@ -267,6 +271,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"item_ids": [
|
||||
1021, // 猫又
|
||||
1101, // 珂蕾妲
|
||||
|
@ -278,6 +283,7 @@
|
|||
"category_weight": 50
|
||||
},
|
||||
"Event-Exclusive:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"is_promotional_items": true,
|
||||
"item_ids": [
|
||||
1191, // 艾莲
|
||||
|
@ -300,6 +306,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"item_ids": [
|
||||
1011, // 安比
|
||||
1031, // 妮可
|
||||
|
@ -312,6 +319,7 @@
|
|||
"category_weight": 3525
|
||||
},
|
||||
"Event-Exclusive:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"is_promotional_items": true,
|
||||
"item_ids": [
|
||||
1111, // 安东
|
||||
|
@ -320,6 +328,7 @@
|
|||
"category_weight": 3525
|
||||
},
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
13001, // 街头巨星
|
||||
13002, // 时光切片
|
||||
|
@ -352,6 +361,7 @@
|
|||
"probability_model_tag": "get-B",
|
||||
"categories": {
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
12001, // 「月相」-望
|
||||
12002, // 「月相」-晦
|
||||
|
@ -398,6 +408,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
14102, // 钢铁肉垫
|
||||
14110, // 燃狱齿轮
|
||||
|
@ -409,6 +420,7 @@
|
|||
"category_weight": 25
|
||||
},
|
||||
"Event-Exclusive:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
14119, // 深海访客
|
||||
],
|
||||
|
@ -433,6 +445,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"item_ids": [
|
||||
1011, // 安比
|
||||
1031, // 妮可
|
||||
|
@ -447,6 +460,7 @@
|
|||
"category_weight": 3750
|
||||
},
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
13001, // 街头巨星
|
||||
13002, // 时光切片
|
||||
|
@ -468,6 +482,7 @@
|
|||
"category_weight": 13125
|
||||
},
|
||||
"Event-Exclusive:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
13113, // 含羞恶面
|
||||
13111, // 旋钻机-赤轴
|
||||
|
@ -485,6 +500,7 @@
|
|||
"probability_model_tag": "get-B",
|
||||
"categories": {
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
12001, // 「月相」-望
|
||||
12002, // 「月相」-晦
|
||||
|
@ -528,6 +544,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"item_ids": [
|
||||
1021, // 猫又
|
||||
1101, // 珂蕾妲
|
||||
|
@ -539,6 +556,7 @@
|
|||
"category_weight": 50
|
||||
},
|
||||
"Event-Exclusive:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"is_promotional_items": true,
|
||||
"item_ids": [
|
||||
1241 // 朱鸢
|
||||
|
@ -558,6 +576,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"item_ids": [
|
||||
1011, // 安比
|
||||
1061, // 可琳
|
||||
|
@ -570,6 +589,7 @@
|
|||
"category_weight": 3525
|
||||
},
|
||||
"Event-Exclusive:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"is_promotional_items": true,
|
||||
"item_ids": [
|
||||
1031, // 妮可
|
||||
|
@ -578,6 +598,7 @@
|
|||
"category_weight": 3525
|
||||
},
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
13001, // 街头巨星
|
||||
13002, // 时光切片
|
||||
|
@ -610,6 +631,7 @@
|
|||
"probability_model_tag": "get-B",
|
||||
"categories": {
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
12001, // 「月相」-望
|
||||
12002, // 「月相」-晦
|
||||
|
@ -653,6 +675,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
14102, // 钢铁肉垫
|
||||
14110, // 燃狱齿轮
|
||||
|
@ -664,6 +687,7 @@
|
|||
"category_weight": 25
|
||||
},
|
||||
"Event-Exclusive:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
14124 // 防暴者VI型
|
||||
],
|
||||
|
@ -683,6 +707,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"item_ids": [
|
||||
1011, // 安比
|
||||
1031, // 妮可
|
||||
|
@ -697,6 +722,7 @@
|
|||
"category_weight": 3750
|
||||
},
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
13007, // 正版变身器
|
||||
13002, // 时光切片
|
||||
|
@ -718,6 +744,7 @@
|
|||
"category_weight": 13125
|
||||
},
|
||||
"Event-Exclusive:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
13103, // 聚宝箱
|
||||
13112, // 比格气缸
|
||||
|
@ -735,6 +762,7 @@
|
|||
"probability_model_tag": "get-B",
|
||||
"categories": {
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
12001, // 「月相」-望
|
||||
12002, // 「月相」-晦
|
||||
|
@ -780,6 +808,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"item_ids": [
|
||||
1021, // 猫又
|
||||
1101, // 珂蕾妲
|
||||
|
@ -791,6 +820,7 @@
|
|||
"category_weight": 50
|
||||
},
|
||||
"Event-Exclusive:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"is_promotional_items": true,
|
||||
"item_ids": [
|
||||
1251, // 青衣
|
||||
|
@ -810,6 +840,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"item_ids": [
|
||||
1011, // 安比
|
||||
1031, // 妮可
|
||||
|
@ -823,6 +854,7 @@
|
|||
"category_weight": 3525
|
||||
},
|
||||
"Event-Exclusive:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"is_promotional_items": true,
|
||||
"item_ids": [
|
||||
1131, // 苍角
|
||||
|
@ -831,6 +863,7 @@
|
|||
"category_weight": 3525
|
||||
},
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
13001, // 街头巨星
|
||||
13002, // 时光切片
|
||||
|
@ -864,6 +897,7 @@
|
|||
"probability_model_tag": "get-B",
|
||||
"categories": {
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
12001, // 「月相」-望
|
||||
12002, // 「月相」-晦
|
||||
|
@ -907,6 +941,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
14102, // 钢铁肉垫
|
||||
14110, // 燃狱齿轮
|
||||
|
@ -918,6 +953,7 @@
|
|||
"category_weight": 25
|
||||
},
|
||||
"Event-Exclusive:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
14125, // 玉壶青冰
|
||||
],
|
||||
|
@ -937,6 +973,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"item_ids": [
|
||||
1011, // 安比
|
||||
1031, // 妮可
|
||||
|
@ -949,6 +986,7 @@
|
|||
"category_weight": 3750
|
||||
},
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
13001, // 街头巨星
|
||||
13002, // 时光切片
|
||||
|
@ -971,6 +1009,7 @@
|
|||
"category_weight": 13125
|
||||
},
|
||||
"Event-Exclusive:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
13113, // 含羞恶面
|
||||
13127, // 维序者-特化型
|
||||
|
@ -988,6 +1027,7 @@
|
|||
"probability_model_tag": "get-B",
|
||||
"categories": {
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
12001, // 「月相」-望
|
||||
12002, // 「月相」-晦
|
||||
|
@ -1031,6 +1071,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"item_ids": [
|
||||
1021, // 猫又
|
||||
1101, // 珂蕾妲
|
||||
|
@ -1042,6 +1083,7 @@
|
|||
"category_weight": 50
|
||||
},
|
||||
"Event-Exclusive:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"is_promotional_items": true,
|
||||
"item_ids": [
|
||||
1261, // 简
|
||||
|
@ -1061,6 +1103,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"item_ids": [
|
||||
1011, // 安比
|
||||
1031, // 妮可
|
||||
|
@ -1074,6 +1117,7 @@
|
|||
"category_weight": 3525
|
||||
},
|
||||
"Event-Exclusive:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"is_promotional_items": true,
|
||||
"item_ids": [
|
||||
1271, // 赛斯
|
||||
|
@ -1082,6 +1126,7 @@
|
|||
"category_weight": 3525
|
||||
},
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
13001, // 街头巨星
|
||||
13002, // 时光切片
|
||||
|
@ -1115,6 +1160,7 @@
|
|||
"probability_model_tag": "get-B",
|
||||
"categories": {
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
12001, // 「月相」-望
|
||||
12002, // 「月相」-晦
|
||||
|
@ -1158,6 +1204,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
14102, // 钢铁肉垫
|
||||
14110, // 燃狱齿轮
|
||||
|
@ -1169,6 +1216,7 @@
|
|||
"category_weight": 25
|
||||
},
|
||||
"Event-Exclusive:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
14126, // 淬锋钳刺
|
||||
],
|
||||
|
@ -1188,6 +1236,7 @@
|
|||
],
|
||||
"categories": {
|
||||
"Standard:Agent": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
"item_ids": [
|
||||
1011, // 安比
|
||||
1031, // 妮可
|
||||
|
@ -1200,6 +1249,7 @@
|
|||
"category_weight": 3750
|
||||
},
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
13001, // 街头巨星
|
||||
13002, // 时光切片
|
||||
|
@ -1222,6 +1272,7 @@
|
|||
"category_weight": 13125
|
||||
},
|
||||
"Event-Exclusive:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
13127, // 维序者-特化型
|
||||
13113, // 含羞恶面
|
||||
|
@ -1239,6 +1290,7 @@
|
|||
"probability_model_tag": "get-B",
|
||||
"categories": {
|
||||
"Standard:W-Engine": {
|
||||
"item_type": "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
"item_ids": [
|
||||
12001, // 「月相」-望
|
||||
12002, // 「月相」-晦
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use chrono::{prelude::Local, DateTime};
|
||||
use proto::GachaAddedItemType;
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use tracing;
|
||||
|
||||
|
@ -152,13 +151,64 @@ impl DiscountPolicyCollection {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum GachaAddedItemType {
|
||||
#[default]
|
||||
None = 0,
|
||||
Weapon = 1,
|
||||
Character = 2,
|
||||
Bangboo = 3,
|
||||
}
|
||||
|
||||
impl GachaAddedItemType {
|
||||
pub fn as_str_name(&self) -> &'static str {
|
||||
match self {
|
||||
GachaAddedItemType::None => "GACHA_ADDED_ITEM_TYPE_NONE",
|
||||
GachaAddedItemType::Weapon => "GACHA_ADDED_ITEM_TYPE_WEAPON",
|
||||
GachaAddedItemType::Character => "GACHA_ADDED_ITEM_TYPE_CHARACTER",
|
||||
GachaAddedItemType::Bangboo => "GACHA_ADDED_ITEM_TYPE_BANGBOO",
|
||||
}
|
||||
}
|
||||
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
|
||||
match value {
|
||||
"GACHA_ADDED_ITEM_TYPE_NONE" => Some(Self::None),
|
||||
"GACHA_ADDED_ITEM_TYPE_WEAPON" => Some(Self::Weapon),
|
||||
"GACHA_ADDED_ITEM_TYPE_CHARACTER" => Some(Self::Character),
|
||||
"GACHA_ADDED_ITEM_TYPE_BANGBOO" => Some(Self::Bangboo),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<i32> for GachaAddedItemType {
|
||||
fn from(value: i32) -> Self {
|
||||
match value {
|
||||
1 => Self::Weapon,
|
||||
2 => Self::Character,
|
||||
3 => Self::Bangboo,
|
||||
_ => Self::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<i32> for GachaAddedItemType {
|
||||
fn into(self) -> i32 {
|
||||
match self {
|
||||
Self::Weapon => 1,
|
||||
Self::Character => 2,
|
||||
Self::Bangboo => 3,
|
||||
Self::None => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
pub struct GachaCategoryInfo {
|
||||
#[serde(default)]
|
||||
pub is_promotional_items: bool,
|
||||
pub item_ids: Vec<u32>,
|
||||
pub category_weight: u32,
|
||||
#[serde(default, deserialize_with = "from_str")]
|
||||
#[serde(deserialize_with = "from_str")]
|
||||
pub item_type: GachaAddedItemType,
|
||||
}
|
||||
|
||||
|
|
10
nap_data/src/tables/item_template.rs
Normal file
10
nap_data/src/tables/item_template.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use serde::Deserialize;
|
||||
|
||||
template_id!(Item u32 id);
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
pub struct ItemTemplate {
|
||||
#[serde(rename = "ID")]
|
||||
pub id: ItemID,
|
||||
}
|
|
@ -97,4 +97,5 @@ template_tables! {
|
|||
QuickAccessTemplate;
|
||||
QuickFuncTemplate;
|
||||
TeleportConfigTemplate;
|
||||
ItemTemplate;
|
||||
}
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
use std::{
|
||||
cmp::min,
|
||||
collections::{
|
||||
hash_map::Entry::{Occupied, Vacant},
|
||||
HashSet,
|
||||
},
|
||||
};
|
||||
|
||||
use data::{
|
||||
gacha::{gacha_config::*, global_gacha_config},
|
||||
tables::{AvatarBaseID, WeaponID},
|
||||
gacha::{
|
||||
gacha_config::{CharacterGachaPool, GachaAddedItemType},
|
||||
global_gacha_config,
|
||||
},
|
||||
tables::{AvatarBaseID, ItemID, WeaponID},
|
||||
};
|
||||
use proto::{GainItemInfo, GetGachaDataScRsp};
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
handlers::core::NetError,
|
||||
logic::{item::ItemModel, role::RoleModel},
|
||||
};
|
||||
use chrono::{DateTime, Local};
|
||||
use proto::*;
|
||||
|
||||
pub async fn on_get_gacha_data(
|
||||
_session: &NetSession,
|
||||
|
@ -32,7 +31,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, &Local::now())),
|
||||
gacha_data: Some(_player.gacha_model.to_client(&Local::now())),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -51,101 +50,58 @@ pub async fn on_do_gacha(
|
|||
&gachaconf.character_gacha_pool_list,
|
||||
&req.gacha_parent_schedule_id,
|
||||
&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::RetFail.into(),
|
||||
..Default::default()
|
||||
});
|
||||
};
|
||||
let target_pool = target_pool.unwrap();
|
||||
)?;
|
||||
|
||||
// tracing::info!("cost_item_count: {}", req.cost_item_count);
|
||||
let mut pull_count = if req.cost_item_count > 1 { 10 } else { 1 };
|
||||
let cost_count = pull_count;
|
||||
|
||||
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;
|
||||
// cost_count = discount_policy.discounted_prize;
|
||||
}
|
||||
}
|
||||
}
|
||||
if cost_count != req.cost_item_count {
|
||||
let pull_count = if req.cost_item_count > 1 { 10 } else { 1 };
|
||||
let mut cost_count = gacha_model.get_actual_cost_count(target_pool, &pull_count);
|
||||
if pull_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()
|
||||
});
|
||||
return Err(NetError::from(Retcode::RetFail));
|
||||
} else {
|
||||
// TODO: cost resource
|
||||
}
|
||||
|
||||
let mut gain_item_list: Vec<GainItemInfo> = vec![];
|
||||
while pull_count > 0 {
|
||||
while cost_count > 0 {
|
||||
let pull_result = gacha_model.perform_pull_pool(&pull_time, target_pool);
|
||||
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) {
|
||||
Some(id) => item_model.add_weapon(id).value(),
|
||||
None => 0,
|
||||
},
|
||||
GachaAddedItemType::Character => {
|
||||
match AvatarBaseID::new(pull_result.obtained_item_id) {
|
||||
Some(id) => {
|
||||
role_model.add_avatar(id);
|
||||
0
|
||||
}
|
||||
None => 0,
|
||||
}
|
||||
}
|
||||
_ => 0,
|
||||
},
|
||||
Err(_err) => 0,
|
||||
};
|
||||
if extra_item_bin.extra_item_id != 0 {
|
||||
item_model.add_resource(
|
||||
extra_item_bin.extra_item_id,
|
||||
extra_item_bin.extra_item_count,
|
||||
|
||||
let uid = add_item(
|
||||
role_model,
|
||||
item_model,
|
||||
&pull_result.obtained_item_id,
|
||||
&pull_result.item_type,
|
||||
)?;
|
||||
let (mut extra_item_id, mut extra_item_count) = (0, 0);
|
||||
if let Some(extra_resources) = &pull_result.extra_resources {
|
||||
(extra_item_id, extra_item_count) = (
|
||||
extra_resources.extra_item_id.value(),
|
||||
extra_resources.extra_item_count.clone(),
|
||||
);
|
||||
item_model.add_resource(extra_item_id, extra_item_count);
|
||||
}
|
||||
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,
|
||||
item_id: pull_result.obtained_item_id.value(),
|
||||
extra_item_id,
|
||||
extra_item_count,
|
||||
uid,
|
||||
num: 1,
|
||||
..GainItemInfo::default()
|
||||
});
|
||||
pull_count -= 1;
|
||||
gacha_model.gacha_bin.gacha_records.push(pull_result);
|
||||
cost_count -= 1;
|
||||
gacha_model.gacha_records.push(pull_result);
|
||||
}
|
||||
|
||||
_session
|
||||
.notify(construct_sync(role_model, item_model))
|
||||
.await?;
|
||||
Ok(DoGachaScRsp {
|
||||
retcode: Retcode::RetSucc.into(),
|
||||
gain_item_list,
|
||||
gacha_data: Some(generate_all_gacha_info(_player, &pull_time)),
|
||||
gacha_data: Some(gacha_model.to_client(&pull_time)),
|
||||
cost_item_count: req.cost_item_count,
|
||||
})
|
||||
}
|
||||
|
@ -156,123 +112,31 @@ pub async fn on_gacha_free_agent(
|
|||
req: GachaFreeAgentCsReq,
|
||||
) -> NetResult<GachaFreeAgentScRsp> {
|
||||
let gachaconf = global_gacha_config();
|
||||
let gacha_model = &mut _player.gacha_model;
|
||||
let role_model = &mut _player.role_model;
|
||||
let item_model = &mut _player.item_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);
|
||||
}
|
||||
let item_id = ItemID::new(req.avatar_id);
|
||||
if let None = item_id {
|
||||
return Err(NetError::from(Retcode::RetFail));
|
||||
}
|
||||
let item_id = item_id.unwrap();
|
||||
|
||||
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()
|
||||
});
|
||||
let item_type = gacha_model.request_free_agent(target_pool, &item_id);
|
||||
if item_type == GachaAddedItemType::None {
|
||||
return Err(NetError::from(Retcode::RetFail));
|
||||
}
|
||||
let _ = add_item(role_model, item_model, &item_id, &item_type);
|
||||
|
||||
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;
|
||||
|
||||
_session
|
||||
.notify(construct_sync(role_model, item_model))
|
||||
.await?;
|
||||
Ok(GachaFreeAgentScRsp {
|
||||
retcode: Retcode::RetSucc.into(),
|
||||
})
|
||||
|
@ -290,330 +154,82 @@ pub async fn on_choose_gacha_up(
|
|||
&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()
|
||||
});
|
||||
}
|
||||
let item_id = ItemID::new(req.item_id);
|
||||
if let None = item_id {
|
||||
return Err(NetError::from(Retcode::RetFail));
|
||||
}
|
||||
let item_id = item_id.unwrap();
|
||||
|
||||
Ok(ChooseGachaUpScRsp {
|
||||
retcode: Retcode::RetFail.into(),
|
||||
retcode: if gacha_model.choose_gacha_up(target_pool, &item_id) {
|
||||
Retcode::RetSucc.into()
|
||||
} else {
|
||||
Retcode::RetFail.into()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
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 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
|
||||
.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;
|
||||
}
|
||||
} 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![];
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
let need_item_info_list: Vec<NeedItemInfo> = vec![NeedItemInfo {
|
||||
need_item_id: target_pool.cost_item_id,
|
||||
need_item_count: 1,
|
||||
}];
|
||||
|
||||
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,
|
||||
start_timestamp: target_pool.start_time.timestamp(),
|
||||
end_timestamp: target_pool.end_time.timestamp(),
|
||||
discount_avaliable_num,
|
||||
discount_ten_roll_prize,
|
||||
advanced_s_guarantee,
|
||||
s_guarantee,
|
||||
a_guarantee,
|
||||
need_item_info_list,
|
||||
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, 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: 6167,
|
||||
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> {
|
||||
) -> NetResult<&'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);
|
||||
return Ok(target_pool);
|
||||
}
|
||||
}
|
||||
None
|
||||
tracing::info!(
|
||||
"refuse gacha op because: pool of parent_schedule_id {} not found or isn't in open time",
|
||||
gacha_parent_schedule_id
|
||||
);
|
||||
Err(NetError::from(Retcode::RetFail))
|
||||
}
|
||||
|
||||
/// Return is item UID (weapon specific)
|
||||
fn add_item(
|
||||
role_model: &mut RoleModel,
|
||||
item_model: &mut ItemModel,
|
||||
item_id: &ItemID,
|
||||
item_type: &GachaAddedItemType,
|
||||
) -> NetResult<u32> {
|
||||
match item_type {
|
||||
GachaAddedItemType::Character => match AvatarBaseID::new(item_id.value()) {
|
||||
Some(avatar_id) => {
|
||||
role_model.add_avatar(avatar_id);
|
||||
Ok(0)
|
||||
}
|
||||
None => {
|
||||
tracing::info!("add item failed for avatar id {item_id}");
|
||||
Err(NetError::from(Retcode::RetFail))
|
||||
}
|
||||
},
|
||||
GachaAddedItemType::Weapon => match WeaponID::new(item_id.value()) {
|
||||
Some(weapon_id) => Ok(item_model.add_weapon(weapon_id).value()),
|
||||
None => {
|
||||
tracing::info!("add item failed for weapon id {item_id}");
|
||||
Err(NetError::from(Retcode::RetFail))
|
||||
}
|
||||
},
|
||||
GachaAddedItemType::Bangboo => Ok(0),
|
||||
_ => {
|
||||
tracing::info!(
|
||||
"add item failed due to undefined item type (from {item_id}) in configuration"
|
||||
);
|
||||
Err(NetError::from(Retcode::RetFail))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn construct_sync(role_model: &RoleModel, item_model: &ItemModel) -> PlayerSyncScNotify {
|
||||
PlayerSyncScNotify {
|
||||
avatar: Some(role_model.avatar_sync()),
|
||||
item_sync: Some(item_model.item_sync()),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
|
440
nap_gameserver/src/logic/gacha/client_op.rs
Normal file
440
nap_gameserver/src/logic/gacha/client_op.rs
Normal file
|
@ -0,0 +1,440 @@
|
|||
use data::gacha::gacha_config::*;
|
||||
use data::gacha::global_gacha_config;
|
||||
use data::tables::ItemID;
|
||||
|
||||
use chrono::{DateTime, Local};
|
||||
use proto::{Gacha, GachaData, GachaPool, NeedItemInfo};
|
||||
use std::{
|
||||
cmp::min,
|
||||
collections::{
|
||||
hash_map::Entry::{Occupied, Vacant},
|
||||
HashSet,
|
||||
},
|
||||
};
|
||||
|
||||
use super::GachaModel;
|
||||
|
||||
impl GachaModel {
|
||||
pub fn to_client(&self, now: &DateTime<Local>) -> GachaData {
|
||||
let gachaconf = global_gacha_config();
|
||||
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(
|
||||
self.generate_gacha_info_from_pool(target_pool, &gachaconf.common_properties),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// tracing::info!("gacha_list: {:?}", gacha_list);
|
||||
GachaData {
|
||||
random_number: 6167,
|
||||
gacha_pool: Some(GachaPool { gacha_list }),
|
||||
..GachaData::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_gacha_info_from_pool(
|
||||
&self,
|
||||
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 = self
|
||||
.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 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
|
||||
.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;
|
||||
}
|
||||
} 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 = chooseable_up_list.clone();
|
||||
} else if rarity_items.rarity == gachaconf.common_properties.a_item_rarity {
|
||||
up_a_item_list = vec![];
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
let need_item_info_list: Vec<NeedItemInfo> = vec![NeedItemInfo {
|
||||
need_item_id: target_pool.cost_item_id,
|
||||
need_item_count: 1,
|
||||
}];
|
||||
|
||||
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,
|
||||
start_timestamp: target_pool.start_time.timestamp(),
|
||||
end_timestamp: target_pool.end_time.timestamp(),
|
||||
discount_avaliable_num,
|
||||
discount_ten_roll_prize,
|
||||
advanced_s_guarantee,
|
||||
s_guarantee,
|
||||
a_guarantee,
|
||||
need_item_info_list,
|
||||
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
|
||||
}
|
||||
|
||||
/// Get the actual item cost count (counting discount).
|
||||
pub fn get_actual_cost_count<'bin, 'conf>(
|
||||
&'bin mut self,
|
||||
target_pool: &'conf CharacterGachaPool,
|
||||
pull_count: &u32,
|
||||
) -> u32 {
|
||||
let gachaconf = global_gacha_config();
|
||||
|
||||
if *pull_count == 10 {
|
||||
let discount_tag = &gachaconf.common_properties.ten_pull_discount_tag;
|
||||
if target_pool.discount_policy_tags.contains(&discount_tag) {
|
||||
let status_bin = self
|
||||
.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;
|
||||
return discount_policy.discounted_prize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pull_count.clone()
|
||||
}
|
||||
|
||||
pub fn request_free_agent<'bin, 'conf>(
|
||||
&'bin mut self,
|
||||
target_pool: &'conf CharacterGachaPool,
|
||||
item_id: &ItemID,
|
||||
) -> GachaAddedItemType {
|
||||
let gachaconf = global_gacha_config();
|
||||
let sharing_guarantee_category_tag = &target_pool.sharing_guarantee_info_category;
|
||||
let status_bin = self
|
||||
.gacha_status_map
|
||||
.get_mut(sharing_guarantee_category_tag)
|
||||
.unwrap();
|
||||
let item_id = item_id.value();
|
||||
|
||||
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)",
|
||||
target_pool.gacha_parent_schedule_id
|
||||
);
|
||||
return GachaAddedItemType::None;
|
||||
} 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}",
|
||||
target_pool.gacha_parent_schedule_id
|
||||
);
|
||||
return GachaAddedItemType::None;
|
||||
}
|
||||
|
||||
let free_select_policy = free_select_policy.unwrap();
|
||||
let mut item_type: GachaAddedItemType = GachaAddedItemType::None;
|
||||
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;
|
||||
}
|
||||
if category.item_ids.contains(&item_id) {
|
||||
item_type = category.item_type.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if item_type != GachaAddedItemType::None {
|
||||
(*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;
|
||||
}
|
||||
item_type
|
||||
}
|
||||
|
||||
pub fn choose_gacha_up<'bin, 'conf>(
|
||||
&'bin mut self,
|
||||
target_pool: &'conf CharacterGachaPool,
|
||||
item_id: &ItemID,
|
||||
) -> bool {
|
||||
let gachaconf = global_gacha_config();
|
||||
let item_id = item_id.value();
|
||||
for rarity_items in target_pool.gacha_items.iter() {
|
||||
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(&item_id) {
|
||||
up_category = Some(category_tag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if let None = up_category {
|
||||
continue;
|
||||
};
|
||||
let up_category = up_category.unwrap();
|
||||
|
||||
let progress_bin = self
|
||||
.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(item_id);
|
||||
}
|
||||
Vacant(vacant_entry) => {
|
||||
vacant_entry.insert(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 true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
|
@ -1,20 +1,25 @@
|
|||
use super::record::*;
|
||||
use super::stat::*;
|
||||
use data::gacha;
|
||||
use data::gacha::gacha_config::*;
|
||||
use proto::*;
|
||||
use data::tables::ItemID;
|
||||
|
||||
use chrono::{DateTime, Local};
|
||||
use proto::GachaModelBin;
|
||||
use rand::{thread_rng, Rng};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
|
||||
pub struct GachaModel {
|
||||
pub gacha_bin: GachaModelBin,
|
||||
pub gacha_status_map: HashMap<String, GachaStatus>,
|
||||
pub gacha_records: Vec<GachaRecord>,
|
||||
}
|
||||
|
||||
impl Default for GachaModel {
|
||||
fn default() -> GachaModel {
|
||||
let result = GachaModel {
|
||||
gacha_bin: GachaModelBin::default(),
|
||||
gacha_status_map: HashMap::new(),
|
||||
gacha_records: vec![],
|
||||
};
|
||||
result.post_deserialize()
|
||||
}
|
||||
|
@ -22,19 +27,37 @@ impl Default for GachaModel {
|
|||
|
||||
impl GachaModel {
|
||||
pub fn from_bin(gacha_bin: GachaModelBin) -> Self {
|
||||
let result = Self { gacha_bin };
|
||||
let result = Self {
|
||||
gacha_status_map: gacha_bin
|
||||
.gacha_status_map
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, GachaStatus::from_bin(v)))
|
||||
.collect(),
|
||||
gacha_records: gacha_bin
|
||||
.gacha_records
|
||||
.into_iter()
|
||||
.map(|x| GachaRecord::from_bin(x))
|
||||
.collect(),
|
||||
};
|
||||
result.post_deserialize()
|
||||
}
|
||||
|
||||
pub fn to_bin(&self) -> GachaModelBin {
|
||||
self.gacha_bin.clone()
|
||||
GachaModelBin {
|
||||
gacha_status_map: self
|
||||
.gacha_status_map
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.to_bin()))
|
||||
.collect(),
|
||||
gacha_records: self.gacha_records.iter().map(|x| x.to_bin()).collect(),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
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 mut gacha_status_map = &mut self.gacha_status_map;
|
||||
let status_bin = get_or_add(
|
||||
&mut gacha_status_map,
|
||||
&gacha_pool.sharing_guarantee_info_category,
|
||||
|
@ -97,11 +120,11 @@ impl GachaModel {
|
|||
&'bin mut self,
|
||||
pull_time: &DateTime<Local>,
|
||||
target_pool: &'conf CharacterGachaPool,
|
||||
) -> GachaRecordBin {
|
||||
let mut gacha_bin = &mut self.gacha_bin;
|
||||
) -> GachaRecord {
|
||||
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);
|
||||
self.determine_rarity(target_pool);
|
||||
let (category_tag, category) =
|
||||
self.determine_category(rarity_items, progress_bin, target_pool);
|
||||
let result = determine_gacha_result(
|
||||
pull_time,
|
||||
category,
|
||||
|
@ -110,11 +133,311 @@ impl GachaModel {
|
|||
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);
|
||||
self.update_pity(rarity_items, probability_model, target_pool);
|
||||
self.update_category_guarantee_info(rarity_items, &category_tag, target_pool);
|
||||
self.update_discount(target_pool, &category_tag, rarity_items);
|
||||
result
|
||||
}
|
||||
|
||||
fn rand_rarity<'bin, 'conf>(
|
||||
&'bin self,
|
||||
target_pool: &'conf CharacterGachaPool,
|
||||
status_bin: &'bin GachaStatus,
|
||||
) -> (
|
||||
&'conf GachaAvailableItemsInfo,
|
||||
&'bin GachaProgress,
|
||||
&'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>(
|
||||
&'bin self,
|
||||
target_pool: &'conf CharacterGachaPool,
|
||||
) -> (
|
||||
&'conf GachaAvailableItemsInfo,
|
||||
&'bin GachaProgress,
|
||||
&'bin GachaStatus,
|
||||
&'conf ProbabilityModel,
|
||||
) {
|
||||
let gachaconf = gacha::global_gacha_config();
|
||||
let status_bin = self
|
||||
.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) =
|
||||
self.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>(
|
||||
&'bin self,
|
||||
rarity_items: &'conf GachaAvailableItemsInfo,
|
||||
progress_bin: &'bin GachaProgress,
|
||||
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 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() {
|
||||
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 update_pity<'bin, 'conf>(
|
||||
&'bin mut self,
|
||||
rarity_items: &'conf GachaAvailableItemsInfo,
|
||||
probability_model: &'conf ProbabilityModel,
|
||||
target_pool: &'conf CharacterGachaPool,
|
||||
) {
|
||||
let status_bin = self
|
||||
.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>(
|
||||
&'bin mut self,
|
||||
rarity_items: &'conf GachaAvailableItemsInfo,
|
||||
category_tag: &String,
|
||||
target_pool: &'conf CharacterGachaPool,
|
||||
) {
|
||||
let gachaconf = gacha::global_gacha_config();
|
||||
let status_bin = self
|
||||
.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>(
|
||||
&'bin mut self,
|
||||
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 = self
|
||||
.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 = self
|
||||
.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.free_select_map.iter() {
|
||||
if !target_pool.discount_policy_tags.contains(policy_tag) {
|
||||
continue;
|
||||
}
|
||||
let status_bin = self
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_or_add<'a, K: Eq + PartialEq + Hash + Clone, V: Default, S: BuildHasher>(
|
||||
|
@ -127,200 +450,14 @@ fn get_or_add<'a, K: Eq + PartialEq + Hash + Clone, V: Default, S: BuildHasher>(
|
|||
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 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() {
|
||||
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,
|
||||
progress_bin: &'bin GachaProgressBin,
|
||||
status_bin: &'bin GachaStatus,
|
||||
progress_bin: &'bin GachaProgress,
|
||||
rarity_items: &'conf GachaAvailableItemsInfo,
|
||||
) -> GachaRecordBin {
|
||||
) -> GachaRecord {
|
||||
let gachaconf = gacha::global_gacha_config();
|
||||
let item_pool_len = category.item_ids.len() as u32;
|
||||
let mut item_id: Option<&u32> = None;
|
||||
|
@ -361,7 +498,7 @@ fn determine_gacha_result<'bin, 'conf>(
|
|||
.get(rand::thread_rng().gen_range(0..item_pool_len) as usize)
|
||||
.unwrap(),
|
||||
};
|
||||
let mut extra_item_id: u32 = 0;
|
||||
let mut extra_item_id: Option<ItemID> = None;
|
||||
let mut extra_item_count: u32 = 0;
|
||||
|
||||
for extra_items_policy_tag in rarity_items.extra_items_policy_tags.iter() {
|
||||
|
@ -372,134 +509,23 @@ fn determine_gacha_result<'bin, 'conf>(
|
|||
// 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_id = ItemID::new(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,
|
||||
let extra_resources = match extra_item_id {
|
||||
Some(item_id) => Some(GachaExtraResources {
|
||||
extra_item_id: item_id,
|
||||
extra_item_count,
|
||||
}),
|
||||
None => None,
|
||||
};
|
||||
GachaRecordBin {
|
||||
GachaRecord {
|
||||
pull_timestamp: pull_time.timestamp(),
|
||||
obtained_item_id: item_id.clone(),
|
||||
obtained_item_id: ItemID::new_unchecked(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),
|
||||
item_type: category.item_type.into(),
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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;
|
||||
extra_resources,
|
||||
item_type: category.item_type.clone(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
mod client_op;
|
||||
mod gacha_model;
|
||||
mod record;
|
||||
mod stat;
|
||||
|
||||
pub use gacha_model::GachaModel;
|
|
@ -0,0 +1,85 @@
|
|||
use data::gacha::gacha_config::*;
|
||||
use data::tables::ItemID;
|
||||
|
||||
use proto::{GachaExtraItemBin, GachaRecordBin};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::stat::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GachaExtraResources {
|
||||
pub extra_item_id: ItemID,
|
||||
pub extra_item_count: u32,
|
||||
}
|
||||
|
||||
impl GachaExtraResources {
|
||||
pub fn from_bin_opt(bin_opt: Option<GachaExtraItemBin>) -> Option<Self> {
|
||||
match bin_opt {
|
||||
None => None,
|
||||
Some(bin) => {
|
||||
let item_id_opt = ItemID::new(bin.extra_item_id);
|
||||
match item_id_opt {
|
||||
None => None,
|
||||
Some(extra_item_id) => Some(Self {
|
||||
extra_item_id,
|
||||
extra_item_count: bin.extra_item_count,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bin_opt(opt: &Option<Self>) -> Option<GachaExtraItemBin> {
|
||||
match opt {
|
||||
None => None,
|
||||
Some(this) => Some(GachaExtraItemBin {
|
||||
extra_item_id: this.extra_item_id.value(),
|
||||
extra_item_count: this.extra_item_count.clone(),
|
||||
..Default::default()
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GachaRecord {
|
||||
pub pull_timestamp: i64,
|
||||
pub obtained_item_id: ItemID,
|
||||
pub gacha_id: u32,
|
||||
/// The progress BEFORE this gacha is performed. key is rarity.
|
||||
pub progress_map: HashMap<u32, GachaProgress>,
|
||||
pub extra_resources: Option<GachaExtraResources>,
|
||||
pub item_type: GachaAddedItemType,
|
||||
}
|
||||
|
||||
impl GachaRecord {
|
||||
pub fn from_bin(bin: GachaRecordBin) -> Self {
|
||||
Self {
|
||||
pull_timestamp: bin.pull_timestamp,
|
||||
obtained_item_id: ItemID::new_unchecked(bin.obtained_item_id),
|
||||
gacha_id: bin.gacha_id,
|
||||
progress_map: bin
|
||||
.progress_map
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, GachaProgress::from_bin(v)))
|
||||
.collect(),
|
||||
extra_resources: GachaExtraResources::from_bin_opt(bin.extra_item_bin),
|
||||
item_type: GachaAddedItemType::from(bin.item_type),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bin(&self) -> GachaRecordBin {
|
||||
GachaRecordBin {
|
||||
pull_timestamp: self.pull_timestamp.clone(),
|
||||
obtained_item_id: self.obtained_item_id.value(),
|
||||
gacha_id: self.gacha_id.clone(),
|
||||
progress_map: self
|
||||
.progress_map
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.to_bin()))
|
||||
.collect(),
|
||||
extra_item_bin: GachaExtraResources::to_bin_opt(&self.extra_resources),
|
||||
item_type: self.item_type.clone().into(),
|
||||
}
|
||||
}
|
||||
}
|
73
nap_gameserver/src/logic/gacha/stat.rs
Normal file
73
nap_gameserver/src/logic/gacha/stat.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
use proto::*;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct GachaProgress {
|
||||
/// The pity (counting how many pulls) of this pull (in previous record) or the next pull (in status).
|
||||
pub pity: u32,
|
||||
/// The failure times of this category.
|
||||
pub categories_progress_map: HashMap<String, u32>,
|
||||
/// The selected priority (category) for a Chooseable category.
|
||||
pub categories_chosen_guarantee_category_map: HashMap<String, String>,
|
||||
/// The selectedpriority (a specified item) for a Chooseable category.
|
||||
pub categories_chosen_guarantee_item_map: HashMap<String, u32>,
|
||||
/// The failure times for selected priority (a specified item).
|
||||
pub categories_chosen_guarantee_progress_map: HashMap<String, u32>,
|
||||
}
|
||||
|
||||
impl GachaProgress {
|
||||
pub fn from_bin(bin: GachaProgressBin) -> Self {
|
||||
Self {
|
||||
pity: bin.pity,
|
||||
categories_progress_map: bin.categories_progress_map,
|
||||
categories_chosen_guarantee_category_map: bin.categories_chosen_guarantee_category_map,
|
||||
categories_chosen_guarantee_item_map: bin.categories_chosen_guarantee_item_map,
|
||||
categories_chosen_guarantee_progress_map: bin.categories_chosen_guarantee_progress_map,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bin(&self) -> GachaProgressBin {
|
||||
GachaProgressBin {
|
||||
pity: self.pity,
|
||||
categories_progress_map: self.categories_progress_map.clone(),
|
||||
categories_chosen_guarantee_category_map: self
|
||||
.categories_chosen_guarantee_category_map
|
||||
.clone(),
|
||||
categories_chosen_guarantee_item_map: self.categories_chosen_guarantee_item_map.clone(),
|
||||
categories_chosen_guarantee_progress_map: self
|
||||
.categories_chosen_guarantee_progress_map
|
||||
.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct GachaStatus {
|
||||
pub rarity_status_map: HashMap<u32, GachaProgress>,
|
||||
pub discount_usage_map: HashMap<String, u32>,
|
||||
}
|
||||
|
||||
impl GachaStatus {
|
||||
pub fn from_bin(bin: GachaStatusBin) -> Self {
|
||||
Self {
|
||||
rarity_status_map: bin
|
||||
.rarity_status_map
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k, GachaProgress::from_bin(v)))
|
||||
.collect(),
|
||||
discount_usage_map: bin.discount_usage_map,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_bin(&self) -> GachaStatusBin {
|
||||
GachaStatusBin {
|
||||
rarity_status_map: self
|
||||
.rarity_status_map
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), v.to_bin()))
|
||||
.collect(),
|
||||
discount_usage_map: self.discount_usage_map.clone(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ impl Default for BasicDataModel {
|
|||
nick_name: None,
|
||||
frontend_avatar_id: None,
|
||||
beginner_procedure_id: Some(ProcedureConfigID::new_unchecked(1)),
|
||||
..Default::default()
|
||||
selected_post_girl_id: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue