Inline skills::Boost to voxygen code

This commit is contained in:
juliancoffee 2021-08-24 23:00:03 +03:00
parent 74183ae5ec
commit 9211b4a00a
9 changed files with 824 additions and 341 deletions

View File

@ -1211,10 +1211,10 @@ impl CharacterAbility {
*base_damage *= modifiers.base_damage.powi(level.into());
}
if let Ok(Some(level)) = skillset.skill_level(Hammer(LKnockback)) {
*knockback *= modifiers.base_damage.powi(level.into());
*knockback *= modifiers.knockback.powi(level.into());
}
if let Ok(Some(level)) = skillset.skill_level(Hammer(LCost)) {
*energy_cost *= modifiers.base_damage.powi(level.into());
*energy_cost *= modifiers.energy_cost.powi(level.into());
}
if let Ok(Some(level)) = skillset.skill_level(Hammer(LDistance)) {
let strength = modifiers.leap_strength;
@ -1275,7 +1275,7 @@ impl CharacterAbility {
*charge_duration *= charge_time.powi(level.into());
}
if let Ok(Some(level)) = skillset.skill_level(Bow(CMove)) {
*move_speed *= modifiers.charge_rate.powi(level.into());
*move_speed *= modifiers.move_speed.powi(level.into());
}
},
CharacterAbility::RepeaterRanged {
@ -1344,8 +1344,8 @@ impl CharacterAbility {
let regen_level = skillset.skill_level_or(Staff(BRegen), 0);
let range_level = skillset.skill_level_or(Staff(BRadius), 0);
let power = modifiers.power.powi(damage_level.into());
let regen = modifiers.power.powi(regen_level.into());
let range = modifiers.power.powi(range_level.into());
let regen = modifiers.regen.powi(regen_level.into());
let range = modifiers.range.powi(range_level.into());
*projectile = projectile.modified_projectile(power, regen, range);
},
CharacterAbility::BasicBeam {

View File

@ -16,7 +16,7 @@ pub mod theropod;
use crate::{
assets::{self, Asset},
consts::{HUMAN_DENSITY, WATER_DENSITY},
consts::{HUMANOID_HP_PER_LEVEL, HUMAN_DENSITY, WATER_DENSITY},
make_case_elim,
npc::NpcKind,
};
@ -563,7 +563,7 @@ impl Body {
#[allow(unreachable_patterns)]
pub fn base_health_increase(&self) -> u32 {
match self {
Body::Humanoid(_) => 50,
Body::Humanoid(_) => HUMANOID_HP_PER_LEVEL,
Body::QuadrupedSmall(quadruped_small) => match quadruped_small.species {
quadruped_small::Species::Boar => 20,
quadruped_small::Species::Batfox => 10,

View File

@ -1,9 +1,8 @@
use crate::comp::Body;
use crate::{comp::Body, consts::ENERGY_PER_LEVEL};
use serde::{Deserialize, Serialize};
use specs::{Component, DerefFlaggedStorage};
use specs_idvs::IdvStorage;
pub const ENERGY_PER_LEVEL: u32 = 50;
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct Energy {
current: u32,

View File

@ -14,19 +14,25 @@ use std::{
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ToolKind {
// weapons
Sword,
Axe,
Hammer,
Bow,
Dagger,
Staff,
Sceptre,
// future weapons
Dagger,
Shield,
Spear,
Natural, // Intended for invisible weapons (e.g. a creature using its claws or biting)
// tools
Debug,
Farming,
Pick,
// npcs
/// Intended for invisible weapons (e.g. a creature using its claws or
/// biting)
Natural,
/// This is an placeholder item, it is used by non-humanoid npcs to attack
Empty,
}

View File

@ -1,10 +1,6 @@
use crate::{
assets::{self, Asset, AssetExt},
comp::{
self,
body::{humanoid, Body},
item::tool::ToolKind,
},
comp::item::tool::ToolKind,
};
use hashbrown::{HashMap, HashSet};
use lazy_static::lazy_static;
@ -442,8 +438,8 @@ impl SceptreTreeModifiers {
pub struct MiningTreeModifiers {
pub speed: f32,
pub gem_gain: f64,
pub ore_gain: f64,
pub gem_gain: f32,
pub ore_gain: f32,
}
impl MiningTreeModifiers {
@ -493,58 +489,6 @@ impl GeneralTreeModifiers {
}
}
}
/// Enum which returned as result from `boost` function
/// `Number` can represent values from -inf to +inf,
/// but it should generaly be in range -50..50
///
/// Number(-25) says that some value
/// will be reduced by 25% (for example energy consumption)
///
/// Number(15) says that some value
/// will be increased by 15% (for example damage)
// TODO: move it to voxygen diary code
// (and inline directly to formating skill descriptions)
#[derive(Debug, Clone, Copy)]
pub enum BoostValue {
Number(i16),
NonDescriptive,
}
impl From<i16> for BoostValue {
fn from(number: i16) -> Self { BoostValue::Number(number) }
}
/// Returns value which corresponds to the boost given by this skill
pub trait Boost {
fn boost(self) -> BoostValue;
}
impl Boost for Skill {
fn boost(self) -> BoostValue {
match self {
// General tree boosts
Skill::General(s) => s.boost(),
// Weapon tree boosts
Skill::Sword(s) => s.boost(),
Skill::Axe(s) => s.boost(),
Skill::Hammer(s) => s.boost(),
Skill::Bow(s) => s.boost(),
Skill::Staff(s) => s.boost(),
Skill::Sceptre(s) => s.boost(),
// Movement tree boosts
Skill::Roll(s) => s.boost(),
Skill::Climb(s) => s.boost(),
Skill::Swim(s) => s.boost(),
// Non-combat tree boosts
Skill::Pick(s) => s.boost(),
// Unlock Group has more complex semantic
Skill::UnlockGroup(_) => BoostValue::NonDescriptive,
}
}
}
pub enum SkillError {
MissingSkill,
}
@ -573,32 +517,6 @@ pub enum SwordSkill {
SSpins,
}
impl Boost for SwordSkill {
fn boost(self) -> BoostValue {
match self {
// Dash
Self::DDamage => 20.into(),
Self::DCost => (-25_i16).into(),
Self::DDrain => (-25_i16).into(),
Self::DScaling => 20.into(),
Self::DSpeed => 15.into(),
// Spin
Self::SDamage => 40.into(),
Self::SSpeed => (-20_i16).into(),
Self::SCost => (-25_i16).into(),
// Non-descriptive values
Self::InterruptingAttacks
| Self::TsCombo
| Self::TsDamage
| Self::TsRegen
| Self::TsSpeed
| Self::DInfinite
| Self::UnlockSpin
| Self::SSpins => BoostValue::NonDescriptive,
}
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum AxeSkill {
// Double strike upgrades
@ -620,30 +538,6 @@ pub enum AxeSkill {
LDistance,
}
impl Boost for AxeSkill {
fn boost(self) -> BoostValue {
match self {
// Spin upgrades
Self::SDamage => 30.into(),
Self::SSpeed => (-20_i16).into(),
Self::SCost => (-25_i16).into(),
// Leap upgrades
Self::LDamage => 35.into(),
Self::LKnockback => 40.into(),
Self::LCost => (-25_i16).into(),
Self::LDistance => 20.into(),
// Non-descriptive boosts
Self::UnlockLeap
| Self::DsCombo
| Self::DsDamage
| Self::DsSpeed
| Self::DsRegen
| Self::SInfinite
| Self::SHelicopter => BoostValue::NonDescriptive,
}
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum HammerSkill {
// Single strike upgrades
@ -665,30 +559,6 @@ pub enum HammerSkill {
LRange,
}
impl Boost for HammerSkill {
fn boost(self) -> BoostValue {
match self {
// Single strike upgrades
Self::SsKnockback => 50.into(),
// Charged melee upgrades
Self::CDamage => 25.into(),
Self::CKnockback => 50.into(),
Self::CDrain => (-25_i16).into(),
Self::CSpeed => 25.into(),
// Leap upgrades
Self::LDamage => 40.into(),
Self::LKnockback => 50.into(),
Self::LCost => (-25_i16).into(),
Self::LDistance => 25.into(),
Self::LRange => 1.into(),
// Non-descriptive values
Self::UnlockLeap | Self::SsDamage | Self::SsSpeed | Self::SsRegen => {
BoostValue::NonDescriptive
},
}
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum BowSkill {
// Passives
@ -711,32 +581,6 @@ pub enum BowSkill {
SSpread,
}
impl Boost for BowSkill {
fn boost(self) -> BoostValue {
match self {
// Passive
Self::ProjSpeed => 20.into(),
// Charged upgrades
Self::CDamage => 20.into(),
Self::CRegen => 20.into(),
Self::CKnockback => 20.into(),
Self::CSpeed => 10.into(),
Self::CMove => 10.into(),
// Repeater upgrades
Self::RDamage => 20.into(),
Self::RCost => (-20_i16).into(),
Self::RSpeed => 20.into(),
// Shotgun upgrades
Self::SDamage => 20.into(),
Self::SCost => (-20_i16).into(),
Self::SArrows => 1.into(),
Self::SSpread => (-20_i16).into(),
// Non-descriptive values
Self::UnlockShotgun => BoostValue::NonDescriptive,
}
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum StaffSkill {
// Basic ranged upgrades
@ -756,29 +600,6 @@ pub enum StaffSkill {
SCost,
}
impl Boost for StaffSkill {
fn boost(self) -> BoostValue {
match self {
// Fireball upgrades
Self::BDamage => 20.into(),
Self::BRegen => 20.into(),
Self::BRadius => 15.into(),
// Flamethrower upgrades
Self::FDamage => 30.into(),
Self::FRange => 25.into(),
Self::FDrain => (-20_i16).into(),
Self::FVelocity => 25.into(),
// Shockwave upgrades
Self::SDamage => 30.into(),
Self::SKnockback => 30.into(),
Self::SRange => 20.into(),
Self::SCost => (-20_i16).into(),
// Non-descriptive values
Self::UnlockShockwave => BoostValue::NonDescriptive,
}
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum SceptreSkill {
// Lifesteal beam upgrades
@ -786,7 +607,7 @@ pub enum SceptreSkill {
LRange,
LLifesteal,
LRegen,
// Healing beam upgrades
// Healing aura upgrades
HHeal,
HRange,
HDuration,
@ -799,53 +620,12 @@ pub enum SceptreSkill {
ACost,
}
impl Boost for SceptreSkill {
fn boost(self) -> BoostValue {
match self {
// Lifesteal beam upgrades
Self::LDamage => 20.into(),
Self::LRange => 20.into(),
Self::LRegen => 20.into(),
Self::LLifesteal => 15.into(),
// Healing beam upgrades
Self::HHeal => 20.into(),
Self::HRange => 20.into(),
Self::HDuration => 20.into(),
Self::HCost => (-20_i16).into(),
// Warding aura upgrades
Self::AStrength => 15.into(),
Self::ADuration => 20.into(),
Self::ARange => 25.into(),
Self::ACost => (-15_i16).into(),
Self::UnlockAura => BoostValue::NonDescriptive,
}
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum GeneralSkill {
HealthIncrease,
EnergyIncrease,
}
impl Boost for GeneralSkill {
fn boost(self) -> BoostValue {
// NOTE: These should be used only for UI.
// Source of truth are corresponding systems
match self {
Self::HealthIncrease => {
let health_increase =
(Body::Humanoid(humanoid::Body::random()).base_health_increase() / 10) as i16;
health_increase.into()
},
Self::EnergyIncrease => {
let energy_increase = (comp::energy::ENERGY_PER_LEVEL / 10) as i16;
energy_increase.into()
},
}
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum RollSkill {
Cost,
@ -853,44 +633,17 @@ pub enum RollSkill {
Duration,
}
impl Boost for RollSkill {
fn boost(self) -> BoostValue {
match self {
Self::Cost => (-10_i16).into(),
Self::Strength => 10.into(),
Self::Duration => 10.into(),
}
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum ClimbSkill {
Cost,
Speed,
}
impl Boost for ClimbSkill {
fn boost(self) -> BoostValue {
match self {
Self::Cost => (-20_i16).into(),
Self::Speed => 20.into(),
}
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum SwimSkill {
Speed,
}
impl Boost for SwimSkill {
fn boost(self) -> BoostValue {
match self {
Self::Speed => 25.into(),
}
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum MiningSkill {
Speed,
@ -898,16 +651,6 @@ pub enum MiningSkill {
GemGain,
}
impl Boost for MiningSkill {
fn boost(self) -> BoostValue {
match self {
Self::Speed => 10.into(),
Self::OreGain => 5.into(),
Self::GemGain => 5.into(),
}
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Serialize, Deserialize)]
pub enum SkillGroupKind {
General,

View File

@ -26,3 +26,7 @@ pub const MIN_RECOMMENDED_RAYON_THREADS: usize = 2;
pub const MIN_RECOMMENDED_TOKIO_THREADS: usize = 2;
pub const SOUND_TRAVEL_DIST_PER_VOLUME: f32 = 3.0;
// Stat increase per level (multiplied by 10 compared to what you'll see in UI)
pub const ENERGY_PER_LEVEL: u32 = 50;
pub const HUMANOID_HP_PER_LEVEL: u32 = 50;

View File

@ -354,14 +354,14 @@ pub fn handle_mine_block(
let mut rng = rand::thread_rng();
let need_double_ore = |rng: &mut rand::rngs::ThreadRng| {
let chance_mod = SKILL_MODIFIERS.mining_tree.ore_gain;
let chance_mod = f64::from(SKILL_MODIFIERS.mining_tree.ore_gain);
let skill_level =
skillset.skill_level_or(Skill::Pick(MiningSkill::OreGain), 0);
rng.gen_bool(chance_mod * f64::from(skill_level))
};
let need_double_gem = |rng: &mut rand::rngs::ThreadRng| {
let chance_mod = SKILL_MODIFIERS.mining_tree.gem_gain;
let chance_mod = f64::from(SKILL_MODIFIERS.mining_tree.gem_gain);
let skill_level =
skillset.skill_level_or(Skill::Pick(MiningSkill::GemGain), 0);

View File

@ -4,7 +4,10 @@ use super::{
Position, PositionSpecifier, Show, CRITICAL_HP_COLOR, HP_COLOR, TEXT_COLOR, UI_HIGHLIGHT_0,
UI_MAIN, XP_COLOR,
};
use crate::ui::{fonts::Fonts, ImageFrame, Tooltip, TooltipManager, Tooltipable};
use crate::{
hud,
ui::{fonts::Fonts, ImageFrame, Tooltip, TooltipManager, Tooltipable},
};
use conrod_core::{
color, image,
widget::{self, Button, Image, Rectangle, State, Text},
@ -13,11 +16,19 @@ use conrod_core::{
use i18n::Localization;
use client::{self, Client};
use common::comp::{
item::tool::ToolKind,
skills::{self, Boost, BoostValue, Skill},
SkillSet,
use common::{
comp::{
item::tool::ToolKind,
skills::{
self, AxeSkill, BowSkill, ClimbSkill, GeneralSkill, HammerSkill, MiningSkill,
RollSkill, SceptreSkill, Skill, SkillGroupKind, StaffSkill, SwimSkill, SwordSkill,
SKILL_MODIFIERS,
},
SkillSet,
},
consts::{ENERGY_PER_LEVEL, HUMANOID_HP_PER_LEVEL},
};
use std::borrow::Cow;
const ART_SIZE: [f64; 2] = [320.0, 320.0];
@ -576,42 +587,6 @@ fn skill_tree_from_str(string: &str) -> Option<SelectedSkillTree> {
}
}
/// Formats skill description, e.g.
///
/// "Increase max health by {boost}{SP}"
///
/// turns into
///
/// """
/// Increase max health by 5
///
/// Requires 4 SP
/// """
fn format_skill_description<'a>(
raw_description: &'a str,
skill: Skill,
skill_set: &'a skills::SkillSet,
localized_strings: &'a Localization,
) -> String {
let boost = skill.boost();
let with_values = match boost {
BoostValue::Number(x) => {
let value = x.abs();
raw_description.replace("{boost}", &value.to_string())
},
BoostValue::NonDescriptive => raw_description.to_owned(),
};
match skill_set.skill_level(skill) {
Ok(level) if level == skill.max_level() => with_values.replace("{SP}", ""),
_ => with_values.replace(
"{SP}",
&localized_strings
.get("hud.skill.req_sp")
.replace("{number}", &format!("{}", skill_set.skill_cost(skill))),
),
}
}
impl<'a> Diary<'a> {
fn handle_general_skills_window(
&mut self,
@ -643,13 +618,7 @@ impl<'a> Diary<'a> {
skills_bot_l,
skills_bot_r,
);
use skills::{
ClimbSkill,
GeneralSkill::*,
RollSkill::{self, *},
SkillGroupKind::*,
SwimSkill,
};
use skills::{GeneralSkill::*, RollSkill::*, SkillGroupKind::*};
use ToolKind::*;
// General Combat
Image::new(animate_by_pulse(
@ -2391,15 +2360,14 @@ impl<'a> Diary<'a> {
diary_tooltip: &Tooltip,
) {
let label = if self.skill_set.prerequisites_met(skill) {
format!(
"{}/{}",
self.skill_set
.skill_level(skill)
.map_or(0, |l| l.unwrap_or(1)),
skill.max_level().unwrap_or(1)
)
let current = self
.skill_set
.skill_level(skill)
.map_or(0, |l| l.unwrap_or(1));
let max = skill.max_level().unwrap_or(1);
format!("{}/{}", current, max)
} else {
"".to_string()
"".to_owned()
};
let label_color = if self.skill_set.is_at_max_level(skill) {
@ -2416,16 +2384,13 @@ impl<'a> Diary<'a> {
Color::Rgba(0.41, 0.41, 0.41, 0.7)
};
let skill_title_key = &format!("hud.skill.{}_title", skill_key);
let skill_title = self.localized_strings.get(skill_title_key);
let (skill_title, skill_description) = self.skill_info(skill);
let skill_description_key = &format!("hud.skill.{}", skill_key);
let skill_description = &format_skill_description(
self.localized_strings.get(skill_description_key),
skill,
self.skill_set,
self.localized_strings,
);
// Borrowcheck forced me to do this.
// I need to borrow self.tooltip_manager mutably later, while
// keeping references to self.localized_strings otherwise.
let skill_title: String = skill_title.to_owned();
let skill_description: String = skill_description.into_owned();
let button = Button::image(skill_image)
.w_h(74.0, 74.0)
@ -2439,8 +2404,8 @@ impl<'a> Diary<'a> {
.image_color(image_color)
.with_tooltip(
self.tooltip_manager,
skill_title,
skill_description,
&skill_title,
&skill_description,
diary_tooltip,
TEXT_COLOR,
)
@ -2450,4 +2415,759 @@ impl<'a> Diary<'a> {
events.push(Event::UnlockSkill(skill));
};
}
fn skill_info(&self, skill: Skill) -> (&str, Cow<str>) {
let (title, description) = skill_strings(skill, self.localized_strings);
let description = if description.contains("{SP}") {
Cow::Owned(self.splice_skill_reqs(skill, &description))
} else {
description
};
(title, description)
}
fn splice_skill_reqs(&self, skill: Skill, desc: &str) -> String {
let current_level = self.skill_set.skill_level(skill);
if matches!(current_level, Ok(level) if level == skill.max_level()) {
desc.replace("{SP}", "")
} else {
let req_sp_text = self.localized_strings.get("hud.skill.req_sp");
let skill_cost_text = self.skill_set.skill_cost(skill).to_string();
desc.replace("{SP}", &req_sp_text.replace("{number}", &skill_cost_text))
}
}
}
/// Returns skill info as a tuple of title and description.
///
/// Title is ready to use, description may include `"{SP}"` placeholder you
/// will want to handle yourself.
pub fn skill_strings(skill: Skill, i18n: &Localization) -> (&str, Cow<str>) {
match skill {
// general tree
Skill::General(s) => general_skill_strings(s, i18n),
Skill::UnlockGroup(s) => unlock_skill_strings(s, i18n),
// weapon trees
Skill::Sword(s) => sword_skill_strings(s, i18n),
Skill::Axe(s) => axe_skill_strings(s, i18n),
Skill::Hammer(s) => hammer_skill_strings(s, i18n),
Skill::Bow(s) => bow_skill_strings(s, i18n),
Skill::Staff(s) => staff_skill_strings(s, i18n),
Skill::Sceptre(s) => sceptre_skill_strings(s, i18n),
// movement trees
Skill::Roll(s) => roll_skill_strings(s, i18n),
Skill::Climb(s) => climb_skill_strings(s, i18n),
Skill::Swim(s) => swim_skill_strings(s, i18n),
// mining
Skill::Pick(s) => mining_skill_strings(s, i18n),
}
}
fn general_skill_strings(skill: GeneralSkill, i18n: &Localization) -> (&str, Cow<str>) {
match skill {
GeneralSkill::HealthIncrease => splice_constant(
i18n,
"hud.skill.inc_health_title",
"hud.skill.inc_health",
HUMANOID_HP_PER_LEVEL / 10,
),
GeneralSkill::EnergyIncrease => splice_constant(
i18n,
"hud.skill.inc_energy_title",
"hud.skill.inc_energy",
ENERGY_PER_LEVEL / 10,
),
}
}
fn unlock_skill_strings(group: SkillGroupKind, i18n: &Localization) -> (&str, Cow<str>) {
match group {
SkillGroupKind::Weapon(ToolKind::Sword) => {
localize(i18n, "hud.skill.unlck_sword_title", "hud.skill.unlck_sword")
},
SkillGroupKind::Weapon(ToolKind::Axe) => {
localize(i18n, "hud.skill.unlck_axe_title", "hud.skill.unlck_axe")
},
SkillGroupKind::Weapon(ToolKind::Hammer) => localize(
i18n,
"hud.skill.unlck_hammer_title",
"hud.skill.unlck_hammer",
),
SkillGroupKind::Weapon(ToolKind::Bow) => {
localize(i18n, "hud.skill.unlck_bow_title", "hud.skill.unlck_bow")
},
SkillGroupKind::Weapon(ToolKind::Staff) => {
localize(i18n, "hud.skill.unlck_staff_title", "hud.skill.unlck_staff")
},
SkillGroupKind::Weapon(ToolKind::Sceptre) => localize(
i18n,
"hud.skill.unlck_sceptre_title",
"hud.skill.unlck_sceptre",
),
SkillGroupKind::General
| SkillGroupKind::Weapon(
ToolKind::Dagger
| ToolKind::Shield
| ToolKind::Spear
| ToolKind::Debug
| ToolKind::Farming
| ToolKind::Pick
| ToolKind::Natural
| ToolKind::Empty,
) => {
tracing::warn!("Requesting title for unlocking unexpected skill group");
("", Cow::Owned(String::new()))
},
}
}
fn sword_skill_strings<'a>(skill: SwordSkill, i18n: &'a Localization) -> (&str, Cow<str>) {
let modifiers = SKILL_MODIFIERS.sword_tree;
match skill {
// triple strike
SwordSkill::TsCombo => localize(
i18n,
"hud.skill.sw_trip_str_combo_title",
"hud.skill.sw_trip_str_combo",
),
SwordSkill::TsDamage => localize(
i18n,
"hud.skill.sw_trip_str_dmg_title",
"hud.skill.sw_trip_str_dmg",
),
SwordSkill::TsSpeed => localize(
i18n,
"hud.skill.sw_trip_str_sp_title",
"hud.skill.sw_trip_str_sp",
),
SwordSkill::TsRegen => localize(
i18n,
"hud.skill.sw_trip_str_reg_title",
"hud.skill.sw_trip_str_reg",
),
// dash
SwordSkill::DDamage => splice_multiplier(
i18n,
"hud.skill.sw_dash_dmg_title",
"hud.skill.sw_dash_dmg",
modifiers.dash.base_damage,
),
SwordSkill::DDrain => splice_multiplier(
i18n,
"hud.skill.sw_dash_drain_title",
"hud.skill.sw_dash_drain",
modifiers.dash.energy_drain,
),
SwordSkill::DCost => splice_multiplier(
i18n,
"hud.skill.sw_dash_cost_title",
"hud.skill.sw_dash_cost",
modifiers.dash.energy_cost,
),
SwordSkill::DSpeed => splice_multiplier(
i18n,
"hud.skill.sw_dash_speed_title",
"hud.skill.sw_dash_speed",
modifiers.dash.forward_speed,
),
SwordSkill::DInfinite => localize(
i18n,
"hud.skill.sw_dash_charge_through_title",
"hud.skill.sw_dash_charge_through",
),
SwordSkill::DScaling => splice_multiplier(
i18n,
"hud.skill.sw_dash_scale_title",
"hud.skill.sw_dash_scale",
modifiers.dash.scaled_damage,
),
// spin
SwordSkill::UnlockSpin => localize(i18n, "hud.skill.sw_spin_title", "hud.skill.sw_spin"),
SwordSkill::SDamage => splice_multiplier(
i18n,
"hud.skill.sw_spin_dmg_title",
"hud.skill.sw_spin_dmg",
modifiers.spin.base_damage,
),
SwordSkill::SSpeed => splice_multiplier(
i18n,
"hud.skill.sw_spin_spd_title",
"hud.skill.sw_spin_spd",
modifiers.spin.swing_duration,
),
SwordSkill::SCost => splice_multiplier(
i18n,
"hud.skill.sw_spin_cost_title",
"hud.skill.sw_spin_cost",
modifiers.spin.energy_cost,
),
SwordSkill::SSpins => splice_constant(
i18n,
"hud.skill.sw_spin_spins_title",
"hud.skill.sw_spin_spins",
modifiers.spin.num,
),
// independent skills
SwordSkill::InterruptingAttacks => localize(
i18n,
"hud.skill.sw_interrupt_title",
"hud.skill.sw_interrupt",
),
}
}
fn axe_skill_strings(skill: AxeSkill, i18n: &Localization) -> (&str, Cow<str>) {
let modifiers = SKILL_MODIFIERS.axe_tree;
match skill {
// Double strike upgrades
AxeSkill::DsCombo => localize(
i18n,
"hud.skill.axe_double_strike_combo_title",
"hud.skill.axe_double_strike_combo",
),
AxeSkill::DsDamage => localize(
i18n,
"hud.skill.axe_double_strike_damage_title",
"hud.skill.axe_double_strike_damage",
),
AxeSkill::DsSpeed => localize(
i18n,
"hud.skill.axe_double_strike_speed_title",
"hud.skill.axe_double_strike_speed",
),
AxeSkill::DsRegen => localize(
i18n,
"hud.skill.axe_double_strike_regen_title",
"hud.skill.axe_double_strike_regen",
),
// Spin upgrades
AxeSkill::SInfinite => localize(
i18n,
"hud.skill.axe_infinite_axe_spin_title",
"hud.skill.axe_infinite_axe_spin",
),
AxeSkill::SHelicopter => localize(
i18n,
"hud.skill.axe_spin_helicopter_title",
"hud.skill.axe_spin_helicopter",
),
AxeSkill::SDamage => splice_multiplier(
i18n,
"hud.skill.axe_spin_damage_title",
"hud.skill.axe_spin_damage",
modifiers.spin.base_damage,
),
AxeSkill::SSpeed => splice_multiplier(
i18n,
"hud.skill.axe_spin_speed_title",
"hud.skill.axe_spin_speed",
modifiers.spin.swing_duration,
),
AxeSkill::SCost => splice_multiplier(
i18n,
"hud.skill.axe_spin_cost_title",
"hud.skill.axe_spin_cost",
modifiers.spin.energy_cost,
),
// Leap upgrades
AxeSkill::UnlockLeap => localize(
i18n,
"hud.skill.axe_unlock_leap_title",
"hud.skill.axe_unlock_leap",
),
AxeSkill::LDamage => splice_multiplier(
i18n,
"hud.skill.axe_leap_damage_title",
"hud.skill.axe_leap_damage",
modifiers.leap.base_damage,
),
AxeSkill::LKnockback => splice_multiplier(
i18n,
"hud.skill.axe_leap_knockback_title",
"hud.skill.axe_leap_knockback",
modifiers.leap.knockback,
),
AxeSkill::LCost => splice_multiplier(
i18n,
"hud.skill.axe_leap_cost_title",
"hud.skill.axe_leap_cost",
modifiers.leap.energy_cost,
),
AxeSkill::LDistance => splice_multiplier(
i18n,
"hud.skill.axe_leap_distance_title",
"hud.skill.axe_leap_distance",
modifiers.leap.leap_strength,
),
}
}
fn hammer_skill_strings(skill: HammerSkill, i18n: &Localization) -> (&str, Cow<str>) {
let modifiers = SKILL_MODIFIERS.hammer_tree;
// Single strike upgrades
match skill {
HammerSkill::SsKnockback => splice_multiplier(
i18n,
"hud.skill.hmr_single_strike_knockback_title",
"hud.skill.hmr_single_strike_knockback",
modifiers.single_strike.knockback,
),
HammerSkill::SsDamage => localize(
i18n,
"hud.skill.hmr_single_strike_damage_title",
"hud.skill.hmr_single_strike_damage",
),
HammerSkill::SsSpeed => localize(
i18n,
"hud.skill.hmr_single_strike_speed_title",
"hud.skill.hmr_single_strike_speed",
),
HammerSkill::SsRegen => localize(
i18n,
"hud.skill.hmr_single_strike_regen_title",
"hud.skill.hmr_single_strike_regen",
),
// Charged melee upgrades
HammerSkill::CDamage => splice_multiplier(
i18n,
"hud.skill.hmr_charged_melee_damage_title",
"hud.skill.hmr_charged_melee_damage",
modifiers.charged.scaled_damage,
),
HammerSkill::CKnockback => splice_multiplier(
i18n,
"hud.skill.hmr_charged_melee_knockback_title",
"hud.skill.hmr_charged_melee_knockback",
modifiers.charged.scaled_knockback,
),
HammerSkill::CDrain => splice_multiplier(
i18n,
"hud.skill.hmr_charged_melee_nrg_drain_title",
"hud.skill.hmr_charged_melee_nrg_drain",
modifiers.charged.energy_drain,
),
HammerSkill::CSpeed => splice_multiplier(
i18n,
"hud.skill.hmr_charged_rate_title",
"hud.skill.hmr_charged_rate",
modifiers.charged.charge_rate,
),
// Leap upgrades
HammerSkill::UnlockLeap => localize(
i18n,
"hud.skill.hmr_unlock_leap_title",
"hud.skill.hmr_unlock_leap",
),
HammerSkill::LDamage => splice_multiplier(
i18n,
"hud.skill.hmr_leap_damage_title",
"hud.skill.hmr_leap_damage",
modifiers.leap.base_damage,
),
HammerSkill::LCost => splice_multiplier(
i18n,
"hud.skill.hmr_leap_cost_title",
"hud.skill.hmr_leap_cost",
modifiers.leap.energy_cost,
),
HammerSkill::LDistance => splice_multiplier(
i18n,
"hud.skill.hmr_leap_distance_title",
"hud.skill.hmr_leap_distance",
modifiers.leap.leap_strength,
),
HammerSkill::LKnockback => splice_multiplier(
i18n,
"hud.skill.hmr_leap_knockback_title",
"hud.skill.hmr_leap_knockback",
modifiers.leap.knockback,
),
HammerSkill::LRange => splice_multiplier(
i18n,
"hud.skill.hmr_leap_radius_title",
"hud.skill.hmr_leap_radius",
modifiers.leap.range,
),
}
}
fn bow_skill_strings(skill: BowSkill, i18n: &Localization) -> (&str, Cow<str>) {
let modifiers = SKILL_MODIFIERS.bow_tree;
match skill {
// Passives
BowSkill::ProjSpeed => splice_multiplier(
i18n,
"hud.skill.bow_projectile_speed_title",
"hud.skill.bow_projectile_speed",
modifiers.universal.projectile_speed,
),
// Charged upgrades
BowSkill::CDamage => splice_multiplier(
i18n,
"hud.skill.bow_charged_damage_title",
"hud.skill.bow_charged_damage",
modifiers.charged.damage_scaling,
),
BowSkill::CRegen => splice_multiplier(
i18n,
"hud.skill.bow_charged_energy_regen_title",
"hud.skill.bow_charged_energy_regen",
modifiers.charged.regen_scaling,
),
BowSkill::CKnockback => splice_multiplier(
i18n,
"hud.skill.bow_charged_knockback_title",
"hud.skill.bow_charged_knockback",
modifiers.charged.knockback_scaling,
),
BowSkill::CSpeed => splice_multiplier(
i18n,
"hud.skill.bow_charged_speed_title",
"hud.skill.bow_charged_speed",
modifiers.charged.charge_rate,
),
BowSkill::CMove => splice_multiplier(
i18n,
"hud.skill.bow_charged_move_title",
"hud.skill.bow_charged_move",
modifiers.charged.move_speed,
),
// Repeater upgrades
BowSkill::RDamage => splice_multiplier(
i18n,
"hud.skill.bow_repeater_damage_title",
"hud.skill.bow_repeater_damage",
modifiers.repeater.power,
),
BowSkill::RCost => splice_multiplier(
i18n,
"hud.skill.bow_repeater_cost_title",
"hud.skill.bow_repeater_cost",
modifiers.repeater.energy_cost,
),
BowSkill::RSpeed => splice_multiplier(
i18n,
"hud.skill.bow_repeater_speed_title",
"hud.skill.bow_repeater_speed",
modifiers.repeater.max_speed,
),
// Shotgun upgrades
BowSkill::UnlockShotgun => localize(
i18n,
"hud.skill.bow_shotgun_unlock_title",
"hud.skill.bow_shotgun_unlock",
),
BowSkill::SDamage => splice_multiplier(
i18n,
"hud.skill.bow_shotgun_damage_title",
"hud.skill.bow_shotgun_damage",
modifiers.shotgun.power,
),
BowSkill::SCost => splice_multiplier(
i18n,
"hud.skill.bow_shotgun_cost_title",
"hud.skill.bow_shotgun_cost",
modifiers.shotgun.energy_cost,
),
BowSkill::SArrows => splice_constant(
i18n,
"hud.skill.bow_shotgun_arrow_count_title",
"hud.skill.bow_shotgun_arrow_count",
modifiers.shotgun.num_projectiles,
),
BowSkill::SSpread => splice_multiplier(
i18n,
"hud.skill.bow_shotgun_spread_title",
"hud.skill.bow_shotgun_spread",
modifiers.shotgun.spread,
),
}
}
fn staff_skill_strings(skill: StaffSkill, i18n: &Localization) -> (&str, Cow<str>) {
let modifiers = SKILL_MODIFIERS.staff_tree;
match skill {
// Basic ranged upgrades
StaffSkill::BDamage => splice_multiplier(
i18n,
"hud.skill.st_damage_title",
"hud.skill.st_damage",
modifiers.fireball.power,
),
StaffSkill::BRegen => splice_multiplier(
i18n,
"hud.skill.st_energy_regen_title",
"hud.skill.st_energy_regen",
modifiers.fireball.regen,
),
StaffSkill::BRadius => splice_multiplier(
i18n,
"hud.skill.st_explosion_radius_title",
"hud.skill.st_explosion_radius",
modifiers.fireball.range,
),
// Flamethrower upgrades
StaffSkill::FDamage => splice_multiplier(
i18n,
"hud.skill.st_flamethrower_damage_title",
"hud.skill.st_flamethrower_damage",
modifiers.flamethrower.damage,
),
StaffSkill::FRange => splice_multiplier(
i18n,
"hud.skill.st_flamethrower_range_title",
"hud.skill.st_flamethrower_range",
modifiers.flamethrower.range,
),
StaffSkill::FDrain => splice_multiplier(
i18n,
"hud.skill.st_energy_drain_title",
"hud.skill.st_energy_drain",
modifiers.flamethrower.energy_drain,
),
StaffSkill::FVelocity => splice_multiplier(
i18n,
"hud.skill.st_flame_velocity_title",
"hud.skill.st_flame_velocity",
modifiers.flamethrower.velocity,
),
// Shockwave upgrades
StaffSkill::UnlockShockwave => localize(
i18n,
"hud.skill.st_shockwave_unlock_title",
"hud.skill.st_shockwave_unlock",
),
StaffSkill::SDamage => splice_multiplier(
i18n,
"hud.skill.st_shockwave_damage_title",
"hud.skill.st_shockwave_damage",
modifiers.shockwave.damage,
),
StaffSkill::SKnockback => splice_multiplier(
i18n,
"hud.skill.st_shockwave_knockback_title",
"hud.skill.st_shockwave_knockback",
modifiers.shockwave.knockback,
),
StaffSkill::SRange => splice_multiplier(
i18n,
"hud.skill.st_shockwave_range_title",
"hud.skill.st_shockwave_range",
modifiers.shockwave.duration,
),
StaffSkill::SCost => splice_multiplier(
i18n,
"hud.skill.st_shockwave_cost_title",
"hud.skill.st_shockwave_cost",
modifiers.shockwave.energy_cost,
),
}
}
fn sceptre_skill_strings(skill: SceptreSkill, i18n: &Localization) -> (&str, Cow<str>) {
let modifiers = SKILL_MODIFIERS.sceptre_tree;
match skill {
// Lifesteal beam upgrades
SceptreSkill::LDamage => splice_multiplier(
i18n,
"hud.skill.sc_lifesteal_damage_title",
"hud.skill.sc_lifesteal_damage",
modifiers.beam.damage,
),
SceptreSkill::LRange => splice_multiplier(
i18n,
"hud.skill.sc_lifesteal_range_title",
"hud.skill.sc_lifesteal_range",
modifiers.beam.range,
),
SceptreSkill::LLifesteal => splice_multiplier(
i18n,
"hud.skill.sc_lifesteal_lifesteal_title",
"hud.skill.sc_lifesteal_lifesteal",
modifiers.beam.lifesteal,
),
SceptreSkill::LRegen => splice_multiplier(
i18n,
"hud.skill.sc_lifesteal_regen_title",
"hud.skill.sc_lifesteal_regen",
modifiers.beam.energy_regen,
),
// Healing aura upgrades
SceptreSkill::HHeal => splice_multiplier(
i18n,
"hud.skill.sc_heal_heal_title",
"hud.skill.sc_heal_heal",
modifiers.healing_aura.strength,
),
SceptreSkill::HRange => splice_multiplier(
i18n,
"hud.skill.sc_heal_range_title",
"hud.skill.sc_heal_range",
modifiers.healing_aura.range,
),
SceptreSkill::HDuration => splice_multiplier(
i18n,
"hud.skill.sc_heal_duration_title",
"hud.skill.sc_heal_duration",
modifiers.healing_aura.duration,
),
SceptreSkill::HCost => splice_multiplier(
i18n,
"hud.skill.sc_heal_cost_title",
"hud.skill.sc_heal_cost",
modifiers.healing_aura.energy_cost,
),
// Warding aura upgrades
SceptreSkill::UnlockAura => localize(
i18n,
"hud.skill.sc_wardaura_unlock_title",
"hud.skill.sc_wardaura_unlock",
),
SceptreSkill::AStrength => splice_multiplier(
i18n,
"hud.skill.sc_wardaura_strength_title",
"hud.skill.sc_wardaura_strength",
modifiers.warding_aura.strength,
),
SceptreSkill::ADuration => splice_multiplier(
i18n,
"hud.skill.sc_wardaura_duration_title",
"hud.skill.sc_wardaura_duration",
modifiers.warding_aura.duration,
),
SceptreSkill::ARange => splice_multiplier(
i18n,
"hud.skill.sc_wardaura_range_title",
"hud.skill.sc_wardaura_range",
modifiers.warding_aura.range,
),
SceptreSkill::ACost => splice_multiplier(
i18n,
"hud.skill.sc_wardaura_cost_title",
"hud.skill.sc_wardaura_cost",
modifiers.warding_aura.energy_cost,
),
}
}
fn roll_skill_strings(skill: RollSkill, i18n: &Localization) -> (&str, Cow<str>) {
let modifiers = SKILL_MODIFIERS.general_tree.roll;
match skill {
RollSkill::Cost => splice_multiplier(
i18n,
"hud.skill.roll_energy_title",
"hud.skill.roll_energy",
modifiers.energy_cost,
),
RollSkill::Strength => splice_multiplier(
i18n,
"hud.skill.roll_speed_title",
"hud.skill.roll_speed",
modifiers.strength,
),
RollSkill::Duration => splice_multiplier(
i18n,
"hud.skill.roll_dur_title",
"hud.skill.roll_dur",
modifiers.duration,
),
}
}
fn climb_skill_strings(skill: ClimbSkill, i18n: &Localization) -> (&str, Cow<str>) {
let modifiers = SKILL_MODIFIERS.general_tree.climb;
match skill {
ClimbSkill::Cost => splice_multiplier(
i18n,
"hud.skill.climbing_cost_title",
"hud.skill.climbing_cost",
modifiers.energy_cost,
),
ClimbSkill::Speed => splice_multiplier(
i18n,
"hud.skill.climbing_speed_title",
"hud.skill.climbing_speed",
modifiers.speed,
),
}
}
fn swim_skill_strings(skill: SwimSkill, i18n: &Localization) -> (&str, Cow<str>) {
let modifiers = SKILL_MODIFIERS.general_tree.swim;
match skill {
SwimSkill::Speed => splice_multiplier(
i18n,
"hud.skill.swim_speed_title",
"hud.skill.swim_speed",
modifiers.speed,
),
}
}
fn mining_skill_strings(skill: MiningSkill, i18n: &Localization) -> (&str, Cow<str>) {
let modifiers = SKILL_MODIFIERS.mining_tree;
match skill {
MiningSkill::Speed => splice_multiplier(
i18n,
"hud.skill.pick_strike_speed_title",
"hud.skill.pick_strike_speed",
modifiers.speed,
),
MiningSkill::OreGain => splice_multiplier(
i18n,
"hud.skill.pick_strike_oregain_title",
"hud.skill.pick_strike_oregain",
modifiers.ore_gain,
),
MiningSkill::GemGain => splice_multiplier(
i18n,
"hud.skill.pick_strike_gemgain_title",
"hud.skill.pick_strike_gemgain",
modifiers.gem_gain,
),
}
}
/// Helper function which takes title i18n key and description i18n key
/// and returns localized title and localized description replacing "{boost}"
/// placeholder with passed constant.
// TODO: do something better when we get to multimodifier skills
fn splice_constant<'loc>(
i18n: &'loc Localization,
title: &'loc str,
desc: &str,
constant: u32,
) -> (&'loc str, Cow<'loc, str>) {
let title = i18n.get(title);
let desc = i18n.get(desc);
let desc = desc.replace("{boost}", &constant.to_string());
(title, Cow::Owned(desc))
}
/// Helper function which takes title i18n key and description i18n key
/// and returns localized title and localized description replacing "{boost}"
/// placeholder with absolute value of percentage effect of multiplier.
// TODO: do something better when we get to multimodifier skills
fn splice_multiplier<'loc>(
i18n: &'loc Localization,
title: &'loc str,
desc: &str,
multipler: f32,
) -> (&'loc str, Cow<'loc, str>) {
let percentage = hud::multiplier_to_percentage(multipler).unsigned_abs();
splice_constant(i18n, title, desc, percentage)
}
// Small helper function to get localized skill text.
fn localize<'loc>(
i18n: &'loc Localization,
title: &'loc str,
desc: &'loc str,
) -> (&'loc str, Cow<'loc, str>) {
let title = i18n.get(title);
let desc = i18n.get(desc);
(title, Cow::Borrowed(desc))
}

View File

@ -4132,3 +4132,14 @@ pub fn angle_of_attack_text(
"Angle of Attack: Not moving".to_owned()
}
}
/// Converts multiplier to percentage.
///
/// # Examples
/// ```
/// use veloren_voxygen::hud::multiplier_to_percentage;
///
/// assert_eq!(multiplier_to_percentage(1.05), 5);
/// assert_eq!(multiplier_to_percentage(0.85), -15);
/// ```
pub fn multiplier_to_percentage(value: f32) -> i32 { (value * 100.0 - 100.0).round() as i32 }