1use anyhow::Context;
2
3use configs::{
4 abilities::{AbilityCasesSettingsByLevel, AbilityLevel, Projectile, ProjectileId},
5 cheats::{CheatScript, CheatScriptId},
6 fighting::ArenaLeague,
7 game_config::GameConfig,
8 pets::{PetCasesSettingsByLevel, PetLevel},
9 stones::{EffectStoneTemplate, TriggerStoneTemplate},
10};
11
12use essences::{
13 abilities::{AbilityId, AbilityRarity, AbilityRarityId, AbilityTemplate},
14 ability_stones::AbilityStoneId,
15 bundles::{BundleId, BundleRaw},
16 class::{Class, ClassId},
17 dungeons::{DungeonTemplate, DungeonTemplateId},
18 effect::{Effect, EffectId},
19 fighting::{FightTemplate, FightTemplateId},
20 game::{Chapter, CharacterLevel, EntityTemplate, EntityTemplateId},
21 gift::GiftTemplate,
22 item_case::ItemCasesSettingsByLevel,
23 items::{Attribute, AttributeId, ItemRarity, ItemRarityId, ItemTemplate, ItemTemplateId},
24 mail::MailTemplate,
25 pets::{PetId, PetRarity, PetRarityId, PetTemplate},
26 quest::QuestTemplate,
27 referrals::ReferralLevelInfo,
28 skins::{ConfigSkin, SkinId},
29 stones::StoneTemplateId,
30 talent_tree::{TalentId, TalentTemplate},
31 vassals::VassalTaskTemplate,
32};
33
34pub trait GameConfigLookup {
35 fn attribute(&self, attribute_id: AttributeId) -> Option<&Attribute>;
36 fn require_attribute(&self, attribute_id: AttributeId) -> anyhow::Result<&Attribute>;
37
38 fn attribute_by_db_code(&self, db_code: u8) -> Option<&Attribute>;
39 fn require_attribute_by_db_code(&self, db_code: u8) -> anyhow::Result<&Attribute>;
40
41 fn ability_template(&self, ability_id: AbilityId) -> Option<&AbilityTemplate>;
42 fn require_ability_template(&self, ability_id: AbilityId) -> anyhow::Result<&AbilityTemplate>;
43
44 fn ability_rarity(&self, rarity_id: AbilityRarityId) -> Option<&AbilityRarity>;
45 fn require_ability_rarity(&self, rarity_id: AbilityRarityId) -> anyhow::Result<&AbilityRarity>;
46
47 fn ability_stone_template(
48 &self,
49 stone_id: AbilityStoneId,
50 ) -> Option<&essences::ability_stones::AbilityStoneTemplate>;
51
52 fn ability_level(&self, rarity_id: AbilityRarityId, level: i64) -> Option<&AbilityLevel>;
53 fn require_ability_level(
54 &self,
55 rarity_id: AbilityRarityId,
56 level: i64,
57 ) -> anyhow::Result<&AbilityLevel>;
58
59 fn ability_case_settings_by_level(&self, level: i64) -> Option<&AbilityCasesSettingsByLevel>;
60 fn require_ability_case_settings_by_level(
61 &self,
62 level: i64,
63 ) -> anyhow::Result<&AbilityCasesSettingsByLevel>;
64
65 fn bundle(&self, bundle_id: BundleId) -> Option<&BundleRaw>;
66 fn require_bundle(&self, bundle_id: BundleId) -> anyhow::Result<&BundleRaw>;
67
68 fn chapter_by_level(&self, chapter_level: i64) -> Option<&Chapter>;
69 fn require_chapter_by_level(&self, chapter_level: i64) -> anyhow::Result<&Chapter>;
70
71 fn ability_slots_for_chapter_level(&self, chapter_level: i64) -> Option<u64>;
72
73 fn character_level(&self, level: i64) -> Option<CharacterLevel>;
74 fn require_character_level(&self, level: i64) -> anyhow::Result<CharacterLevel>;
75 fn level_for_experience(&self, experience: i64) -> i64;
77
78 fn class(&self, class_id: ClassId) -> Option<&Class>;
79 fn require_class(&self, class_id: ClassId) -> anyhow::Result<&Class>;
80
81 fn dungeon_template(&self, dungeon_id: DungeonTemplateId) -> Option<&DungeonTemplate>;
82 fn require_dungeon_template(
83 &self,
84 dungeon_id: DungeonTemplateId,
85 ) -> anyhow::Result<&DungeonTemplate>;
86
87 fn effect(&self, effect_id: EffectId) -> Option<&Effect>;
88 fn require_effect(&self, effect_id: EffectId) -> anyhow::Result<&Effect>;
89
90 fn fight_template(&self, fight_id: FightTemplateId) -> Option<&FightTemplate>;
91 fn require_fight_template(&self, fight_id: FightTemplateId) -> anyhow::Result<&FightTemplate>;
92
93 fn arena_league_for_rating(&self, rating: i64) -> Option<&ArenaLeague>;
94 fn require_arena_league_for_rating(&self, rating: i64) -> anyhow::Result<&ArenaLeague>;
95
96 fn entity_template(&self, entity_template_id: EntityTemplateId) -> Option<&EntityTemplate>;
97 fn require_entity_template(
98 &self,
99 entity_template_id: EntityTemplateId,
100 ) -> anyhow::Result<&EntityTemplate>;
101
102 fn gift_template(&self, gift_id: uuid::Uuid) -> Option<&GiftTemplate>;
103 fn require_gift_template(&self, gift_id: uuid::Uuid) -> anyhow::Result<&GiftTemplate>;
104
105 fn item_case_settings_by_level(&self, level: i64) -> Option<&ItemCasesSettingsByLevel>;
106 fn require_item_case_settings_by_level(
107 &self,
108 level: i64,
109 ) -> anyhow::Result<&ItemCasesSettingsByLevel>;
110
111 fn item_rarity(&self, rarity_id: ItemRarityId) -> Option<&ItemRarity>;
112 fn require_item_rarity(&self, rarity_id: ItemRarityId) -> anyhow::Result<&ItemRarity>;
113
114 fn item_template(&self, item_id: ItemTemplateId) -> Option<&ItemTemplate>;
115 fn require_item_template(&self, item_id: ItemTemplateId) -> anyhow::Result<&ItemTemplate>;
116
117 fn mail_template(&self, template_id: uuid::Uuid) -> Option<&MailTemplate>;
118 fn require_mail_template(&self, template_id: uuid::Uuid) -> anyhow::Result<&MailTemplate>;
119
120 fn patron_level_info(&self, level: i64) -> Option<&ReferralLevelInfo>;
121 fn require_patron_level_info(&self, level: i64) -> anyhow::Result<&ReferralLevelInfo>;
122
123 fn projectile(&self, projectile_id: ProjectileId) -> Option<&Projectile>;
124 fn require_projectile(&self, projectile_id: ProjectileId) -> anyhow::Result<&Projectile>;
125
126 fn quest(&self, quest_id: uuid::Uuid) -> Option<&QuestTemplate>;
127 fn require_quest(&self, quest_id: uuid::Uuid) -> anyhow::Result<&QuestTemplate>;
128
129 fn quest_by_code(&self, code: &str) -> Option<&QuestTemplate>;
130
131 fn skin(&self, skin_id: SkinId) -> Option<&ConfigSkin>;
132 fn require_skin(&self, skin_id: SkinId) -> anyhow::Result<&ConfigSkin>;
133
134 fn vassal_task_template(&self, task_template_id: uuid::Uuid) -> Option<&VassalTaskTemplate>;
135 fn require_vassal_task_template(
136 &self,
137 task_template_id: uuid::Uuid,
138 ) -> anyhow::Result<&VassalTaskTemplate>;
139
140 fn cheat_script(&self, cheat_script_id: CheatScriptId) -> Option<&CheatScript>;
141
142 fn talent(&self, talent_id: TalentId) -> Option<&TalentTemplate>;
143 fn require_talent(&self, talent_id: TalentId) -> anyhow::Result<&TalentTemplate>;
144
145 fn allowed_wishlist_rarity_ids(
146 &self,
147 ability_case_level: i64,
148 ) -> Vec<essences::abilities::AbilityRarityId>;
149
150 fn pet_template(&self, pet_id: PetId) -> Option<&PetTemplate>;
151 fn require_pet_template(&self, pet_id: PetId) -> anyhow::Result<&PetTemplate>;
152
153 fn pet_rarity(&self, rarity_id: PetRarityId) -> Option<&PetRarity>;
154
155 fn pet_level(&self, rarity_id: PetRarityId, level: i64) -> Option<&PetLevel>;
156
157 fn pet_slots_for_chapter_level(&self, chapter_level: i64) -> Option<u64>;
158
159 fn pet_case_settings_by_level(&self, level: i64) -> Option<&PetCasesSettingsByLevel>;
160 fn require_pet_case_settings_by_level(
161 &self,
162 level: i64,
163 ) -> anyhow::Result<&PetCasesSettingsByLevel>;
164
165 fn allowed_pet_wishlist_rarity_ids(
166 &self,
167 pet_case_level: i64,
168 ) -> Vec<essences::pets::PetRarityId>;
169
170 fn trigger_stone_template(&self, template_id: StoneTemplateId)
179 -> Option<&TriggerStoneTemplate>;
180
181 fn effect_stone_template(&self, template_id: StoneTemplateId) -> Option<&EffectStoneTemplate>;
184}
185
186impl GameConfigLookup for GameConfig {
187 fn attribute(&self, attribute_id: AttributeId) -> Option<&Attribute> {
188 self.attributes.iter().find(|a| a.id == attribute_id)
189 }
190
191 fn require_attribute(&self, attribute_id: AttributeId) -> anyhow::Result<&Attribute> {
192 self.attribute(attribute_id)
193 .with_context(|| format!("No attribute in config with id={attribute_id}"))
194 }
195
196 fn attribute_by_db_code(&self, db_code: u8) -> Option<&Attribute> {
197 self.attributes.iter().find(|a| a.db_code == db_code)
198 }
199
200 fn require_attribute_by_db_code(&self, db_code: u8) -> anyhow::Result<&Attribute> {
201 self.attribute_by_db_code(db_code)
202 .with_context(|| format!("No attribute in config with db_code={db_code}"))
203 }
204
205 fn ability_template(&self, ability_id: AbilityId) -> Option<&AbilityTemplate> {
206 self.abilities.iter().find(|a| a.id == ability_id)
207 }
208
209 fn require_ability_template(&self, ability_id: AbilityId) -> anyhow::Result<&AbilityTemplate> {
210 self.ability_template(ability_id)
211 .with_context(|| format!("No ability_template in config with id={ability_id}"))
212 }
213
214 fn ability_rarity(&self, rarity_id: AbilityRarityId) -> Option<&AbilityRarity> {
215 self.ability_rarities.iter().find(|r| r.id == rarity_id)
216 }
217
218 fn require_ability_rarity(&self, rarity_id: AbilityRarityId) -> anyhow::Result<&AbilityRarity> {
219 self.ability_rarity(rarity_id)
220 .with_context(|| format!("No ability_rarity in config with id={rarity_id}"))
221 }
222
223 fn ability_stone_template(
224 &self,
225 stone_id: AbilityStoneId,
226 ) -> Option<&essences::ability_stones::AbilityStoneTemplate> {
227 self.ability_stones.iter().find(|s| s.id == stone_id)
228 }
229
230 fn ability_level(&self, rarity_id: AbilityRarityId, level: i64) -> Option<&AbilityLevel> {
231 self.ability_levels
232 .iter()
233 .find(|x| x.rarity_id == rarity_id && x.level == level)
234 }
235
236 fn require_ability_level(
237 &self,
238 rarity_id: AbilityRarityId,
239 level: i64,
240 ) -> anyhow::Result<&AbilityLevel> {
241 self.ability_level(rarity_id, level).with_context(|| {
242 format!("No ability_level in config with rarity_id={rarity_id} and level={level}")
243 })
244 }
245
246 fn ability_case_settings_by_level(&self, level: i64) -> Option<&AbilityCasesSettingsByLevel> {
247 self.ability_cases_settings
248 .iter()
249 .find(|x| x.level == level)
250 }
251
252 fn require_ability_case_settings_by_level(
253 &self,
254 level: i64,
255 ) -> anyhow::Result<&AbilityCasesSettingsByLevel> {
256 self.ability_case_settings_by_level(level)
257 .with_context(|| format!("No ability_case_settings with level={level}"))
258 }
259
260 fn bundle(&self, bundle_id: BundleId) -> Option<&BundleRaw> {
261 self.bundles.iter().find(|b| b.id == bundle_id)
262 }
263
264 fn require_bundle(&self, bundle_id: BundleId) -> anyhow::Result<&BundleRaw> {
265 self.bundle(bundle_id)
266 .with_context(|| format!("No bundle in config with id={bundle_id}"))
267 }
268
269 fn chapter_by_level(&self, chapter_level: i64) -> Option<&Chapter> {
270 self.chapters.iter().find(|c| c.level == chapter_level)
271 }
272
273 fn require_chapter_by_level(&self, chapter_level: i64) -> anyhow::Result<&Chapter> {
274 self.chapter_by_level(chapter_level)
275 .with_context(|| format!("No chapter with level={chapter_level}"))
276 }
277
278 fn ability_slots_for_chapter_level(&self, chapter_level: i64) -> Option<u64> {
279 self.ability_slots_levels
280 .iter()
281 .rev()
282 .find(|level| level.from_chapter_level <= chapter_level)
283 .map(|level| level.ability_slots)
284 }
285
286 fn character_level(&self, level: i64) -> Option<CharacterLevel> {
287 if level > self.character_level_formula.anchor_level {
290 return Some(self.character_level_formula.character_level(level));
291 }
292 self.character_levels
293 .iter()
294 .find(|x| x.level == level)
295 .cloned()
296 }
297
298 fn require_character_level(&self, level: i64) -> anyhow::Result<CharacterLevel> {
299 self.character_level(level)
300 .with_context(|| format!("No character_level with level={level}"))
301 }
302
303 fn level_for_experience(&self, experience: i64) -> i64 {
304 let formula = &self.character_level_formula;
305 if experience >= formula.anchor_experience {
306 return formula.level_for_experience(experience);
307 }
308 self.character_levels
310 .iter()
311 .filter(|x| x.required_experience <= experience)
312 .map(|x| x.level)
313 .max()
314 .unwrap_or(0)
315 }
316
317 fn class(&self, class_id: ClassId) -> Option<&Class> {
318 self.classes.iter().find(|c| c.id == class_id)
319 }
320
321 fn require_class(&self, class_id: ClassId) -> anyhow::Result<&Class> {
322 self.class(class_id)
323 .with_context(|| format!("No class in config with id={class_id}"))
324 }
325
326 fn dungeon_template(&self, dungeon_id: DungeonTemplateId) -> Option<&DungeonTemplate> {
327 self.dungeon_templates.iter().find(|d| d.id == dungeon_id)
328 }
329
330 fn require_dungeon_template(
331 &self,
332 dungeon_id: DungeonTemplateId,
333 ) -> anyhow::Result<&DungeonTemplate> {
334 self.dungeon_template(dungeon_id)
335 .with_context(|| format!("No dungeon_template in config with id={dungeon_id}"))
336 }
337
338 fn effect(&self, effect_id: EffectId) -> Option<&Effect> {
339 self.effects.iter().find(|e| e.id == effect_id)
340 }
341
342 fn require_effect(&self, effect_id: EffectId) -> anyhow::Result<&Effect> {
343 self.effect(effect_id)
344 .with_context(|| format!("No effect in config with id={effect_id}"))
345 }
346
347 fn fight_template(&self, fight_id: FightTemplateId) -> Option<&FightTemplate> {
348 self.fight_templates.iter().find(|f| f.id == fight_id)
349 }
350
351 fn require_fight_template(&self, fight_id: FightTemplateId) -> anyhow::Result<&FightTemplate> {
352 self.fight_template(fight_id)
353 .with_context(|| format!("No fight_template in config with id={fight_id}"))
354 }
355
356 fn arena_league_for_rating(&self, rating: i64) -> Option<&ArenaLeague> {
357 self.arena_leagues
358 .iter()
359 .find(|league| rating >= league.min_rating && rating <= league.max_rating)
360 }
361
362 fn require_arena_league_for_rating(&self, rating: i64) -> anyhow::Result<&ArenaLeague> {
363 self.arena_league_for_rating(rating)
364 .with_context(|| format!("No arena_league in config for rating={rating}"))
365 }
366
367 fn entity_template(&self, entity_template_id: EntityTemplateId) -> Option<&EntityTemplate> {
368 self.entities.iter().find(|e| e.id == entity_template_id)
369 }
370
371 fn require_entity_template(
372 &self,
373 entity_template_id: EntityTemplateId,
374 ) -> anyhow::Result<&EntityTemplate> {
375 self.entity_template(entity_template_id)
376 .with_context(|| format!("No entity_template in config with id={entity_template_id}"))
377 }
378
379 fn gift_template(&self, gift_id: uuid::Uuid) -> Option<&GiftTemplate> {
380 self.gifts.iter().find(|g| g.id == gift_id)
381 }
382
383 fn require_gift_template(&self, gift_id: uuid::Uuid) -> anyhow::Result<&GiftTemplate> {
384 self.gift_template(gift_id)
385 .with_context(|| format!("No gift_template in config with id={gift_id}"))
386 }
387
388 fn item_case_settings_by_level(&self, level: i64) -> Option<&ItemCasesSettingsByLevel> {
389 self.item_cases_settings.iter().find(|s| s.level == level)
390 }
391
392 fn require_item_case_settings_by_level(
393 &self,
394 level: i64,
395 ) -> anyhow::Result<&ItemCasesSettingsByLevel> {
396 self.item_case_settings_by_level(level)
397 .with_context(|| format!("No item_case_settings with level={level}"))
398 }
399
400 fn item_rarity(&self, rarity_id: ItemRarityId) -> Option<&ItemRarity> {
401 self.item_rarities.iter().find(|r| r.id == rarity_id)
402 }
403
404 fn require_item_rarity(&self, rarity_id: ItemRarityId) -> anyhow::Result<&ItemRarity> {
405 self.item_rarity(rarity_id)
406 .with_context(|| format!("No item_rarity in config with id={rarity_id}"))
407 }
408
409 fn item_template(&self, item_id: ItemTemplateId) -> Option<&ItemTemplate> {
410 self.items.iter().find(|i| i.id == item_id)
411 }
412
413 fn require_item_template(&self, item_id: ItemTemplateId) -> anyhow::Result<&ItemTemplate> {
414 self.item_template(item_id)
415 .with_context(|| format!("No item_template in config with id={item_id}"))
416 }
417
418 fn mail_template(&self, template_id: uuid::Uuid) -> Option<&MailTemplate> {
419 self.mail_templates.iter().find(|t| t.id == template_id)
420 }
421
422 fn require_mail_template(&self, template_id: uuid::Uuid) -> anyhow::Result<&MailTemplate> {
423 self.mail_template(template_id)
424 .with_context(|| format!("No mail_template in config with id={template_id}"))
425 }
426
427 fn patron_level_info(&self, level: i64) -> Option<&ReferralLevelInfo> {
428 self.patron_levels.iter().find(|x| x.level == level)
429 }
430
431 fn require_patron_level_info(&self, level: i64) -> anyhow::Result<&ReferralLevelInfo> {
432 self.patron_level_info(level)
433 .with_context(|| format!("No patron_level_info in config with level={level}"))
434 }
435
436 fn projectile(&self, projectile_id: ProjectileId) -> Option<&Projectile> {
437 self.projectiles.iter().find(|p| p.id == projectile_id)
438 }
439
440 fn require_projectile(&self, projectile_id: ProjectileId) -> anyhow::Result<&Projectile> {
441 self.projectile(projectile_id)
442 .with_context(|| format!("No projectile in config with id={projectile_id}"))
443 }
444
445 fn quest(&self, quest_id: uuid::Uuid) -> Option<&QuestTemplate> {
446 self.quests.iter().find(|q| q.id == quest_id)
447 }
448
449 fn require_quest(&self, quest_id: uuid::Uuid) -> anyhow::Result<&QuestTemplate> {
450 self.quest(quest_id)
451 .with_context(|| format!("No quest in config with id={quest_id}"))
452 }
453
454 fn quest_by_code(&self, code: &str) -> Option<&QuestTemplate> {
455 self.quests.iter().find(|q| q.code.as_deref() == Some(code))
456 }
457
458 fn skin(&self, skin_id: SkinId) -> Option<&ConfigSkin> {
459 self.skins.iter().find(|s| s.id == skin_id)
460 }
461
462 fn require_skin(&self, skin_id: SkinId) -> anyhow::Result<&ConfigSkin> {
463 self.skin(skin_id)
464 .with_context(|| format!("No skin in config with id={skin_id}"))
465 }
466
467 fn vassal_task_template(&self, task_template_id: uuid::Uuid) -> Option<&VassalTaskTemplate> {
468 self.vassal_tasks.iter().find(|t| t.id == task_template_id)
469 }
470
471 fn require_vassal_task_template(
472 &self,
473 task_template_id: uuid::Uuid,
474 ) -> anyhow::Result<&VassalTaskTemplate> {
475 self.vassal_task_template(task_template_id)
476 .with_context(|| {
477 format!("No vassal_task_template in config with id={task_template_id}")
478 })
479 }
480
481 fn cheat_script(&self, cheat_script_id: CheatScriptId) -> Option<&CheatScript> {
482 self.cheat_scripts.iter().find(|c| c.id == cheat_script_id)
483 }
484
485 fn talent(&self, talent_id: TalentId) -> Option<&TalentTemplate> {
486 self.talents.iter().find(|t| t.id == talent_id)
487 }
488
489 fn require_talent(&self, talent_id: TalentId) -> anyhow::Result<&TalentTemplate> {
490 self.talent(talent_id)
491 .with_context(|| format!("No talent in config with id={talent_id}"))
492 }
493
494 fn allowed_wishlist_rarity_ids(&self, ability_case_level: i64) -> Vec<AbilityRarityId> {
495 self.ability_case_settings_by_level(ability_case_level)
496 .map(|settings| settings.allowed_wishlist_rarity_ids.clone())
497 .unwrap_or_default()
498 }
499
500 fn pet_template(&self, pet_id: PetId) -> Option<&PetTemplate> {
501 self.pet_templates.iter().find(|t| t.id == pet_id)
502 }
503
504 fn require_pet_template(&self, pet_id: PetId) -> anyhow::Result<&PetTemplate> {
505 self.pet_template(pet_id)
506 .with_context(|| format!("No pet_template in config with id={pet_id}"))
507 }
508
509 fn pet_rarity(&self, rarity_id: PetRarityId) -> Option<&PetRarity> {
510 self.pet_rarities.iter().find(|r| r.id == rarity_id)
511 }
512
513 fn pet_level(&self, rarity_id: PetRarityId, level: i64) -> Option<&PetLevel> {
514 self.pet_levels
515 .iter()
516 .find(|x| x.rarity_id == rarity_id && x.level == level)
517 }
518
519 fn pet_slots_for_chapter_level(&self, chapter_level: i64) -> Option<u64> {
520 self.pet_slots_levels
521 .iter()
522 .rev()
523 .find(|level| level.from_chapter_level <= chapter_level)
524 .map(|level| level.pet_slots)
525 }
526
527 fn pet_case_settings_by_level(&self, level: i64) -> Option<&PetCasesSettingsByLevel> {
528 self.pet_cases_settings.iter().find(|x| x.level == level)
529 }
530
531 fn require_pet_case_settings_by_level(
532 &self,
533 level: i64,
534 ) -> anyhow::Result<&PetCasesSettingsByLevel> {
535 self.pet_case_settings_by_level(level)
536 .with_context(|| format!("No pet_case_settings with level={level}"))
537 }
538
539 fn allowed_pet_wishlist_rarity_ids(
540 &self,
541 pet_case_level: i64,
542 ) -> Vec<essences::pets::PetRarityId> {
543 self.pet_case_settings_by_level(pet_case_level)
544 .map(|settings| settings.allowed_wishlist_rarity_ids.clone())
545 .unwrap_or_default()
546 }
547
548 fn trigger_stone_template(
549 &self,
550 template_id: StoneTemplateId,
551 ) -> Option<&TriggerStoneTemplate> {
552 self.trigger_stones.iter().find(|t| t.id == template_id)
553 }
554
555 fn effect_stone_template(&self, template_id: StoneTemplateId) -> Option<&EffectStoneTemplate> {
556 self.effect_stones.iter().find(|t| t.id == template_id)
557 }
558}