veloren_voxygen/hud/
util.rs

1use super::img_ids;
2use common::{
3    comp::{
4        BuffData, BuffKind,
5        inventory::trade_pricing::TradePricing,
6        item::{
7            Effects, Item, ItemDefinitionId, ItemDesc, ItemI18n, ItemKind, MaterialKind,
8            MaterialStatManifest,
9            armor::{Armor, ArmorKind, Protection},
10            tool::{Hands, Tool, ToolKind},
11        },
12    },
13    effect::Effect,
14    trade::{Good, SitePrices},
15};
16use conrod_core::image;
17use i18n::{Localization, fluent_args};
18use std::{borrow::Cow, fmt::Write};
19
20pub fn price_desc<'a>(
21    prices: &Option<SitePrices>,
22    item_definition_id: ItemDefinitionId<'_>,
23    i18n: &'a Localization,
24) -> Option<(Cow<'a, str>, Cow<'a, str>, f32)> {
25    let prices = prices.as_ref()?;
26    let materials = TradePricing::get_materials(&item_definition_id)?;
27    let coinprice = prices.values.get(&Good::Coin).cloned().unwrap_or(1.0);
28    let buyprice: f32 = materials
29        .iter()
30        .map(|e| prices.values.get(&e.1).cloned().unwrap_or_default() * e.0)
31        .sum();
32    let sellprice: f32 = materials
33        .iter()
34        .map(|e| prices.values.get(&e.1).cloned().unwrap_or_default() * e.0 * e.1.trade_margin())
35        .sum();
36
37    let deal_goodness: f32 = materials
38        .iter()
39        .map(|e| prices.values.get(&e.1).cloned().unwrap_or(0.0))
40        .sum::<f32>()
41        / prices.values.get(&Good::Coin).cloned().unwrap_or(1.0)
42        / (materials.len() as f32);
43    let deal_goodness = deal_goodness.log(2.0);
44
45    let buy_string = i18n.get_msg_ctx("hud-trade-buy", &fluent_args! {
46        "coin_num" => buyprice / coinprice,
47        "coin_formatted" => format!("{:0.1}", buyprice / coinprice),
48    });
49    let sell_string = i18n.get_msg_ctx("hud-trade-sell", &fluent_args! {
50        "coin_num" => sellprice / coinprice,
51        "coin_formatted" => format!("{:0.1}", sellprice / coinprice),
52    });
53
54    let deal_goodness = match deal_goodness {
55        x if x < -2.5 => 0.0,
56        x if x < -1.05 => 0.25,
57        x if x < -0.95 => 0.5,
58        x if x < 0.0 => 0.75,
59        _ => 1.0,
60    };
61    Some((buy_string, sell_string, deal_goodness))
62}
63
64pub fn item_text<'a, I: ItemDesc + ?Sized>(
65    item: &I,
66    i18n: &'a Localization,
67    i18n_spec: &'a ItemI18n,
68) -> (String, String) {
69    let (title, desc) = item.i18n(i18n_spec);
70
71    (i18n.get_content(&title), i18n.get_content(&desc))
72}
73
74pub fn describe<'a, I: ItemDesc + ?Sized>(
75    item: &I,
76    i18n: &'a Localization,
77    i18n_spec: &'a ItemI18n,
78) -> String {
79    let (title, _) = item_text(item, i18n, i18n_spec);
80    let amount = item.amount();
81
82    if amount.get() > 1 {
83        format!("{amount} x {title}")
84    } else {
85        title
86    }
87}
88
89pub fn kind_text<'a>(kind: &ItemKind, i18n: &'a Localization) -> Cow<'a, str> {
90    match kind {
91        ItemKind::Armor(armor) => armor_kind(armor, i18n),
92        ItemKind::Tool(tool) => Cow::Owned(format!(
93            "{} ({})",
94            tool_kind(tool, i18n),
95            tool_hands(tool, i18n)
96        )),
97        ItemKind::ModularComponent(mc) => {
98            if let Some(toolkind) = mc.toolkind() {
99                Cow::Owned(format!(
100                    "{} {}",
101                    i18n.get_msg(&format!("common-weapons-{}", toolkind.identifier_name())),
102                    i18n.get_msg("common-kind-modular_component_partial")
103                ))
104            } else {
105                i18n.get_msg("common-kind-modular_component")
106            }
107        },
108        ItemKind::Glider => i18n.get_msg("common-kind-glider"),
109        ItemKind::Consumable { .. } => i18n.get_msg("common-kind-consumable"),
110        ItemKind::Throwable { .. } => i18n.get_msg("common-kind-throwable"),
111        ItemKind::Utility { .. } => i18n.get_msg("common-kind-utility"),
112        ItemKind::Ingredient { .. } => i18n.get_msg("common-kind-ingredient"),
113        ItemKind::Lantern { .. } => i18n.get_msg("common-kind-lantern"),
114        ItemKind::TagExamples { .. } => Cow::Borrowed(""),
115        ItemKind::RecipeGroup { .. } => i18n.get_msg("common-kind-recipegroup"),
116    }
117}
118
119pub fn material_kind_text<'a>(kind: &MaterialKind, i18n: &'a Localization) -> Cow<'a, str> {
120    match kind {
121        MaterialKind::Metal => i18n.get_msg("common-material-metal"),
122        MaterialKind::Gem => i18n.get_msg("common-material-gem"),
123        MaterialKind::Wood => i18n.get_msg("common-material-wood"),
124        MaterialKind::Stone => i18n.get_msg("common-material-stone"),
125        MaterialKind::Cloth => i18n.get_msg("common-material-cloth"),
126        MaterialKind::Hide => i18n.get_msg("common-material-hide"),
127    }
128}
129
130pub fn stats_count(item: &dyn ItemDesc, msm: &MaterialStatManifest) -> usize {
131    let mut count = match &*item.kind() {
132        ItemKind::Armor(armor) => {
133            let armor_stats = armor.stats(msm, item.stats_durability_multiplier());
134            armor_stats.energy_reward.is_some() as usize
135                + armor_stats.energy_max.is_some() as usize
136                + armor_stats.stealth.is_some() as usize
137                + armor_stats.precision_power.is_some() as usize
138                + armor_stats.poise_resilience.is_some() as usize
139                + armor_stats.protection.is_some() as usize
140                + (item.num_slots() > 0) as usize
141        },
142        ItemKind::Tool(_) => 6,
143        ItemKind::Consumable { effects, .. } => match effects {
144            Effects::Any(_) | Effects::One(_) => 1,
145            Effects::All(effects) => effects.len(),
146        },
147        ItemKind::RecipeGroup { recipes } => {
148            /* Add one for recipe known/not known message */
149            recipes.len() + 1
150        },
151        ItemKind::ModularComponent { .. } => 6,
152        _ => 0,
153    };
154    if item.has_durability() {
155        count += 1;
156    }
157    count
158}
159
160pub fn line_count(item: &dyn ItemDesc, msm: &MaterialStatManifest, i18n: &Localization) -> usize {
161    match &*item.kind() {
162        ItemKind::Consumable { effects, .. } => {
163            let descs = consumable_desc(effects, i18n);
164            let mut lines = 0;
165            for desc in descs {
166                lines += desc.matches('\n').count() + 1;
167            }
168
169            lines
170        },
171        _ => stats_count(item, msm),
172    }
173}
174
175/// Returns i18n key for a buff with title, .desc and optionally .stat
176///
177/// NOTE: not to be confused with buff key for buff's kill message
178fn buff_key(buff: BuffKind) -> &'static str {
179    match buff {
180        // Buffs
181        BuffKind::Regeneration => "buff-heal",
182        BuffKind::Saturation => "buff-saturation",
183        BuffKind::Potion => "buff-potion",
184        BuffKind::Agility => "buff-agility",
185        BuffKind::CampfireHeal => "buff-campfire_heal",
186        BuffKind::EnergyRegen => "buff-energy_regen",
187        BuffKind::IncreaseMaxHealth => "buff-increase_max_health",
188        BuffKind::IncreaseMaxEnergy => "buff-increase_max_energy",
189        BuffKind::Invulnerability => "buff-invulnerability",
190        BuffKind::ProtectingWard => "buff-protectingward",
191        BuffKind::Frenzied => "buff-frenzied",
192        BuffKind::Hastened => "buff-hastened",
193        BuffKind::Fortitude => "buff-fortitude",
194        BuffKind::Reckless => "buff-reckless",
195        // BuffKind::SalamanderAspect => "buff-salamanderaspect",
196        BuffKind::Flame => "buff-burn",
197        BuffKind::Frigid => "buff-frigid",
198        BuffKind::Lifesteal => "buff-lifesteal",
199        BuffKind::ImminentCritical => "buff-imminentcritical",
200        BuffKind::Fury => "buff-fury",
201        BuffKind::Sunderer => "buff-sunderer",
202        BuffKind::Defiance => "buff-defiance",
203        BuffKind::Bloodfeast => "buff-bloodfeast",
204        BuffKind::Berserk => "buff-berserk",
205        BuffKind::ScornfulTaunt => "buff-scornfultaunt",
206        BuffKind::Tenacity => "buff-tenacity",
207        BuffKind::Resilience => "buff-resilience",
208        // Debuffs
209        BuffKind::Bleeding => "buff-bleed",
210        BuffKind::Cursed => "buff-cursed",
211        BuffKind::Burning => "buff-burn",
212        BuffKind::Crippled => "buff-crippled",
213        BuffKind::Frozen => "buff-frozen",
214        BuffKind::Wet => "buff-wet",
215        BuffKind::Ensnared => "buff-ensnared",
216        BuffKind::Poisoned => "buff-poisoned",
217        BuffKind::Parried => "buff-parried",
218        BuffKind::PotionSickness => "buff-potionsickness",
219        BuffKind::Heatstroke => "buff-heatstroke",
220        BuffKind::Rooted => "buff-rooted",
221        BuffKind::Winded => "buff-winded",
222        BuffKind::Concussion => "buff-concussion",
223        BuffKind::Staggered => "buff-staggered",
224        // Neutral
225        BuffKind::Polymorphed => "buff-polymorphed",
226    }
227}
228
229/// Returns localized buff title
230pub fn get_buff_title(buff: BuffKind, i18n: &Localization) -> Cow<str> {
231    let key = buff_key(buff);
232
233    i18n.get_msg(key)
234}
235
236/// Returns localized buff description
237pub fn get_buff_desc(buff: BuffKind, data: BuffData, i18n: &Localization) -> Cow<str> {
238    let key = buff_key(buff);
239    if let BuffKind::CampfireHeal = buff {
240        i18n.get_attr_ctx(key, "desc", &i18n::fluent_args! {
241            "rate" => data.strength * 100.0
242        })
243    } else {
244        i18n.get_attr(key, "desc")
245    }
246}
247
248/// Takes N `effects` and returns N effect descriptions
249/// If effect isn't intended to have description, returns empty string
250///
251/// FIXME: handle which effects should have description in `stats_count`
252/// to not waste space in item box
253pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec<String> {
254    let mut descriptions = Vec::new();
255    match effects {
256        Effects::Any(_) => {
257            descriptions.push(i18n.get_msg("buff-mysterious").into_owned());
258        },
259        Effects::All(_) | Effects::One(_) => {
260            for effect in effects.effects() {
261                let mut description = String::new();
262                if let Effect::Buff(buff) = effect {
263                    let strength = buff.data.strength;
264                    let dur_secs = buff.data.duration.map(|d| d.0 as f32);
265                    let str_total = dur_secs.map_or(strength, |secs| strength * secs);
266
267                    let format_float =
268                        |input: f32| format!("{:.1}", input).trim_end_matches(".0").to_string();
269
270                    let buff_desc = match buff.kind {
271                        // These share common buff-key and show full possible regen
272                        BuffKind::Saturation | BuffKind::Regeneration | BuffKind::Potion => {
273                            let key = "buff-heal";
274                            i18n.get_attr_ctx(key, "stat", &i18n::fluent_args! {
275                                "str_total" => format_float(str_total),
276                            })
277                        },
278                        // Shows its full possible regen
279                        BuffKind::EnergyRegen => {
280                            let key = buff_key(buff.kind);
281                            i18n.get_attr_ctx(key, "stat", &i18n::fluent_args! {
282                                "str_total" => format_float(str_total),
283                            })
284                        },
285                        // Show buff strength
286                        BuffKind::IncreaseMaxEnergy
287                        | BuffKind::IncreaseMaxHealth => {
288                            let key = buff_key(buff.kind);
289                            i18n.get_attr_ctx(key, "stat", &i18n::fluent_args! {
290                                "strength" => format_float(strength),
291                            })
292                        },
293                        // Show percentage
294                        BuffKind::PotionSickness
295                        | BuffKind::Agility => {
296                            let key = buff_key(buff.kind);
297                            i18n.get_attr_ctx(key, "stat", &i18n::fluent_args! {
298                                "strength" => format_float(strength * 100.0),
299                            })
300                        },
301                        // Independent of strength
302                        BuffKind::Invulnerability => {
303                            let key = buff_key(buff.kind);
304                            i18n.get_attr(key, "stat")
305                        },
306                        // Have no stat description
307                        BuffKind::Bleeding
308                        | BuffKind::Burning
309                        | BuffKind::CampfireHeal
310                        | BuffKind::Cursed
311                        | BuffKind::ProtectingWard
312                        | BuffKind::Crippled
313                        | BuffKind::Frenzied
314                        | BuffKind::Frozen
315                        | BuffKind::Wet
316                        | BuffKind::Ensnared
317                        | BuffKind::Poisoned
318                        | BuffKind::Hastened
319                        | BuffKind::Fortitude
320                        | BuffKind::Parried
321                        | BuffKind::Reckless
322                        | BuffKind::Polymorphed
323                        | BuffKind::Flame
324                        | BuffKind::Frigid
325                        | BuffKind::Lifesteal
326                        // | BuffKind::SalamanderAspect
327                        | BuffKind::ImminentCritical
328                        | BuffKind::Fury
329                        | BuffKind::Sunderer
330                        | BuffKind::Defiance
331                        | BuffKind::Bloodfeast
332                        | BuffKind::Berserk
333                        | BuffKind::Heatstroke
334                        | BuffKind::ScornfulTaunt
335                        | BuffKind::Rooted
336                        | BuffKind::Winded
337                        | BuffKind::Concussion
338                        | BuffKind::Staggered
339                        | BuffKind::Tenacity
340                        | BuffKind::Resilience => Cow::Borrowed(""),
341                    };
342
343                    write!(&mut description, "{}", buff_desc).unwrap();
344
345                    let dur_desc = if let Some(dur_secs) = dur_secs {
346                        match buff.kind {
347                            BuffKind::Saturation
348                            | BuffKind::Regeneration
349                            | BuffKind::EnergyRegen => {
350                                i18n.get_msg_ctx("buff-text-over_seconds", &i18n::fluent_args! {
351                                    "dur_secs" => dur_secs
352                                })
353                            },
354                            BuffKind::IncreaseMaxEnergy
355                            | BuffKind::Agility
356                            | BuffKind::IncreaseMaxHealth
357                            | BuffKind::Invulnerability
358                            | BuffKind::PotionSickness
359                            | BuffKind::Polymorphed => {
360                                i18n.get_msg_ctx("buff-text-for_seconds", &i18n::fluent_args! {
361                                    "dur_secs" => dur_secs
362                                })
363                            },
364                            BuffKind::Bleeding
365                            | BuffKind::Burning
366                            | BuffKind::Potion
367                            | BuffKind::CampfireHeal
368                            | BuffKind::Cursed
369                            | BuffKind::ProtectingWard
370                            | BuffKind::Crippled
371                            | BuffKind::Frenzied
372                            | BuffKind::Frozen
373                            | BuffKind::Wet
374                            | BuffKind::Ensnared
375                            | BuffKind::Poisoned
376                            | BuffKind::Hastened
377                            | BuffKind::Fortitude
378                            | BuffKind::Parried
379                            | BuffKind::Reckless
380                            | BuffKind::Flame
381                            | BuffKind::Frigid
382                            | BuffKind::Lifesteal
383                            // | BuffKind::SalamanderAspect
384                            | BuffKind::ImminentCritical
385                            | BuffKind::Fury
386                            | BuffKind::Sunderer
387                            | BuffKind::Defiance
388                            | BuffKind::Bloodfeast
389                            | BuffKind::Berserk
390                            | BuffKind::Heatstroke
391                            | BuffKind::ScornfulTaunt
392                            | BuffKind::Rooted
393                            | BuffKind::Winded
394                            | BuffKind::Concussion
395                            | BuffKind::Staggered
396                            | BuffKind::Tenacity
397                            | BuffKind::Resilience => Cow::Borrowed(""),
398                        }
399                    } else if let BuffKind::Saturation
400                    | BuffKind::Regeneration
401                    | BuffKind::EnergyRegen = buff.kind
402                    {
403                        i18n.get_msg("buff-text-every_second")
404                    } else {
405                        Cow::Borrowed("")
406                    };
407
408                    write!(&mut description, " {}", dur_desc).unwrap();
409                }
410                descriptions.push(description);
411            }
412        },
413    }
414    descriptions
415}
416
417// Armor
418fn armor_kind<'a>(armor: &Armor, i18n: &'a Localization) -> Cow<'a, str> {
419    let kind = match armor.kind {
420        ArmorKind::Shoulder => i18n.get_msg("hud-bag-shoulders"),
421        ArmorKind::Chest => i18n.get_msg("hud-bag-chest"),
422        ArmorKind::Belt => i18n.get_msg("hud-bag-belt"),
423        ArmorKind::Hand => i18n.get_msg("hud-bag-hands"),
424        ArmorKind::Pants => i18n.get_msg("hud-bag-legs"),
425        ArmorKind::Foot => i18n.get_msg("hud-bag-feet"),
426        ArmorKind::Back => i18n.get_msg("hud-bag-back"),
427        ArmorKind::Backpack => i18n.get_msg("hud-bag-backpack"),
428        ArmorKind::Ring => i18n.get_msg("hud-bag-ring"),
429        ArmorKind::Neck => i18n.get_msg("hud-bag-neck"),
430        ArmorKind::Head => i18n.get_msg("hud-bag-head"),
431        ArmorKind::Tabard => i18n.get_msg("hud-bag-tabard"),
432        ArmorKind::Bag => i18n.get_msg("hud-bag-bag"),
433    };
434    kind
435}
436
437// Tool
438fn tool_kind<'a>(tool: &Tool, i18n: &'a Localization) -> Cow<'a, str> {
439    let kind = match tool.kind {
440        ToolKind::Sword => i18n.get_msg("common-weapons-sword"),
441        ToolKind::Axe => i18n.get_msg("common-weapons-axe"),
442        ToolKind::Hammer => i18n.get_msg("common-weapons-hammer"),
443        ToolKind::Bow => i18n.get_msg("common-weapons-bow"),
444        ToolKind::Dagger => i18n.get_msg("common-weapons-dagger"),
445        ToolKind::Staff => i18n.get_msg("common-weapons-staff"),
446        ToolKind::Sceptre => i18n.get_msg("common-weapons-sceptre"),
447        ToolKind::Shield => i18n.get_msg("common-weapons-shield"),
448        ToolKind::Spear => i18n.get_msg("common-weapons-spear"),
449        ToolKind::Blowgun => i18n.get_msg("common-weapons-blowgun"),
450        ToolKind::Natural => i18n.get_msg("common-weapons-natural"),
451        ToolKind::Debug => i18n.get_msg("common-tool-debug"),
452        ToolKind::Farming => i18n.get_msg("common-tool-farming"),
453        ToolKind::Instrument => i18n.get_msg("common-tool-instrument"),
454        ToolKind::Pick => i18n.get_msg("common-tool-pick"),
455        ToolKind::Shovel => i18n.get_msg("common-tool-shovel"),
456        ToolKind::Empty => i18n.get_msg("common-empty"),
457    };
458    kind
459}
460
461/// Output the number of hands needed to hold a tool
462pub fn tool_hands<'a>(tool: &Tool, i18n: &'a Localization) -> Cow<'a, str> {
463    let hands = match tool.hands {
464        Hands::One => i18n.get_msg("common-hands-one"),
465        Hands::Two => i18n.get_msg("common-hands-two"),
466    };
467    hands
468}
469
470/// Compare two type, output a colored character to show comparison
471pub fn comparison<T: PartialOrd>(first: T, other: T) -> (&'static str, conrod_core::Color) {
472    if first == other {
473        ("•", conrod_core::color::GREY)
474    } else if other < first {
475        ("▲", conrod_core::color::GREEN)
476    } else {
477        ("▼", conrod_core::color::RED)
478    }
479}
480
481/// Compare two Option type, output a colored character to show comparison
482pub fn option_comparison<T: PartialOrd>(
483    first: &Option<T>,
484    other: &Option<T>,
485) -> (&'static str, conrod_core::Color) {
486    if let Some(first) = first {
487        if let Some(other) = other {
488            if first == other {
489                ("•", conrod_core::color::GREY)
490            } else if other < first {
491                ("▲", conrod_core::color::GREEN)
492            } else {
493                ("▼", conrod_core::color::RED)
494            }
495        } else {
496            ("▲", conrod_core::color::GREEN)
497        }
498    } else if other.is_some() {
499        ("▼", conrod_core::color::RED)
500    } else {
501        ("•", conrod_core::color::GREY)
502    }
503}
504
505/// Output protection as a string
506pub fn protec2string(stat: Protection) -> String {
507    match stat {
508        Protection::Normal(a) => format!("{:.1}", a),
509        Protection::Invincible => "Inf".to_string(),
510    }
511}
512
513/// Gets the durability of an item in a format more intuitive for UI
514pub fn item_durability(item: &dyn ItemDesc) -> Option<u32> {
515    let durability = item
516        .durability_lost()
517        .or_else(|| item.has_durability().then_some(0));
518    durability.map(|d| Item::MAX_DURABILITY - d)
519}
520
521pub fn ability_image(imgs: &img_ids::Imgs, ability_id: &str) -> image::Id {
522    match ability_id {
523        // Debug stick
524        "common.abilities.debug.forwardboost" => imgs.flyingrod_m1,
525        "common.abilities.debug.upboost" => imgs.flyingrod_m2,
526        "common.abilities.debug.possess" => imgs.snake_arrow_0,
527        // Sword
528        "veloren.core.pseudo_abilities.sword.heavy_stance" => imgs.sword_heavy_stance,
529        "veloren.core.pseudo_abilities.sword.agile_stance" => imgs.sword_agile_stance,
530        "veloren.core.pseudo_abilities.sword.defensive_stance" => imgs.sword_defensive_stance,
531        "veloren.core.pseudo_abilities.sword.crippling_stance" => imgs.sword_crippling_stance,
532        "veloren.core.pseudo_abilities.sword.cleaving_stance" => imgs.sword_cleaving_stance,
533        "veloren.core.pseudo_abilities.sword.double_slash" => imgs.sword_double_slash,
534        "common.abilities.sword.basic_double_slash" => imgs.sword_basic_double_slash,
535        "common.abilities.sword.heavy_double_slash" => imgs.sword_heavy_double_slash,
536        "common.abilities.sword.agile_double_slash" => imgs.sword_agile_double_slash,
537        "common.abilities.sword.defensive_double_slash" => imgs.sword_defensive_double_slash,
538        "common.abilities.sword.crippling_double_slash" => imgs.sword_crippling_double_slash,
539        "common.abilities.sword.cleaving_double_slash" => imgs.sword_cleaving_double_slash,
540        "veloren.core.pseudo_abilities.sword.secondary_ability" => imgs.sword_secondary_ability,
541        "common.abilities.sword.basic_thrust" => imgs.sword_basic_thrust,
542        "common.abilities.sword.heavy_slam" => imgs.sword_heavy_slam,
543        "common.abilities.sword.agile_perforate" => imgs.sword_agile_perforate,
544        "common.abilities.sword.agile_dual_perforate" => imgs.sword_agile_perforate,
545        "common.abilities.sword.defensive_vital_jab" => imgs.sword_defensive_vital_jab,
546        "common.abilities.sword.crippling_deep_rend" => imgs.sword_crippling_deep_rend,
547        "common.abilities.sword.cleaving_spiral_slash" => imgs.sword_cleaving_spiral_slash,
548        "common.abilities.sword.cleaving_dual_spiral_slash" => imgs.sword_cleaving_spiral_slash,
549        "veloren.core.pseudo_abilities.sword.crescent_slash" => imgs.sword_crescent_slash,
550        "common.abilities.sword.basic_crescent_slash" => imgs.sword_basic_crescent_slash,
551        "common.abilities.sword.heavy_crescent_slash" => imgs.sword_heavy_crescent_slash,
552        "common.abilities.sword.agile_crescent_slash" => imgs.sword_agile_crescent_slash,
553        "common.abilities.sword.defensive_crescent_slash" => imgs.sword_defensive_crescent_slash,
554        "common.abilities.sword.crippling_crescent_slash" => imgs.sword_crippling_crescent_slash,
555        "common.abilities.sword.cleaving_crescent_slash" => imgs.sword_cleaving_crescent_slash,
556        "veloren.core.pseudo_abilities.sword.fell_strike" => imgs.sword_fell_strike,
557        "common.abilities.sword.basic_fell_strike" => imgs.sword_basic_fell_strike,
558        "common.abilities.sword.heavy_fell_strike" => imgs.sword_heavy_fell_strike,
559        "common.abilities.sword.agile_fell_strike" => imgs.sword_agile_fell_strike,
560        "common.abilities.sword.defensive_fell_strike" => imgs.sword_defensive_fell_strike,
561        "common.abilities.sword.crippling_fell_strike" => imgs.sword_crippling_fell_strike,
562        "common.abilities.sword.cleaving_fell_strike" => imgs.sword_cleaving_fell_strike,
563        "veloren.core.pseudo_abilities.sword.skewer" => imgs.sword_skewer,
564        "common.abilities.sword.basic_skewer" => imgs.sword_basic_skewer,
565        "common.abilities.sword.heavy_skewer" => imgs.sword_heavy_skewer,
566        "common.abilities.sword.agile_skewer" => imgs.sword_agile_skewer,
567        "common.abilities.sword.defensive_skewer" => imgs.sword_defensive_skewer,
568        "common.abilities.sword.crippling_skewer" => imgs.sword_crippling_skewer,
569        "common.abilities.sword.cleaving_skewer" => imgs.sword_cleaving_skewer,
570        "veloren.core.pseudo_abilities.sword.cascade" => imgs.sword_cascade,
571        "common.abilities.sword.basic_cascade" => imgs.sword_basic_cascade,
572        "common.abilities.sword.heavy_cascade" => imgs.sword_heavy_cascade,
573        "common.abilities.sword.agile_cascade" => imgs.sword_agile_cascade,
574        "common.abilities.sword.defensive_cascade" => imgs.sword_defensive_cascade,
575        "common.abilities.sword.crippling_cascade" => imgs.sword_crippling_cascade,
576        "common.abilities.sword.cleaving_cascade" => imgs.sword_cleaving_cascade,
577        "veloren.core.pseudo_abilities.sword.cross_cut" => imgs.sword_cross_cut,
578        "common.abilities.sword.basic_cross_cut" => imgs.sword_basic_cross_cut,
579        "common.abilities.sword.heavy_cross_cut" => imgs.sword_heavy_cross_cut,
580        "common.abilities.sword.agile_cross_cut" => imgs.sword_agile_cross_cut,
581        "common.abilities.sword.defensive_cross_cut" => imgs.sword_defensive_cross_cut,
582        "common.abilities.sword.crippling_cross_cut" => imgs.sword_crippling_cross_cut,
583        "common.abilities.sword.cleaving_cross_cut" => imgs.sword_cleaving_cross_cut,
584        "common.abilities.sword.basic_dual_cross_cut" => imgs.sword_basic_cross_cut,
585        "common.abilities.sword.heavy_dual_cross_cut" => imgs.sword_heavy_cross_cut,
586        "common.abilities.sword.agile_dual_cross_cut" => imgs.sword_agile_cross_cut,
587        "common.abilities.sword.defensive_dual_cross_cut" => imgs.sword_defensive_cross_cut,
588        "common.abilities.sword.crippling_dual_cross_cut" => imgs.sword_crippling_cross_cut,
589        "common.abilities.sword.cleaving_dual_cross_cut" => imgs.sword_cleaving_cross_cut,
590        "veloren.core.pseudo_abilities.sword.finisher" => imgs.sword_finisher,
591        "common.abilities.sword.basic_mighty_strike" => imgs.sword_basic_mighty_strike,
592        "common.abilities.sword.heavy_guillotine" => imgs.sword_heavy_guillotine,
593        "common.abilities.sword.agile_hundred_cuts" => imgs.sword_agile_hundred_cuts,
594        "common.abilities.sword.defensive_counter" => imgs.sword_defensive_counter,
595        "common.abilities.sword.crippling_mutilate" => imgs.sword_crippling_mutilate,
596        "common.abilities.sword.cleaving_bladestorm" => imgs.sword_cleaving_bladestorm,
597        "common.abilities.sword.cleaving_dual_bladestorm" => imgs.sword_cleaving_bladestorm,
598        "common.abilities.sword.heavy_sweep" => imgs.sword_heavy_sweep,
599        "common.abilities.sword.heavy_pommel_strike" => imgs.sword_heavy_pommel_strike,
600        "common.abilities.sword.agile_quick_draw" => imgs.sword_agile_quick_draw,
601        "common.abilities.sword.agile_feint" => imgs.sword_agile_feint,
602        "common.abilities.sword.defensive_riposte" => imgs.sword_defensive_riposte,
603        "common.abilities.sword.defensive_disengage" => imgs.sword_defensive_disengage,
604        "common.abilities.sword.crippling_gouge" => imgs.sword_crippling_gouge,
605        "common.abilities.sword.crippling_hamstring" => imgs.sword_crippling_hamstring,
606        "common.abilities.sword.cleaving_whirlwind_slice" => imgs.sword_cleaving_whirlwind_slice,
607        "common.abilities.sword.cleaving_dual_whirlwind_slice" => {
608            imgs.sword_cleaving_whirlwind_slice
609        },
610        "common.abilities.sword.cleaving_earth_splitter" => imgs.sword_cleaving_earth_splitter,
611        "common.abilities.sword.heavy_fortitude" => imgs.sword_heavy_fortitude,
612        "common.abilities.sword.heavy_pillar_thrust" => imgs.sword_heavy_pillar_thrust,
613        "common.abilities.sword.agile_dancing_edge" => imgs.sword_agile_dancing_edge,
614        "common.abilities.sword.agile_flurry" => imgs.sword_agile_flurry,
615        "common.abilities.sword.agile_dual_flurry" => imgs.sword_agile_flurry,
616        "common.abilities.sword.defensive_stalwart_sword" => imgs.sword_defensive_stalwart_sword,
617        "common.abilities.sword.defensive_deflect" => imgs.sword_defensive_deflect,
618        "common.abilities.sword.crippling_eviscerate" => imgs.sword_crippling_eviscerate,
619        "common.abilities.sword.crippling_bloody_gash" => imgs.sword_crippling_bloody_gash,
620        "common.abilities.sword.cleaving_blade_fever" => imgs.sword_cleaving_blade_fever,
621        "common.abilities.sword.cleaving_sky_splitter" => imgs.sword_cleaving_sky_splitter,
622        // Axe
623        "common.abilities.axe.triple_chop" => imgs.axe_triple_chop,
624        "common.abilities.axe.cleave" => imgs.axe_cleave,
625        "common.abilities.axe.brutal_swing" => imgs.axe_brutal_swing,
626        "common.abilities.axe.berserk" => imgs.axe_berserk,
627        "common.abilities.axe.rising_tide" => imgs.axe_rising_tide,
628        "common.abilities.axe.savage_sense" => imgs.axe_savage_sense,
629        "common.abilities.axe.adrenaline_rush" => imgs.axe_adrenaline_rush,
630        "common.abilities.axe.execute" => imgs.axe_execute,
631        "common.abilities.axe.maelstrom" => imgs.axe_maelstrom,
632        "common.abilities.axe.rake" => imgs.axe_rake,
633        "common.abilities.axe.bloodfeast" => imgs.axe_bloodfeast,
634        "common.abilities.axe.fierce_raze" => imgs.axe_fierce_raze,
635        "common.abilities.axe.dual_fierce_raze" => imgs.axe_fierce_raze,
636        "common.abilities.axe.furor" => imgs.axe_furor,
637        "common.abilities.axe.fracture" => imgs.axe_fracture,
638        "common.abilities.axe.lacerate" => imgs.axe_lacerate,
639        "common.abilities.axe.riptide" => imgs.axe_riptide,
640        "common.abilities.axe.skull_bash" => imgs.axe_skull_bash,
641        "common.abilities.axe.sunder" => imgs.axe_sunder,
642        "common.abilities.axe.plunder" => imgs.axe_plunder,
643        "common.abilities.axe.defiance" => imgs.axe_defiance,
644        "common.abilities.axe.keelhaul" => imgs.axe_keelhaul,
645        "common.abilities.axe.bulkhead" => imgs.axe_bulkhead,
646        "common.abilities.axe.capsize" => imgs.axe_capsize,
647        // Hammer
648        "common.abilities.hammer.solid_smash" => imgs.hammer_solid_smash,
649        "common.abilities.hammer.wide_wallop" => imgs.hammer_wide_wallop,
650        "common.abilities.hammer.scornful_swipe" => imgs.hammer_scornful_swipe,
651        "common.abilities.hammer.tremor" => imgs.hammer_tremor,
652        "common.abilities.hammer.vigorous_bash" => imgs.hammer_vigorous_bash,
653        "common.abilities.hammer.heavy_whorl" => imgs.hammer_heavy_whorl,
654        "common.abilities.hammer.dual_heavy_whorl" => imgs.hammer_heavy_whorl,
655        "common.abilities.hammer.intercept" => imgs.hammer_intercept,
656        "common.abilities.hammer.dual_intercept" => imgs.hammer_intercept,
657        "common.abilities.hammer.retaliate" => imgs.hammer_retaliate,
658        "common.abilities.hammer.spine_cracker" => imgs.hammer_spine_cracker,
659        "common.abilities.hammer.breach" => imgs.hammer_breach,
660        "common.abilities.hammer.pile_driver" => imgs.hammer_pile_driver,
661        "common.abilities.hammer.lung_pummel" => imgs.hammer_lung_pummel,
662        "common.abilities.hammer.helm_crusher" => imgs.hammer_helm_crusher,
663        "common.abilities.hammer.iron_tempest" => imgs.hammer_iron_tempest,
664        "common.abilities.hammer.dual_iron_tempest" => imgs.hammer_iron_tempest,
665        "common.abilities.hammer.upheaval" => imgs.hammer_upheaval,
666        "common.abilities.hammer.dual_upheaval" => imgs.hammer_upheaval,
667        "common.abilities.hammer.rampart" => imgs.hammer_rampart,
668        "common.abilities.hammer.tenacity" => imgs.hammer_tenacity,
669        "common.abilities.hammer.thunderclap" => imgs.hammer_thunderclap,
670        "common.abilities.hammer.seismic_shock" => imgs.hammer_seismic_shock,
671        "common.abilities.hammer.earthshaker" => imgs.hammer_earthshaker,
672        "common.abilities.hammer.judgement" => imgs.hammer_judgement,
673        // Bow
674        "common.abilities.bow.charged" => imgs.bow_m1,
675        "common.abilities.bow.repeater" => imgs.bow_m2,
676        "common.abilities.bow.shotgun" => imgs.skill_bow_jump_burst,
677        // Staff
678        "common.abilities.staff.firebomb" => imgs.fireball,
679        "common.abilities.staff.flamethrower" => imgs.flamethrower,
680        "common.abilities.staff.fireshockwave" => imgs.fire_aoe,
681        // Sceptre
682        "common.abilities.sceptre.lifestealbeam" => imgs.skill_sceptre_lifesteal,
683        "common.abilities.sceptre.healingaura" => imgs.skill_sceptre_heal,
684        "common.abilities.sceptre.wardingaura" => imgs.skill_sceptre_aura,
685        // Shield
686        "common.abilities.shield.singlestrike" => imgs.onehshield_m1,
687        "common.abilities.shield.power_guard" => imgs.onehshield_m1,
688        // Dagger
689        "common.abilities.dagger.tempbasic" => imgs.onehdagger_m1,
690        // Pickaxe
691        "common.abilities.pick.swing" => imgs.mining,
692        // Shovel
693        "common.abilities.shovel.dig" => imgs.dig,
694        // Instruments
695        "common.abilities.music.bass" => imgs.instrument,
696        "common.abilities.music.flute" => imgs.instrument,
697        "common.abilities.music.harp" => imgs.instrument,
698        "common.abilities.music.perc" => imgs.instrument,
699        "common.abilities.music.kalimba" => imgs.instrument,
700        "common.abilities.music.melodica" => imgs.instrument,
701        "common.abilities.music.lute" => imgs.instrument,
702        "common.abilities.music.guitar" => imgs.instrument,
703        "common.abilities.music.dark_guitar" => imgs.instrument,
704        "common.abilities.music.sitar" => imgs.instrument,
705        "common.abilities.music.double_bass" => imgs.instrument,
706        "common.abilities.music.glass_flute" => imgs.instrument,
707        "common.abilities.music.lyre" => imgs.instrument,
708        "common.abilities.music.wildskin_drum" => imgs.instrument,
709        "common.abilities.music.icy_talharpa" => imgs.instrument,
710        "common.abilities.music.washboard" => imgs.instrument,
711        "common.abilities.music.steeltonguedrum" => imgs.instrument,
712        "common.abilities.music.shamisen" => imgs.instrument,
713        "common.abilities.music.kora" => imgs.instrument,
714        "common.abilities.music.banjo" => imgs.instrument,
715        // Glider
716        "common.abilities.debug.glide_boost" => imgs.flyingrod_m2,
717        "common.abilities.debug.glide_speeder" => imgs.flyingrod_m1,
718        _ => imgs.not_found,
719    }
720}
721
722pub fn ability_description<'a>(
723    ability_id: &str,
724    loc: &'a Localization,
725) -> (Cow<'a, str>, Cow<'a, str>) {
726    let ability = ability_id.replace('.', "-");
727
728    (loc.get_msg(&ability), loc.get_attr(&ability, "desc"))
729}