1use essences::pet_facets::PetFacet;
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4use tsify_next::Tsify;
5
6#[derive(Clone, Copy, Debug, Deserialize, Serialize, JsonSchema, Tsify)]
10pub struct PetFacetPowerQ {
11 #[schemars(title = "Грань")]
12 pub facet: PetFacet,
13
14 #[schemars(
15 title = "Множитель Power на первом уровне пета",
16 description = "1.0 = грань не влияет на Power."
17 )]
18 pub q_first_level: f64,
19
20 #[schemars(
21 title = "Множитель Power на максимальном уровне пета",
22 description = "Промежуточные уровни интерполируются линейно по ln(q)."
23 )]
24 pub q_max_level: f64,
25}
26
27#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, Tsify)]
39pub struct PetFacetSettings {
40 #[schemars(
45 title = "Прирост силы грани за уровень пета (доли от 10000)",
46 description = "На сколько сильнее становятся числа обеих граней за каждый уровень пета выше первого. Количество зарядов и длительности окон не масштабируются."
47 )]
48 pub rank_growth_permyriad: i64,
49
50 #[schemars(
52 title = "Head Start: сокращение кулдаунов (мс)",
53 description = "На сколько миллисекунд уменьшаются все оставшиеся кулдауны скиллов."
54 )]
55 pub head_start_ticks: u64,
56
57 #[schemars(
58 title = "Second Spark: сила повтора (%)",
59 description = "Доля payload следующего original Skill, которую наносит derived-повтор."
60 )]
61 pub second_spark_percent: f64,
62
63 #[schemars(
65 title = "Budget Plan: множитель цены маны",
66 description = "Во сколько раз дешевеет мана следующих кастов."
67 )]
68 pub budget_plan_cost_multiplier: f64,
69
70 #[schemars(
71 title = "Budget Plan: количество кастов",
72 description = "Сколько ближайших original casts получают скидку."
73 )]
74 pub budget_plan_casts: i64,
75
76 #[schemars(
77 title = "Open Tab: максимальная доплата (%)",
78 description = "Сколько сверх цены каста может быть потрачено, если мана есть."
79 )]
80 pub open_tab_surcharge_percent: f64,
81
82 #[schemars(
83 title = "Open Tab: доля доплаты в payload (%)",
84 description = "Какая доля доплаты превращается в прибавку к payload каста."
85 )]
86 pub open_tab_payload_share_percent: f64,
87
88 #[schemars(title = "Overtime: прибавка скорости атаки (%)")]
90 pub overtime_attack_speed_percent: f64,
91 #[schemars(title = "Overtime: длительность окна (мс)")]
92 pub overtime_duration_ticks: u64,
93
94 #[schemars(title = "Magic Volley: количество болтов")]
95 pub magic_volley_bolts: i64,
96 #[schemars(title = "Magic Volley: сила одного болта (% от Attack)")]
97 pub magic_volley_percent: f64,
98
99 #[schemars(title = "Safety Net: снижение входящего урона (%)")]
101 pub safety_net_reduction_percent: f64,
102 #[schemars(title = "Safety Net: длительность окна (мс)")]
103 pub safety_net_duration_ticks: u64,
104
105 #[schemars(
106 title = "Retaliation: сила площадного взрыва (% от Attack)",
107 description = "Взрыв срабатывает на следующем Core-попадании по герою."
108 )]
109 pub retaliation_percent: f64,
110
111 #[schemars(title = "Calibration: прибавка шанса крита (%)")]
113 pub calibration_crit_percent: f64,
114 #[schemars(title = "Calibration: количество атак")]
115 pub calibration_attacks: i64,
116
117 #[schemars(title = "Lucky Star: сила взрыва (% от Attack)")]
118 pub lucky_star_percent: f64,
119
120 #[schemars(title = "Lead Reading: прибавка Resonance (%)")]
122 pub lead_reading_resonance_percent: f64,
123 #[schemars(title = "Lead Reading: количество срабатываний закона")]
124 pub lead_reading_activations: i64,
125
126 #[schemars(title = "Wild Reading: усиление Effect закона (%)")]
127 pub wild_reading_effect_percent: f64,
128 #[schemars(title = "Wild Reading: длительность окна (мс)")]
129 pub wild_reading_duration_ticks: u64,
130
131 #[schemars(
133 title = "Advance Notice: стартовый Flip Gauge (%)",
134 description = "Доля порога флипа, с которой начинается новая фаза. Единственное исключение из правила «грань не создаёт Gauge»."
135 )]
136 pub advance_notice_gauge_percent: f64,
137
138 #[schemars(title = "Rising Gate: количество усиленных Trigger-проков")]
139 pub rising_gate_procs: i64,
140 #[schemars(
141 title = "Rising Gate: множитель Gauge прока на уровне пета 1",
142 description = "BAL-014: первые проки умножают награду Trigger, а не добавляют Gauge. Между уровнями 1 и 10 значение растёт линейно."
143 )]
144 pub rising_gate_multiplier_l1: f64,
145 #[schemars(title = "Rising Gate: множитель Gauge прока на уровне пета 10")]
146 pub rising_gate_multiplier_l10: f64,
147
148 #[schemars(
150 title = "Prepared Slots: сила первого прока слота (%)",
151 description = "100 означает обычную силу; 140 — на 40% сильнее."
152 )]
153 pub prepared_slots_percent: f64,
154
155 #[schemars(title = "First Spell: сила повтора первого Effect (%)")]
156 pub first_spell_percent: f64,
157
158 #[schemars(title = "Souvenir: количество проков с парным скрытым Effect")]
160 pub souvenir_procs: i64,
161 #[schemars(title = "Souvenir: сила парного скрытого Effect (%)")]
162 pub souvenir_percent: f64,
163
164 #[schemars(title = "Dream Reader: сила разбуженного скрытого закона (%)")]
165 pub dream_reader_percent: f64,
166
167 #[schemars(
169 title = "Packed Lunch: лечение (% от Max HP)",
170 description = "Перелечивание сгорает."
171 )]
172 pub packed_lunch_heal_percent: f64,
173
174 #[schemars(
175 title = "Life Bloom: доля Core-урона в лечение на уровне пета 1 (%)",
176 description = "BAL-014: между уровнями 1 и 10 значение растёт линейно."
177 )]
178 pub life_bloom_percent_l1: f64,
179 #[schemars(title = "Life Bloom: доля Core-урона в лечение на уровне пета 10 (%)")]
180 pub life_bloom_percent_l10: f64,
181 #[schemars(title = "Life Bloom: длительность окна (мс)")]
182 pub life_bloom_duration_ticks: u64,
183
184 #[schemars(
185 title = "Множители Power граней",
186 description = "По одной строке на каждую из 20 граней (BAL-030). Порядок значения не имеет, пропуски запрещены."
187 )]
188 pub power_q: Vec<PetFacetPowerQ>,
189}
190
191impl PetFacetSettings {
192 pub fn rank_multiplier(&self, pet_level: i64) -> f64 {
196 let levels_above_first = (pet_level - 1).max(0) as f64;
197 1.0 + (self.rank_growth_permyriad.max(0) as f64 / 10_000.0) * levels_above_first
198 }
199
200 pub fn level_interpolated(l1: f64, l10: f64, pet_level: i64) -> f64 {
204 let t = (pet_level - 1).clamp(0, 9) as f64 / 9.0;
205 l1 + (l10 - l1) * t
206 }
207
208 pub fn power_q_row(&self, facet: PetFacet) -> Option<&PetFacetPowerQ> {
211 self.power_q.iter().find(|row| row.facet == facet)
212 }
213}