1use configs::game_config::GameConfig;
2use essences::{
3 bundles::{BundleAbility, BundleElement, BundleRawStep, BundleStepType},
4 character_state::CharacterState,
5 currency::from_es_currencies,
6 items::Item,
7};
8
9use event_system::script::runner::ScriptRandom;
10
11use crate::{
12 ScriptRunner, cases::try_finalize_item, event::OverlordEvent,
13 gacha::item_case::generate_item_from_template, game_config_helpers::GameConfigLookup,
14 state::OverlordState,
15};
16use rand::SeedableRng;
17
18pub fn bundle_raw_step_to_element(
19 raw_item: &BundleRawStep,
20 character_state: &CharacterState,
21 script_runner: &ScriptRunner<OverlordEvent, OverlordState>,
22 game_config: &GameConfig,
23) -> BundleElement {
24 match raw_item.item_type {
25 BundleStepType::Currency => process_currency(raw_item, character_state, script_runner),
26 BundleStepType::Ability => {
27 process_ability(raw_item, character_state, script_runner, game_config)
28 }
29 BundleStepType::Item => process_item(raw_item, character_state, script_runner, game_config),
30 }
31}
32
33pub fn bundle_raw_afk_step_to_element(
34 raw_item: &BundleRawStep,
35 character_state: &CharacterState,
36 now: chrono::DateTime<chrono::Utc>,
37 script_runner: &ScriptRunner<OverlordEvent, OverlordState>,
38 game_config: &GameConfig,
39) -> BundleElement {
40 match raw_item.item_type {
41 BundleStepType::Currency => {
42 process_afk_currency(raw_item, character_state, now, script_runner)
43 }
44 BundleStepType::Ability => {
45 process_afk_ability(raw_item, character_state, now, script_runner, game_config)
46 }
47 BundleStepType::Item => {
48 process_afk_item(raw_item, character_state, now, script_runner, game_config)
49 }
50 }
51}
52
53fn process_currency(
54 raw_item: &BundleRawStep,
55 character_state: &CharacterState,
56 script_runner: &ScriptRunner<OverlordEvent, OverlordState>,
57) -> BundleElement {
58 let currency_unit = from_es_currencies(&script_runner.run_currencies_calculate(
59 |mut scope| {
60 scope.set_const("CharacterState", character_state.clone());
61 scope
62 },
63 &raw_item.script,
64 ));
65
66 BundleElement::Currencies(currency_unit)
67}
68
69fn process_afk_currency(
70 raw_item: &BundleRawStep,
71 character_state: &CharacterState,
72 now: chrono::DateTime<chrono::Utc>,
73 script_runner: &ScriptRunner<OverlordEvent, OverlordState>,
74) -> BundleElement {
75 let currency_unit = from_es_currencies(&script_runner.run_currencies_calculate(
76 |mut scope| {
77 scope.set_const("CharacterState", character_state.clone());
78 scope.set_const(
79 "LastClaimAt",
80 character_state
81 .character
82 .last_afk_reward_claimed_at
83 .timestamp()
84 .max(0) as u64,
85 );
86 scope.set_const("Now", now.timestamp().max(0) as u64);
87 scope.set_const(
88 "Random",
89 ScriptRandom::new(rand::rngs::StdRng::seed_from_u64(
90 character_state.character.afk_reward_seed,
91 )),
92 );
93 scope
94 },
95 &raw_item.script,
96 ));
97
98 BundleElement::Currencies(currency_unit)
99}
100
101fn process_ability(
102 raw_item: &BundleRawStep,
103 character_state: &CharacterState,
104 script_runner: &ScriptRunner<OverlordEvent, OverlordState>,
105 game_config: &GameConfig,
106) -> BundleElement {
107 let ability_shards = script_runner.run_ability_shards_calculate(
108 |mut scope| {
109 scope.set_const("CharacterState", character_state.clone());
110 scope
111 },
112 &raw_item.script,
113 );
114
115 let abilities = ability_shards
116 .iter()
117 .filter_map(|ability_shard| {
118 let Some(template) = game_config
119 .ability_template(ability_shard.ability_id)
120 .cloned()
121 else {
122 tracing::error!(
123 "Failed to get ability with ability_id={}",
124 ability_shard.ability_id
125 );
126 return None;
127 };
128
129 Some(BundleAbility {
130 template,
131 shards_amount: ability_shard.shards_amount,
132 })
133 })
134 .collect();
135
136 BundleElement::Abilities(abilities)
137}
138
139fn process_afk_ability(
140 raw_item: &BundleRawStep,
141 character_state: &CharacterState,
142 now: chrono::DateTime<chrono::Utc>,
143 script_runner: &ScriptRunner<OverlordEvent, OverlordState>,
144 game_config: &GameConfig,
145) -> BundleElement {
146 let ability_shards = script_runner.run_ability_shards_calculate(
147 |mut scope| {
148 scope.set_const("CharacterState", character_state.clone());
149 scope.set_const(
150 "LastClaimAt",
151 character_state
152 .character
153 .last_afk_reward_claimed_at
154 .timestamp()
155 .max(0) as u64,
156 );
157 scope.set_const("Now", now.timestamp().max(0) as u64);
158 scope.set_const(
159 "Random",
160 ScriptRandom::new(rand::rngs::StdRng::seed_from_u64(
161 character_state.character.afk_reward_seed,
162 )),
163 );
164 scope
165 },
166 &raw_item.script,
167 );
168
169 let abilities = ability_shards
170 .iter()
171 .filter_map(|ability_shard| {
172 let Some(template) = game_config
173 .ability_template(ability_shard.ability_id)
174 .cloned()
175 else {
176 tracing::error!(
177 "Failed to get ability with ability_id={}",
178 ability_shard.ability_id
179 );
180 return None;
181 };
182
183 Some(BundleAbility {
184 template,
185 shards_amount: ability_shard.shards_amount,
186 })
187 })
188 .collect();
189
190 BundleElement::Abilities(abilities)
191}
192
193fn process_item(
194 raw_item: &BundleRawStep,
195 character_state: &CharacterState,
196 script_runner: &ScriptRunner<OverlordEvent, OverlordState>,
197 game_config: &GameConfig,
198) -> BundleElement {
199 let item_ids = script_runner.run_item_ids_script(
200 |mut scope| {
201 scope.set_const("CharacterState", character_state.clone());
202 scope
203 },
204 &raw_item.script,
205 );
206
207 let items: Vec<Item> = item_ids
208 .iter()
209 .filter_map(|&item_id| {
210 let Some(template) = game_config.item_template(item_id) else {
211 tracing::error!("Failed to get item template with item_id={}", item_id);
212 return None;
213 };
214
215 let Some(rarity) = game_config.item_rarity(template.rarity_id).cloned() else {
216 tracing::error!("Failed to get item rarity with id={}", template.rarity_id);
217 return None;
218 };
219
220 Some(generate_item_from_template(
221 template,
222 rarity,
223 character_state.character.character_level,
224 game_config,
225 &mut rand::rngs::StdRng::from_os_rng(),
226 ))
227 })
228 .collect();
229
230 let finalized_items = items
231 .into_iter()
232 .filter_map(
233 |mut item| match try_finalize_item(&mut item, game_config, script_runner) {
234 Ok(()) => Some(item),
235 Err(e) => {
236 tracing::error!("Failed to finalize item: {}", e);
237 None
238 }
239 },
240 )
241 .collect();
242
243 BundleElement::Items(finalized_items)
244}
245
246fn process_afk_item(
247 raw_item: &BundleRawStep,
248 character_state: &CharacterState,
249 now: chrono::DateTime<chrono::Utc>,
250 script_runner: &ScriptRunner<OverlordEvent, OverlordState>,
251 game_config: &GameConfig,
252) -> BundleElement {
253 let item_ids = script_runner.run_item_ids_script(
254 |mut scope| {
255 scope.set_const("CharacterState", character_state.clone());
256 scope.set_const(
257 "LastClaimAt",
258 character_state
259 .character
260 .last_afk_reward_claimed_at
261 .timestamp()
262 .max(0) as u64,
263 );
264 scope.set_const("Now", now.timestamp().max(0) as u64);
265 scope.set_const(
266 "Random",
267 ScriptRandom::new(rand::rngs::StdRng::seed_from_u64(
268 character_state.character.afk_reward_seed,
269 )),
270 );
271 scope
272 },
273 &raw_item.script,
274 );
275
276 let items: Vec<Item> = item_ids
277 .iter()
278 .filter_map(|&item_id| {
279 let Some(template) = game_config.item_template(item_id) else {
280 tracing::error!("Failed to get item template with item_id={}", item_id);
281 return None;
282 };
283
284 let Some(rarity) = game_config.item_rarity(template.rarity_id).cloned() else {
285 tracing::error!("Failed to get item rarity with id={}", template.rarity_id);
286 return None;
287 };
288
289 Some(generate_item_from_template(
290 template,
291 rarity,
292 character_state.character.character_level,
293 game_config,
294 &mut rand::rngs::StdRng::seed_from_u64(character_state.character.afk_reward_seed),
295 ))
296 })
297 .collect();
298
299 let finalized_items = items
300 .into_iter()
301 .filter_map(
302 |mut item| match try_finalize_item(&mut item, game_config, script_runner) {
303 Ok(()) => Some(item),
304 Err(e) => {
305 tracing::error!("Failed to finalize item: {}", e);
306 None
307 }
308 },
309 )
310 .collect();
311
312 BundleElement::Items(finalized_items)
313}