use crate::{ assets, comp::{ item::{Item, ItemKind}, Body, CharacterAbility, ItemConfig, Loadout, }, }; use std::time::Duration; /// Builder for character Loadouts, containing weapon and armour items belonging /// to a character, along with some helper methods for loading Items and /// ItemConfig /// /// ``` /// use veloren_common::LoadoutBuilder; /// /// // Build a loadout with character starter defaults and a specific sword with default sword abilities /// let loadout = LoadoutBuilder::new() /// .defaults() /// .active_item(LoadoutBuilder::default_item_config_from_str( /// Some("common.items.weapons.sword.zweihander_sword_0"), /// )) /// .build(); /// ``` pub struct LoadoutBuilder(Loadout); impl Body { pub fn base_dmg(&self) -> u32 { match self { Body::Humanoid(_) => 8, Body::QuadrupedSmall(_) => 7, Body::QuadrupedMedium(_) => 10, Body::BirdMedium(_) => 6, Body::FishMedium(_) => 5, Body::Dragon(_) => 75, Body::BirdSmall(_) => 4, Body::FishSmall(_) => 3, Body::BipedLarge(_) => 30, Body::Object(_) => 0, Body::Golem(_) => 30, Body::Critter(_) => 6, Body::QuadrupedLow(_) => 9, } } pub fn base_range(&self) -> f32 { match self { Body::Humanoid(_) => 5.0, Body::QuadrupedSmall(_) => 4.5, Body::QuadrupedMedium(_) => 5.5, Body::BirdMedium(_) => 3.5, Body::FishMedium(_) => 3.5, Body::Dragon(_) => 12.5, Body::BirdSmall(_) => 3.0, Body::FishSmall(_) => 3.0, Body::BipedLarge(_) => 10.0, Body::Object(_) => 3.0, Body::Golem(_) => 7.5, Body::Critter(_) => 3.0, Body::QuadrupedLow(_) => 4.5, } } } impl LoadoutBuilder { #[allow(clippy::new_without_default)] // TODO: Pending review in #587 pub fn new() -> Self { Self(Loadout { active_item: None, second_item: None, shoulder: None, chest: None, belt: None, hand: None, pants: None, foot: None, back: None, ring: None, neck: None, lantern: None, head: None, tabard: None, }) } /// Set default armor items for the loadout. This may vary with game /// updates, but should be safe defaults for a new character. pub fn defaults(self) -> Self { self.chest(Some(assets::load_expect_cloned( "common.items.armor.starter.rugged_chest", ))) .pants(Some(assets::load_expect_cloned( "common.items.armor.starter.rugged_pants", ))) .foot(Some(assets::load_expect_cloned( "common.items.armor.starter.sandals_0", ))) .lantern(Some(assets::load_expect_cloned( "common.items.armor.starter.lantern", ))) } /// Default animal configuration pub fn animal(body: Body) -> Self { Self(Loadout { active_item: Some(ItemConfig { item: assets::load_expect_cloned("common.items.weapons.empty"), ability1: Some(CharacterAbility::BasicMelee { energy_cost: 10, buildup_duration: Duration::from_millis(600), recover_duration: Duration::from_millis(100), base_healthchange: -(body.base_dmg() as i32), range: body.base_range(), max_angle: 80.0, }), ability2: None, ability3: None, block_ability: None, dodge_ability: None, }), second_item: None, shoulder: None, chest: None, belt: None, hand: None, pants: None, foot: None, back: None, ring: None, neck: None, lantern: None, head: None, tabard: None, }) } /// Get the default [ItemConfig](../comp/struct.ItemConfig.html) for a tool /// (weapon). This information is required for the `active` and `second` /// weapon items in a loadout. If some customisation to the item's /// abilities or their timings is desired, you should create and provide /// the item config directly to the [active_item](#method.active_item) /// method pub fn default_item_config_from_item(maybe_item: Option) -> Option { if let Some(item) = maybe_item { if let ItemKind::Tool(tool) = item.kind { let mut abilities = tool.get_abilities(); let mut ability_drain = abilities.drain(..); return Some(ItemConfig { item, ability1: ability_drain.next(), ability2: ability_drain.next(), ability3: ability_drain.next(), block_ability: Some(CharacterAbility::BasicBlock), dodge_ability: Some(CharacterAbility::Roll), }); } } None } /// Get an [Item](../comp/struct.Item.html) by its string /// reference by loading its asset pub fn item_from_str(item_ref: Option<&str>) -> Option { item_ref.and_then(|specifier| assets::load_cloned::(&specifier).ok()) } /// Get an item's (weapon's) default /// [ItemConfig](../comp/struct.ItemConfig.html) /// by string reference. This will first attempt to load the Item, then /// the default abilities for that item via the /// [default_item_config_from_item](#method.default_item_config_from_item) /// function pub fn default_item_config_from_str(item_ref: Option<&str>) -> Option { Self::default_item_config_from_item(Self::item_from_str(item_ref)) } pub fn active_item(mut self, item: Option) -> Self { self.0.active_item = item; self } pub fn second_item(mut self, item: Option) -> Self { self.0.second_item = item; self } pub fn shoulder(mut self, item: Option) -> Self { self.0.shoulder = item; self } pub fn chest(mut self, item: Option) -> Self { self.0.chest = item; self } pub fn belt(mut self, item: Option) -> Self { self.0.belt = item; self } pub fn hand(mut self, item: Option) -> Self { self.0.hand = item; self } pub fn pants(mut self, item: Option) -> Self { self.0.pants = item; self } pub fn foot(mut self, item: Option) -> Self { self.0.foot = item; self } pub fn back(mut self, item: Option) -> Self { self.0.back = item; self } pub fn ring(mut self, item: Option) -> Self { self.0.ring = item; self } pub fn neck(mut self, item: Option) -> Self { self.0.neck = item; self } pub fn lantern(mut self, item: Option) -> Self { self.0.lantern = item; self } pub fn head(mut self, item: Option) -> Self { self.0.head = item; self } pub fn tabard(mut self, item: Option) -> Self { self.0.tabard = item; self } pub fn build(self) -> Loadout { self.0 } }