configs/
game_settings.rs

1use essences::{
2    currency::CurrencyId, fighting::FightTemplateId, items::AttributeId, offers::ShopTab,
3};
4use schema_loader::{
5    attribute_link_id_array_schema, attribute_link_id_schema, class_id_schema,
6    currency_link_id_array_schema, currency_link_id_schema, fight_template_link_id_schema,
7    vassal_task_id_schema,
8};
9use schemars::JsonSchema;
10
11use serde::{Deserialize, Serialize};
12use tsify_next::Tsify;
13
14use crate::validated_types::{PositiveI64, WeightMultiplier};
15
16#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, Tsify)]
17pub struct GameSettings {
18    #[schemars(
19        title = "Обязательные атрибуты предметов",
20        description = "Атрибуты, которые обязательно будут на предметах их сундука",
21        schema_with = "attribute_link_id_array_schema"
22    )]
23    pub required_attributes: Vec<AttributeId>,
24
25    #[schemars(
26        title = "Id вассальной таски брыкания",
27        schema_with = "vassal_task_id_schema"
28    )]
29    pub resist_task_id: uuid::Uuid,
30
31    #[schemars(title = "Id аттрибута хп", schema_with = "attribute_link_id_schema")]
32    pub hp_attribute_id: uuid::Uuid,
33
34    #[schemars(
35        title = "Id тайм скип билетика",
36        schema_with = "currency_link_id_schema"
37    )]
38    pub time_skip_ticket_currency_id: CurrencyId,
39
40    #[schemars(title = "Сколько секунд апгрейда сундука скипает тайм скип билетик")]
41    pub chest_upgrade_skip_ticket_skips_sec: u64,
42
43    #[schemars(
44        title = "Id валюты для скипа прокачки сундука",
45        schema_with = "currency_link_id_schema"
46    )]
47    pub chest_upgrade_skip_currency_id: CurrencyId,
48
49    #[schemars(title = "Сколько секунд апгрейда сундука скипает одна единица валюты")]
50    pub chest_upgrade_skip_currency_skips_sec: u64,
51
52    // #[schemars(title = "Ключ значения для повторяющихся квестов в custom_values")]
53    // pub loop_tasks_custom_values_key: String,
54
55    // #[schemars(title = "Дефолтное значение для повторяющихся квестов в custom_values")]
56    // pub loop_tasks_custom_values_default_value: i64,
57    #[schemars(title = "Настройки гачи способностей")]
58    pub ability_gacha: AbilityGachaSettings,
59
60    #[schemars(
61        title = "Id валюты для открытия сундука предметов",
62        schema_with = "currency_link_id_schema"
63    )]
64    pub item_case_currency_id: CurrencyId,
65
66    #[schemars(
67        title = "Id изначального класса персонажа",
68        schema_with = "class_id_schema"
69    )]
70    pub initial_class_id: uuid::Uuid,
71
72    #[schemars(title = "Длительность хранения сообщения в часах")]
73    pub chat_message_ttl_secs: u64,
74
75    #[schemars(
76        title = "Базовое значение атрибута speed",
77        description = "Значение атрибута speed на entity, при котором кулдауны идут 1:1 (\"100%\"). При speed выше базового кулдауны идут быстрее, при меньшем — медленнее."
78    )]
79    pub baseline_speed: u64,
80
81    #[schemars(
82        title = "Id валют для отрисовки на главном экране",
83        schema_with = "currency_link_id_array_schema"
84    )]
85    pub currency_ids_to_show: Vec<CurrencyId>,
86
87    #[schemars(title = "Настройки гачи петов")]
88    pub pet_gacha: PetGachaSettings,
89
90    #[schemars(title = "Длительность паузы между открытиями сундука")]
91    pub auto_chest_pause_duration_ticks: u64,
92
93    #[schemars(
94        title = "Id битвы для арены",
95        schema_with = "fight_template_link_id_schema"
96    )]
97    pub arena_fight_template_id: FightTemplateId,
98
99    #[schemars(
100        title = "Id битвы для вассального pvp",
101        schema_with = "fight_template_link_id_schema"
102    )]
103    pub vassal_fight_id: FightTemplateId,
104
105    #[schemars(title = "Как часто срабатывает эвент fight_progress_tick")]
106    pub fight_progress_tick: u64,
107
108    #[schemars(title = "Частота обновления мощи персонажа в базе")]
109    pub db_power_update_frequency: u64,
110
111    #[schemars(title = "Юзернейм подставляющийся игроку удалившему аккаунт")]
112    pub deleted_user_username: String,
113
114    #[schemars(
115        title = "Id валюты бриллиантов",
116        schema_with = "currency_link_id_schema"
117    )]
118    pub diamond_currency_id: CurrencyId,
119
120    #[schemars(title = "Минимальный интервал между сменами username (секунды)")]
121    pub username_change_cooldown_sec: u64,
122
123    #[schemars(title = "Минимальная длина username")]
124    pub username_min_length: usize,
125
126    #[schemars(title = "Максимальная длина username")]
127    pub username_max_length: usize,
128
129    #[schemars(title = "До какого числа обновлять кол-во ключей у пользователя")]
130    pub dungeons_keys_reset_amount: i64,
131
132    #[schemars(title = "Настройки ежедневного ресета валют")]
133    pub daily_currency_resets: Vec<DailyCurrencyReset>,
134
135    #[schemars(title = "Вкладка магазина, открываемая по умолчанию")]
136    pub default_shop_tab: ShopTab,
137
138    #[schemars(
139        title = "Глава, на которой показывается окно \"оцените нас\"",
140        description = "Если задано, сервер эмитит событие ShowRateUs, когда игрок впервые достигает этой главы. None отключает автоматический показ."
141    )]
142    pub rate_us_chapter: Option<i64>,
143
144    #[serde(default)]
145    #[schemars(
146        title = "Рост награды босса за главу",
147        description = "Множитель валюты, выпадающей с босса главы, растёт как boss_reward_chapter_growth^(глава) — глубже глава, больше падает с босса (\"пушить выгодно\"). Масштабируются только боссы (is_boss) в кампанийных боях; треш и не-кампания не затронуты. None или <=1.0 отключает (множитель 1.0)."
148    )]
149    pub boss_reward_chapter_growth: Option<f64>,
150
151    #[serde(default)]
152    #[schemars(
153        title = "Потолок множителя награды босса",
154        description = "Верхняя граница множителя из boss_reward_chapter_growth (защищает экономику/overflow на глубоких главах). None = без доп. потолка (но множитель всё равно ограничен здравым пределом в коде)."
155    )]
156    pub boss_reward_max_multiplier: Option<f64>,
157
158    /// Name of the native `default_loop_task` fn used to recover the active loop
159    /// task when `active_loop_task_id` is set but the loop-task list rebuilt
160    /// Production defaults to `"default_loop_task_const"`; the test fixture
161    /// overrides it to `"test_default_loop_task"` (which returns the fixture's
162    /// own loop-task id `305300e2`). Same postcard-symmetry / schema-skip
163    /// reasoning as `item_price_script`.
164    #[serde(default = "default_default_loop_task_behavior")]
165    #[schemars(skip)]
166    pub default_loop_task_behavior: String,
167}
168
169/// Default selector for [`GameSettings::default_loop_task_behavior`].
170fn default_default_loop_task_behavior() -> String {
171    "default_loop_task_const".to_string()
172}
173
174#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, Tsify)]
175pub struct AbilityGachaSettings {
176    #[schemars(
177        title = "Id валюты для открытия гачи скиллов",
178        schema_with = "currency_link_id_schema"
179    )]
180    pub currency_id: CurrencyId,
181
182    #[schemars(title = "Цена 1 крутки в бриллиантах")]
183    pub roll_price_in_diamonds: PositiveI64,
184
185    /// Стоимость маленькой крутки в gacha-билетах.
186    /// Она же — количество дропов и очки прогресса (тикеты = поинты = дропы).
187    #[schemars(title = "Стоимость маленькой крутки")]
188    pub small_roll_cost: PositiveI64,
189
190    /// Стоимость большой крутки в gacha-билетах (она же — очки прогресса).
191    #[schemars(title = "Стоимость большой крутки")]
192    pub big_roll_cost: PositiveI64,
193
194    /// Бонусные дропы в большой крутке сверх стоимости. Итого дропов = cost + bonus.
195    #[schemars(title = "Бонусные дропы в большой крутке")]
196    pub big_roll_bonus_drops: u8,
197
198    /// Максимальное число слотов вишлиста.
199    #[schemars(title = "Количество слотов вишлиста")]
200    pub wishlist_slots: u8,
201
202    /// Множитель веса для способностей из вишлиста при розыгрыше.
203    /// Значение `2.0` означает, что вишлист-абилка выпадает вдвое чаще обычной.
204    #[schemars(title = "Множитель веса вишлиста")]
205    pub wishlist_weight_multiplier: WeightMultiplier,
206
207    /// Валюта для прокачки уровней слотов способностей (Stardust).
208    #[schemars(
209        title = "Валюта прокачки слотов способностей",
210        schema_with = "currency_link_id_schema"
211    )]
212    pub slot_upgrade_currency_id: CurrencyId,
213
214    /// Максимальный уровень слота способности.
215    #[schemars(title = "Максимальный уровень слота способности")]
216    pub slot_max_level: PositiveI64,
217
218    /// Стоимость повышения слота до каждого уровня.
219    /// Индекс `0` соответствует прокачке на уровень `1`.
220    #[schemars(title = "Стоимости уровней слотов способностей")]
221    pub slot_level_costs: Vec<PositiveI64>,
222
223    /// Бонус к `AbilityLevel` по уровню слота.
224    /// Индекс = уровень слота, значение = дополнительный уровень способности.
225    #[schemars(title = "Бонус уровня способности от уровня слота")]
226    pub slot_level_bonus_levels: Vec<i64>,
227}
228
229#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, Tsify)]
230pub struct PetGachaSettings {
231    #[schemars(
232        title = "Id валюты для открытия гачи петов",
233        schema_with = "currency_link_id_schema"
234    )]
235    pub currency_id: CurrencyId,
236
237    #[schemars(title = "Цена 1 крутки в бриллиантах")]
238    pub roll_price_in_diamonds: PositiveI64,
239
240    /// Стоимость маленькой крутки в gacha-билетах.
241    /// Она же — количество дропов и очки прогресса (тикеты = поинты = дропы).
242    #[schemars(title = "Стоимость маленькой крутки")]
243    pub small_roll_cost: PositiveI64,
244
245    /// Стоимость большой крутки в gacha-билетах (она же — очки прогресса).
246    #[schemars(title = "Стоимость большой крутки")]
247    pub big_roll_cost: PositiveI64,
248
249    /// Бонусные дропы в большой крутке сверх стоимости. Итого дропов = cost + bonus.
250    #[schemars(title = "Бонусные дропы в большой крутке")]
251    pub big_roll_bonus_drops: u8,
252
253    /// Максимальное число слотов вишлиста.
254    #[schemars(title = "Количество слотов вишлиста")]
255    pub wishlist_slots: u8,
256
257    /// Множитель веса для петов из вишлиста при розыгрыше.
258    /// Значение `2.0` означает, что вишлист-пет выпадает вдвое чаще обычного.
259    #[schemars(title = "Множитель веса вишлиста")]
260    pub wishlist_weight_multiplier: WeightMultiplier,
261}
262
263#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, Tsify)]
264pub struct DailyCurrencyReset {
265    #[schemars(
266        title = "ID валюты для ресета",
267        schema_with = "currency_link_id_schema"
268    )]
269    pub currency_id: CurrencyId,
270
271    #[schemars(title = "Значение, до которого ресетить валюту")]
272    pub reset_amount: i64,
273}
274
275impl GameSettings {
276    pub fn get_daily_currency_reset_amount(&self, currency_id: CurrencyId) -> anyhow::Result<i64> {
277        self.daily_currency_resets
278            .iter()
279            .find(|reset| reset.currency_id == currency_id)
280            .map(|reset| reset.reset_amount)
281            .ok_or_else(|| {
282                anyhow::anyhow!(
283                    "Currency {} not found in daily_currency_resets",
284                    currency_id
285                )
286            })
287    }
288}