use crate::comp::{ biped_large, golem, item::{tool::AbilityMap, Item, ItemKind}, Alignment, Body, CharacterAbility, ItemConfig, Loadout, }; use rand::Rng; /// 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(Some(LoadoutBuilder::default_item_config_from_str( /// "common.items.weapons.sword.zweihander_sword_0" /// ))) /// .build(); /// ``` pub struct LoadoutBuilder(Loadout); 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, glider: 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(Item::new_from_asset_expect( "common.items.armor.starter.rugged_chest", ))) .pants(Some(Item::new_from_asset_expect( "common.items.armor.starter.rugged_pants", ))) .foot(Some(Item::new_from_asset_expect( "common.items.armor.starter.sandals_0", ))) .lantern(Some(Item::new_from_asset_expect( "common.items.armor.starter.lantern", ))) .glider(Some(Item::new_from_asset_expect( "common.items.armor.starter.glider", ))) } /// Builds loadout of creature when spawned #[allow(clippy::single_match)] pub fn build_loadout( body: Body, alignment: Alignment, mut main_tool: Option, is_giant: bool, map: &AbilityMap, ) -> Self { match body { Body::Golem(golem) => match golem.species { golem::Species::StoneGolem => { main_tool = Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.stone_golems_fist", )); }, _ => {}, }, Body::BipedLarge(biped_large) => match (biped_large.species, biped_large.body_type) { (biped_large::Species::Occultsaurok, _) => { main_tool = Some(Item::new_from_asset_expect( "common.items.npc_weapons.staff.saurok_staff", )); }, (biped_large::Species::Mightysaurok, _) => { main_tool = Some(Item::new_from_asset_expect( "common.items.npc_weapons.sword.saurok_sword", )); }, (biped_large::Species::Slysaurok, _) => { main_tool = Some(Item::new_from_asset_expect( "common.items.npc_weapons.bow.saurok_bow", )); }, (biped_large::Species::Ogre, biped_large::BodyType::Male) => { main_tool = Some(Item::new_from_asset_expect( "common.items.npc_weapons.hammer.ogre_hammer", )); }, (biped_large::Species::Ogre, biped_large::BodyType::Female) => { main_tool = Some(Item::new_from_asset_expect( "common.items.npc_weapons.staff.ogre_staff", )); }, (biped_large::Species::Troll, _) => { main_tool = Some(Item::new_from_asset_expect( "common.items.npc_weapons.hammer.troll_hammer", )); }, (biped_large::Species::Wendigo, _) => { main_tool = Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.beast_claws", )); }, (biped_large::Species::Werewolf, _) => { main_tool = Some(Item::new_from_asset_expect( "common.items.npc_weapons.unique.beast_claws", )); }, (biped_large::Species::Cyclops, _) => { main_tool = Some(Item::new_from_asset_expect( "common.items.npc_weapons.hammer.cyclops_hammer", )); }, (biped_large::Species::Dullahan, _) => { main_tool = Some(Item::new_from_asset_expect( "common.items.npc_weapons.sword.dullahan_sword", )); }, }, Body::Humanoid(_) => { if is_giant { main_tool = Some(Item::new_from_asset_expect( "common.items.npc_weapons.sword.zweihander_sword_0", )); } }, _ => {}, }; let active_item = if let Some(ItemKind::Tool(_)) = main_tool.as_ref().map(|i| i.kind()) { main_tool.map(|item| ItemConfig::from((item, map))) } else { Some(ItemConfig { // We need the empty item so npcs can attack item: Item::new_from_asset_expect("common.items.weapons.empty.empty"), ability1: Some(CharacterAbility::default()), ability2: None, ability3: None, block_ability: None, dodge_ability: None, }) }; let loadout = match body { Body::Humanoid(_) => match alignment { Alignment::Npc => { if is_giant { Loadout { active_item, second_item: None, shoulder: Some(Item::new_from_asset_expect( "common.items.armor.shoulder.plate_0", )), chest: Some(Item::new_from_asset_expect(match alignment { Alignment::Enemy => "common.items.npc_armor.chest.plate_red_0", _ => "common.items.npc_armor.chest.plate_green_0", })), belt: Some(Item::new_from_asset_expect( "common.items.armor.belt.plate_0", )), hand: Some(Item::new_from_asset_expect( "common.items.armor.hand.plate_0", )), pants: Some(Item::new_from_asset_expect(match alignment { Alignment::Enemy => "common.items.npc_armor.pants.plate_red_0", _ => "common.items.npc_armor.pants.plate_green_0", })), foot: Some(Item::new_from_asset_expect( "common.items.armor.foot.plate_0", )), back: None, ring: None, neck: None, lantern: Some(Item::new_from_asset_expect( "common.items.lantern.black_0", )), glider: None, head: None, tabard: None, } } else { Loadout { active_item, second_item: None, shoulder: None, chest: Some(Item::new_from_asset_expect( match rand::thread_rng().gen_range(0, 10) { 0 => "common.items.armor.chest.worker_green_0", 1 => "common.items.armor.chest.worker_green_1", 2 => "common.items.armor.chest.worker_red_0", 3 => "common.items.armor.chest.worker_red_1", 4 => "common.items.armor.chest.worker_purple_0", 5 => "common.items.armor.chest.worker_purple_1", 6 => "common.items.armor.chest.worker_yellow_0", 7 => "common.items.armor.chest.worker_yellow_1", 8 => "common.items.armor.chest.worker_orange_0", _ => "common.items.armor.chest.worker_orange_1", }, )), belt: Some(Item::new_from_asset_expect( "common.items.armor.belt.leather_0", )), hand: None, pants: Some(Item::new_from_asset_expect( "common.items.armor.pants.worker_blue_0", )), foot: Some(Item::new_from_asset_expect( match rand::thread_rng().gen_range(0, 2) { 0 => "common.items.armor.foot.leather_0", _ => "common.items.armor.starter.sandals_0", }, )), back: None, ring: None, neck: None, lantern: Some(Item::new_from_asset_expect( "common.items.lantern.black_0", )), glider: None, head: None, tabard: None, } } }, Alignment::Enemy => Loadout { active_item, second_item: None, shoulder: Some(Item::new_from_asset_expect( "common.items.armor.shoulder.cultist_shoulder_purple", )), chest: Some(Item::new_from_asset_expect( "common.items.armor.chest.cultist_chest_purple", )), belt: Some(Item::new_from_asset_expect( "common.items.armor.belt.cultist_belt", )), hand: Some(Item::new_from_asset_expect( "common.items.armor.hand.cultist_hands_purple", )), pants: Some(Item::new_from_asset_expect( "common.items.armor.pants.cultist_legs_purple", )), foot: Some(Item::new_from_asset_expect( "common.items.armor.foot.cultist_boots", )), back: Some(Item::new_from_asset_expect( "common.items.armor.back.dungeon_purple-0", )), ring: None, neck: None, lantern: match rand::thread_rng().gen_range(0, 3) { 0 => Some(Item::new_from_asset_expect("common.items.lantern.black_0")), _ => None, }, glider: None, head: None, tabard: None, }, _ => LoadoutBuilder::animal(body).build(), }, Body::Golem(golem) => match golem.species { golem::Species::StoneGolem => Loadout { active_item, second_item: None, shoulder: None, chest: None, belt: None, hand: None, pants: None, foot: None, back: None, ring: None, neck: None, lantern: None, glider: None, head: None, tabard: None, }, _ => LoadoutBuilder::animal(body).build(), }, Body::BipedLarge(_) => Loadout { active_item, second_item: None, shoulder: None, chest: None, belt: None, hand: None, pants: None, foot: None, back: None, ring: None, neck: None, lantern: None, glider: None, head: None, tabard: None, }, _ => LoadoutBuilder::animal(body).build(), }; Self(loadout) } /// Default animal configuration pub fn animal(body: Body) -> Self { Self(Loadout { active_item: Some(ItemConfig { item: Item::new_from_asset_expect("common.items.weapons.empty.empty"), ability1: Some(CharacterAbility::BasicMelee { energy_cost: 10, buildup_duration: 500, swing_duration: 100, recover_duration: 100, base_damage: body.base_dmg(), knockback: 0.0, range: body.base_range(), max_angle: 20.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, glider: 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(item: Item, map: &AbilityMap) -> ItemConfig { ItemConfig::from((item, map)) } /// 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: &str, map: &AbilityMap) -> ItemConfig { Self::default_item_config_from_item(Item::new_from_asset_expect(item_ref), map) } 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 glider(mut self, item: Option) -> Self { self.0.glider = 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 } }