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