veloren/voxygen/src/hud/util.rs

285 lines
11 KiB
Rust
Raw Normal View History

2021-02-25 16:25:39 +00:00
use common::{
comp::{
inventory::trade_pricing::TradePricing,
2021-02-25 16:25:39 +00:00
item::{
armor::{Armor, ArmorKind, Protection},
2021-02-25 16:25:39 +00:00
tool::{Hands, StatKind, Stats, Tool, ToolKind},
Item, ItemDesc, ItemKind, MaterialKind, MaterialStatManifest, ModularComponent,
2021-02-25 16:25:39 +00:00
},
BuffKind,
},
effect::Effect,
trade::{Good, SitePrices},
};
2021-07-29 18:47:45 +00:00
use i18n::Localization;
2021-03-28 12:27:59 +00:00
use std::{borrow::Cow, fmt::Write};
2021-03-24 22:17:25 +00:00
pub fn price_desc(
prices: &Option<SitePrices>,
item_definition_id: &str,
i18n: &Localization,
) -> Option<(String, String, f32)> {
if let Some(prices) = prices {
let (material, factor) = TradePricing::get_material(item_definition_id);
let coinprice = prices.values.get(&Good::Coin).cloned().unwrap_or(1.0);
let buyprice = prices.values.get(&material).cloned().unwrap_or_default() * factor;
let sellprice = buyprice * material.trade_margin();
let deal_goodness = prices.values.get(&material).cloned().unwrap_or(0.0)
/ prices.values.get(&Good::Coin).cloned().unwrap_or(1.0);
let deal_goodness = deal_goodness.log(2.0);
let buy_string = format!(
"{} : {:0.1} {}",
2021-03-28 12:27:59 +00:00
i18n.get("hud.trade.buy_price"),
buyprice / coinprice,
2021-03-28 12:27:59 +00:00
i18n.get("hud.trade.coin"),
);
let sell_string = format!(
"{} : {:0.1} {}",
2021-03-28 12:27:59 +00:00
i18n.get("hud.trade.sell_price"),
2021-03-24 22:17:25 +00:00
sellprice / coinprice,
2021-03-28 12:27:59 +00:00
i18n.get("hud.trade.coin"),
);
let deal_goodness = match deal_goodness {
x if x < -2.5 => 0.0,
x if x < -1.05 => 0.25,
x if x < -0.95 => 0.5,
x if x < 0.0 => 0.75,
_ => 1.0,
};
Some((buy_string, sell_string, deal_goodness))
2021-03-24 22:17:25 +00:00
} else {
None
}
}
2021-03-28 12:27:59 +00:00
pub fn kind_text<'a>(kind: &ItemKind, i18n: &'a Localization) -> Cow<'a, str> {
2021-03-02 00:45:02 +00:00
match kind {
2021-07-11 18:41:52 +00:00
ItemKind::Armor(armor) => Cow::Borrowed(armor_kind(armor, i18n)),
2021-03-28 12:27:59 +00:00
ItemKind::Tool(tool) => Cow::Owned(format!(
"{} ({})",
2021-07-11 18:41:52 +00:00
tool_kind(tool, i18n),
tool_hands(tool, i18n)
2021-03-28 12:27:59 +00:00
)),
ItemKind::ModularComponent(_mc) => Cow::Borrowed(i18n.get("common.bag.shoulders")),
ItemKind::Glider(_glider) => Cow::Borrowed(i18n.get("common.kind.glider")),
ItemKind::Consumable { .. } => Cow::Borrowed(i18n.get("common.kind.consumable")),
ItemKind::Throwable { .. } => Cow::Borrowed(i18n.get("common.kind.throwable")),
ItemKind::Utility { .. } => Cow::Borrowed(i18n.get("common.kind.utility")),
ItemKind::Ingredient { .. } => Cow::Borrowed(i18n.get("common.kind.ingredient")),
ItemKind::Lantern { .. } => Cow::Borrowed(i18n.get("common.kind.lantern")),
ItemKind::TagExamples { .. } => Cow::Borrowed(""),
2021-03-02 00:45:02 +00:00
}
}
pub fn material_kind_text<'a>(kind: &MaterialKind, i18n: &'a Localization) -> &'a str {
2021-05-26 00:12:43 +00:00
match kind {
MaterialKind::Metal { .. } => i18n.get("common.material.metal"),
MaterialKind::Wood { .. } => i18n.get("common.material.wood"),
MaterialKind::Stone { .. } => i18n.get("common.material.stone"),
MaterialKind::Cloth { .. } => i18n.get("common.material.cloth"),
MaterialKind::Hide { .. } => i18n.get("common.material.hide"),
2021-05-26 00:12:43 +00:00
}
}
2021-03-28 12:27:59 +00:00
// TODO: localization, refactor when mc are player facing
2021-03-24 22:17:25 +00:00
pub fn modular_component_desc(
mc: &ModularComponent,
components: &[Item],
msm: &MaterialStatManifest,
description: &str,
) -> String {
let stats = StatKind::Direct(mc.stats).resolve_stats(msm, components);
let statblock = statblock_desc(&stats);
let mut result = format!("Modular Component\n\n{}\n\n{}", statblock, description);
if !components.is_empty() {
result += "\n\nMade from:\n";
for component in components {
result += component.name();
result += "\n"
}
result += "\n";
}
result
}
2020-10-07 02:23:20 +00:00
pub fn stats_count(item: &dyn ItemDesc) -> usize {
let mut count = match item.kind() {
ItemKind::Armor(armor) => {
if matches!(armor.kind, ArmorKind::Bag(_)) {
0
} else {
5
}
},
ItemKind::Tool(_) => 4,
ItemKind::Consumable { effects, .. } => effects.len(),
ItemKind::ModularComponent { .. } => 1,
_ => 0,
};
let is_bag = match item.kind() {
ItemKind::Armor(armor) => matches!(armor.kind, ArmorKind::Bag(_)),
_ => false,
};
if item.num_slots() != 0 && !is_bag {
count += 1
}
count as usize
}
/// Takes N `effects` and returns N effect descriptions
/// If effect isn't intended to have description, returns empty string
///
/// FIXME: handle which effects should have description in `stats_count`
/// to not waste space in item box
pub fn consumable_desc(effects: &[Effect], i18n: &Localization) -> Vec<String> {
let mut descriptions = Vec::new();
2021-02-25 16:25:39 +00:00
2021-03-02 23:53:21 +00:00
for effect in effects {
let mut description = String::new();
2021-03-02 23:53:21 +00:00
if let Effect::Buff(buff) = effect {
let strength = buff.data.strength;
let dur_secs = buff.data.duration.map(|d| d.as_secs_f32());
let str_total = dur_secs.map_or(strength, |secs| strength * secs);
2021-03-02 23:53:21 +00:00
let buff_desc = match buff.kind {
2021-03-24 01:05:14 +00:00
BuffKind::Saturation | BuffKind::Regeneration | BuffKind::Potion => i18n
.get("buff.stat.health")
.replace("{str_total}", &str_total.to_string()),
2021-03-24 01:05:14 +00:00
BuffKind::IncreaseMaxEnergy => i18n
.get("buff.stat.increase_max_energy")
.replace("{strength}", &strength.to_string()),
2021-03-24 01:05:14 +00:00
BuffKind::IncreaseMaxHealth => i18n
.get("buff.stat.increase_max_health")
.replace("{strength}", &strength.to_string()),
2021-04-11 03:49:47 +00:00
BuffKind::Invulnerability => i18n.get("buff.stat.invulnerability").to_string(),
2021-03-05 10:12:44 +00:00
BuffKind::Bleeding
2021-04-15 20:32:38 +00:00
| BuffKind::Burning
2021-03-05 10:12:44 +00:00
| BuffKind::CampfireHeal
| BuffKind::Cursed
| BuffKind::ProtectingWard
| BuffKind::Crippled
2021-05-30 15:51:47 +00:00
| BuffKind::Frenzied
| BuffKind::Frozen
| BuffKind::Wet
| BuffKind::Ensnared => "".to_owned(),
2021-03-02 23:53:21 +00:00
};
2021-03-16 12:19:31 +00:00
write!(&mut description, "{}", buff_desc).unwrap();
2021-03-02 23:31:47 +00:00
2021-03-24 01:05:14 +00:00
let dur_desc = if let Some(dur_secs) = dur_secs {
match buff.kind {
2021-03-24 01:05:14 +00:00
BuffKind::Saturation | BuffKind::Regeneration => i18n
.get("buff.text.over_seconds")
.replace("{dur_secs}", &dur_secs.to_string()),
BuffKind::IncreaseMaxEnergy
| BuffKind::IncreaseMaxHealth
2021-03-24 01:05:14 +00:00
| BuffKind::Invulnerability => i18n
.get("buff.text.for_seconds")
.replace("{dur_secs}", &dur_secs.to_string()),
BuffKind::Bleeding
2021-04-15 20:32:38 +00:00
| BuffKind::Burning
| BuffKind::Potion
| BuffKind::CampfireHeal
2021-03-05 10:12:44 +00:00
| BuffKind::Cursed
| BuffKind::ProtectingWard
| BuffKind::Crippled
2021-05-30 15:51:47 +00:00
| BuffKind::Frenzied
| BuffKind::Frozen
| BuffKind::Wet
| BuffKind::Ensnared => "".to_owned(),
}
} else if let BuffKind::Saturation | BuffKind::Regeneration = buff.kind {
2021-03-24 01:05:14 +00:00
i18n.get("buff.text.every_second").to_string()
} else {
"".to_owned()
2021-03-02 23:53:21 +00:00
};
write!(&mut description, " {}", dur_desc).unwrap();
2021-02-25 16:25:39 +00:00
}
descriptions.push(description);
2021-02-25 16:25:39 +00:00
}
descriptions
2020-10-07 02:23:20 +00:00
}
2021-03-02 00:45:02 +00:00
// Armor
2021-03-28 12:27:59 +00:00
fn armor_kind<'a>(armor: &Armor, i18n: &'a Localization) -> &'a str {
let kind = match armor.kind {
2021-03-24 01:05:14 +00:00
ArmorKind::Shoulder(_) => i18n.get("hud.bag.shoulders"),
ArmorKind::Chest(_) => i18n.get("hud.bag.chest"),
ArmorKind::Belt(_) => i18n.get("hud.bag.belt"),
ArmorKind::Hand(_) => i18n.get("hud.bag.hands"),
ArmorKind::Pants(_) => i18n.get("hud.bag.legs"),
ArmorKind::Foot(_) => i18n.get("hud.bag.feet"),
ArmorKind::Back(_) => i18n.get("hud.bag.back"),
ArmorKind::Ring(_) => i18n.get("hud.bag.ring"),
ArmorKind::Neck(_) => i18n.get("hud.bag.neck"),
ArmorKind::Head(_) => i18n.get("hud.bag.head"),
ArmorKind::Tabard(_) => i18n.get("hud.bag.tabard"),
ArmorKind::Bag(_) => i18n.get("hud.bag.bag"),
};
2021-03-28 12:27:59 +00:00
kind
2021-03-02 00:45:02 +00:00
}
// Tool
2021-03-28 12:27:59 +00:00
fn tool_kind<'a>(tool: &Tool, i18n: &'a Localization) -> &'a str {
let kind = match tool.kind {
2021-03-24 01:05:14 +00:00
ToolKind::Sword => i18n.get("common.weapons.sword"),
ToolKind::Axe => i18n.get("common.weapons.axe"),
ToolKind::Hammer => i18n.get("common.weapons.hammer"),
ToolKind::Bow => i18n.get("common.weapons.bow"),
ToolKind::Dagger => i18n.get("common.weapons.dagger"),
ToolKind::Staff => i18n.get("common.weapons.staff"),
ToolKind::Sceptre => i18n.get("common.weapons.sceptre"),
ToolKind::Shield => i18n.get("common.weapons.shield"),
ToolKind::Spear => i18n.get("common.weapons.spear"),
ToolKind::Natural => i18n.get("common.weapons.natural"),
2021-03-24 01:05:14 +00:00
ToolKind::Debug => i18n.get("common.tool.debug"),
ToolKind::Farming => i18n.get("common.tool.farming"),
ToolKind::Pick => i18n.get("common.tool.pick"),
ToolKind::Empty => i18n.get("common.empty"),
};
2021-03-28 12:27:59 +00:00
kind
2021-03-02 00:45:02 +00:00
}
2021-01-13 03:26:51 +00:00
/// Output the number of hands needed to hold a tool
2021-03-28 12:27:59 +00:00
pub fn tool_hands<'a>(tool: &Tool, i18n: &'a Localization) -> &'a str {
2021-02-13 00:57:52 +00:00
let hands = match tool.hands {
2021-03-24 01:05:14 +00:00
Hands::One => i18n.get("common.hands.one"),
Hands::Two => i18n.get("common.hands.two"),
2021-02-13 00:57:52 +00:00
};
2021-03-28 12:27:59 +00:00
hands
2021-03-02 00:45:02 +00:00
}
fn statblock_desc(stats: &Stats) -> String {
format!(
// TODO: Change display of Effect Power based on toolkind equipped and what effect power is
// affecting
2021-03-16 12:19:31 +00:00
"Power: {:0.1}\n\nPoise Strength: {:0.1}\n\nSpeed: {:0.1}\n\n",
stats.power * 10.0,
stats.effect_power * 10.0,
stats.speed,
) + &format!("Crit chance: {:0.1}%\n\n", stats.crit_chance * 100.0,)
}
/// Compare two type, output a colored character to show comparison
2021-03-28 12:27:59 +00:00
pub fn comparison<T: PartialOrd>(first: T, other: T) -> (&'static str, conrod_core::Color) {
2021-03-02 00:45:02 +00:00
if first == other {
2021-03-28 12:27:59 +00:00
("", conrod_core::color::GREY)
2021-03-02 00:45:02 +00:00
} else if other < first {
2021-03-28 12:27:59 +00:00
("", conrod_core::color::GREEN)
2021-03-02 00:45:02 +00:00
} else {
2021-03-28 12:27:59 +00:00
("", conrod_core::color::RED)
2021-03-02 00:45:02 +00:00
}
}
/// Output protection as a string
2021-03-02 00:45:02 +00:00
pub fn protec2string(stat: Protection) -> String {
match stat {
Protection::Normal(a) => format!("{:.1}", a),
2021-03-20 20:24:26 +00:00
Protection::Invincible => "Inf".to_string(),
2021-03-02 00:45:02 +00:00
}
}