diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index cb9e62e62c..98f1874095 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -39,6 +39,11 @@ use std::{borrow::Cow, convert::TryFrom, time::Duration}; use super::shockwave::ShockwaveDodgeable; pub const BASE_ABILITY_LIMIT: usize = 5; + +// FIXME: different AbilitySpec on same ToolKind share the same key +// FIXME: only really works with weapons, glider just fallback to (None, None), +// but maybe that's ok? +/// Descriptor to pick the right (auxiliary) ability set pub type AuxiliaryKey = (Option, Option); // TODO: Potentially look into storing previous ability sets for weapon @@ -131,27 +136,38 @@ impl ActiveAbilities { } } - pub fn active_auxiliary_key(inv: Option<&Inventory>) -> AuxiliaryKey { - let tool_kind = |slot| { - inv.and_then(|inv| inv.equipped(slot)) - .and_then(|item| match &*item.kind() { - ItemKind::Tool(tool) => Some(tool.kind), - _ => None, - }) - }; + pub fn active_auxiliary_key( + inv: Option<&Inventory>, + char_state: Option<&CharacterState>, + ) -> AuxiliaryKey { + let source = AbilitySource::determine(char_state); - ( - tool_kind(EquipSlot::ActiveMainhand), - tool_kind(EquipSlot::ActiveOffhand), - ) + match source { + AbilitySource::Weapons => { + let tool_kind = |slot| { + inv.and_then(|inv| inv.equipped(slot)) + .and_then(|item| match &*item.kind() { + ItemKind::Tool(tool) => Some(tool.kind), + _ => None, + }) + }; + + ( + tool_kind(EquipSlot::ActiveMainhand), + tool_kind(EquipSlot::ActiveOffhand), + ) + }, + AbilitySource::Glider => (None, None), + } } pub fn auxiliary_set( &self, inv: Option<&Inventory>, skill_set: Option<&SkillSet>, + char_state: Option<&CharacterState>, ) -> Cow> { - let aux_key = Self::active_auxiliary_key(inv); + let aux_key = Self::active_auxiliary_key(inv, char_state); self.auxiliary_sets .get(&aux_key) @@ -164,6 +180,7 @@ impl ActiveAbilities { input: AbilityInput, inventory: Option<&Inventory>, skill_set: Option<&SkillSet>, + char_state: Option<&CharacterState>, ) -> Ability { match input { AbilityInput::Guard => self.guard.into(), @@ -171,7 +188,7 @@ impl ActiveAbilities { AbilityInput::Secondary => self.secondary.into(), AbilityInput::Movement => self.movement.into(), AbilityInput::Auxiliary(index) => self - .auxiliary_set(inventory, skill_set) + .auxiliary_set(inventory, skill_set, char_state) .get(index) .copied() .map(|a| a.into()) @@ -191,7 +208,7 @@ impl ActiveAbilities { context: &AbilityContext, // bool is from_offhand ) -> Option<(CharacterAbility, bool, SpecifiedAbility)> { - let ability = self.get_ability(input, inv, Some(skill_set)); + let ability = self.get_ability(input, inv, Some(skill_set), char_state); let ability_set = |equip_slot| { inv.and_then(|inv| inv.equipped(equip_slot)) @@ -232,40 +249,48 @@ impl ActiveAbilities { }; let source = AbilitySource::determine(char_state); - match source { - AbilitySource::Glider => match ability { - Ability::ToolGuard => None, - Ability::ToolPrimary => inst_ability(EquipSlot::Glider, false), - Ability::ToolSecondary => inst_ability(EquipSlot::Glider, false), - Ability::SpeciesMovement => None, - Ability::MainWeaponAux(_) | Ability::OffWeaponAux(_) => None, - Ability::Empty => None, - }, - AbilitySource::Weapons => match ability { - Ability::ToolGuard => { + + match ability { + Ability::ToolGuard => match source { + AbilitySource::Weapons => { let equip_slot = combat::get_equip_slot_by_block_priority(inv); inst_ability(equip_slot, matches!(equip_slot, EquipSlot::ActiveOffhand)) }, - Ability::ToolPrimary => inst_ability(EquipSlot::ActiveMainhand, false), - Ability::ToolSecondary => inst_ability(EquipSlot::ActiveOffhand, true) - .or_else(|| inst_ability(EquipSlot::ActiveMainhand, false)), - Ability::SpeciesMovement => matches!(body, Some(Body::Humanoid(_))) - .then(|| CharacterAbility::default_roll(char_state)) - .map(|ability| { - ( - ability.adjusted_by_skills(skill_set, None), - false, - spec_ability(None), - ) - }), - Ability::MainWeaponAux(_) => inst_ability(EquipSlot::ActiveMainhand, false), - Ability::OffWeaponAux(_) => inst_ability(EquipSlot::ActiveOffhand, true), - Ability::Empty => None, + AbilitySource::Glider => None, }, + Ability::ToolPrimary => match source { + AbilitySource::Weapons => inst_ability(EquipSlot::ActiveMainhand, false), + AbilitySource::Glider => inst_ability(EquipSlot::Glider, false), + }, + Ability::ToolSecondary => match source { + AbilitySource::Weapons => inst_ability(EquipSlot::ActiveOffhand, true) + .or_else(|| inst_ability(EquipSlot::ActiveMainhand, false)), + AbilitySource::Glider => inst_ability(EquipSlot::Glider, false), + }, + Ability::MainWeaponAux(_) => match source { + AbilitySource::Weapons => inst_ability(EquipSlot::ActiveMainhand, false), + // TODO: add auxiliary abilities in the future? + AbilitySource::Glider => None, + }, + Ability::OffWeaponAux(_) => match source { + AbilitySource::Weapons => inst_ability(EquipSlot::ActiveOffhand, true), + // TODO: add auxiliary abilities in the future? + AbilitySource::Glider => None, + }, + Ability::Empty => None, + Ability::SpeciesMovement => matches!(body, Some(Body::Humanoid(_))) + .then(|| CharacterAbility::default_roll(char_state)) + .map(|ability| { + ( + ability.adjusted_by_skills(skill_set, None), + false, + spec_ability(None), + ) + }), } } - pub fn iter_available_abilities<'a>( + pub fn iter_available_abilities_on<'a>( inv: Option<&'a Inventory>, skill_set: Option<&'a SkillSet>, equip_slot: EquipSlot, @@ -290,15 +315,66 @@ impl ActiveAbilities { }) } + pub fn all_available_abilities( + inv: Option<&Inventory>, + skill_set: Option<&SkillSet>, + char_state: Option<&CharacterState>, + ) -> Vec { + let source = AbilitySource::determine(char_state); + + match source { + AbilitySource::Weapons => { + // Check if uses combo of two "equal" weapons + let paired = inv + .and_then(|inv| { + let a = inv.equipped(EquipSlot::ActiveMainhand)?; + let b = inv.equipped(EquipSlot::ActiveOffhand)?; + + if let (ItemKind::Tool(tool_a), ItemKind::Tool(tool_b)) = + (&*a.kind(), &*b.kind()) + { + Some((a.ability_spec(), tool_a.kind, b.ability_spec(), tool_b.kind)) + } else { + None + } + }) + .is_some_and(|(a_spec, a_kind, b_spec, b_kind)| { + (a_spec, a_kind) == (b_spec, b_kind) + }); + + // If equal, just take the first + if paired { + Self::iter_available_abilities_on(inv, skill_set, EquipSlot::ActiveMainhand) + .map(AuxiliaryAbility::MainWeapon) + .collect() + } else { + Self::iter_available_abilities_on(inv, skill_set, EquipSlot::ActiveMainhand) + .map(AuxiliaryAbility::MainWeapon) + .chain( + Self::iter_available_abilities_on( + inv, + skill_set, + EquipSlot::ActiveOffhand, + ) + .map(AuxiliaryAbility::OffWeapon), + ) + .collect() + } + }, + // TODO: add auxiliary abilities to gliders + AbilitySource::Glider => vec![], + } + } + fn default_ability_set<'a>( inv: Option<&'a Inventory>, skill_set: Option<&'a SkillSet>, limit: Option, ) -> Vec { - let mut iter = Self::iter_available_abilities(inv, skill_set, EquipSlot::ActiveMainhand) + let mut iter = Self::iter_available_abilities_on(inv, skill_set, EquipSlot::ActiveMainhand) .map(AuxiliaryAbility::MainWeapon) .chain( - Self::iter_available_abilities(inv, skill_set, EquipSlot::ActiveOffhand) + Self::iter_available_abilities_on(inv, skill_set, EquipSlot::ActiveOffhand) .map(AuxiliaryAbility::OffWeapon), ); @@ -406,6 +482,7 @@ impl Ability { Ability::ToolPrimary => inst_ability(EquipSlot::Glider), Ability::ToolSecondary => inst_ability(EquipSlot::Glider), Ability::SpeciesMovement => None, // TODO: Make not None + // TODO: add aux abilities to gliders in the future? Ability::MainWeaponAux(_) | Ability::OffWeaponAux(_) => None, Ability::Empty => None, }, @@ -415,8 +492,8 @@ impl Ability { inst_ability(equip_slot) }, Ability::ToolPrimary => inst_ability(EquipSlot::ActiveMainhand), - Ability::ToolSecondary => inst_ability(EquipSlot::ActiveMainhand) - .or_else(|| inst_ability(EquipSlot::ActiveOffhand)), + Ability::ToolSecondary => inst_ability(EquipSlot::ActiveOffhand) + .or_else(|| inst_ability(EquipSlot::ActiveMainhand)), Ability::SpeciesMovement => None, // TODO: Make not None Ability::MainWeaponAux(_) => inst_ability(EquipSlot::ActiveMainhand), Ability::OffWeaponAux(_) => inst_ability(EquipSlot::ActiveOffhand), @@ -504,6 +581,7 @@ impl SpecifiedAbility { Ability::ToolPrimary => inst_ability(EquipSlot::Glider), Ability::ToolSecondary => inst_ability(EquipSlot::Glider), Ability::SpeciesMovement => None, + // TODO: add aux abilities to gliders in the future? Ability::MainWeaponAux(_) | Ability::OffWeaponAux(_) => None, Ability::Empty => None, }, @@ -524,14 +602,13 @@ impl SpecifiedAbility { #[derive(Copy, Clone, Serialize, Deserialize, Debug)] pub enum PrimaryAbility { Tool, - Glider, Empty, } impl From for Ability { fn from(primary: PrimaryAbility) -> Self { match primary { - PrimaryAbility::Tool | PrimaryAbility::Glider => Ability::ToolPrimary, + PrimaryAbility::Tool => Ability::ToolPrimary, PrimaryAbility::Empty => Ability::Empty, } } @@ -540,14 +617,13 @@ impl From for Ability { #[derive(Copy, Clone, Serialize, Deserialize, Debug)] pub enum SecondaryAbility { Tool, - Glider, Empty, } impl From for Ability { fn from(primary: SecondaryAbility) -> Self { match primary { - SecondaryAbility::Tool | SecondaryAbility::Glider => Ability::ToolSecondary, + SecondaryAbility::Tool => Ability::ToolSecondary, SecondaryAbility::Empty => Ability::Empty, } } diff --git a/server/agent/src/attack.rs b/server/agent/src/attack.rs index 1644b1c096..1f8010f25b 100644 --- a/server/agent/src/attack.rs +++ b/server/agent/src/attack.rs @@ -434,7 +434,8 @@ impl<'a> AgentData<'a> { agent.combat_state.int_counters[IntCounters::Tactics as usize] = tactic as u8; - let auxiliary_key = ActiveAbilities::active_auxiliary_key(Some(self.inventory)); + let auxiliary_key = + ActiveAbilities::active_auxiliary_key(Some(self.inventory), Some(self.char_state)); let set_sword_ability = |controller: &mut Controller, slot, skill| { controller.push_event(ControlEvent::ChangeAbility { slot, @@ -1219,7 +1220,8 @@ impl<'a> AgentData<'a> { agent.combat_state.int_counters[IntCounters::Tactic as usize] = tactic as u8; - let auxiliary_key = ActiveAbilities::active_auxiliary_key(Some(self.inventory)); + let auxiliary_key = + ActiveAbilities::active_auxiliary_key(Some(self.inventory), Some(self.char_state)); let set_axe_ability = |controller: &mut Controller, slot, skill| { controller.push_event(ControlEvent::ChangeAbility { slot, diff --git a/voxygen/src/hud/diary.rs b/voxygen/src/hud/diary.rs index e1dea2ae56..fd27dc68f6 100644 --- a/voxygen/src/hud/diary.rs +++ b/voxygen/src/hud/diary.rs @@ -37,7 +37,7 @@ use common::{ RollSkill, SceptreSkill, Skill, StaffSkill, SwimSkill, SwordSkill, SKILL_MODIFIERS, }, skillset::{SkillGroupKind, SkillSet}, - Body, Energy, Health, Inventory, Poise, + Body, CharacterState, Energy, Health, Inventory, Poise, }, consts::{ENERGY_PER_LEVEL, HP_PER_LEVEL}, }; @@ -212,6 +212,7 @@ pub struct Diary<'a> { skill_set: &'a SkillSet, active_abilities: &'a ActiveAbilities, inventory: &'a Inventory, + char_state: &'a CharacterState, health: &'a Health, energy: &'a Energy, poise: &'a Poise, @@ -258,6 +259,7 @@ impl<'a> Diary<'a> { skill_set: &'a SkillSet, active_abilities: &'a ActiveAbilities, inventory: &'a Inventory, + char_state: &'a CharacterState, health: &'a Health, energy: &'a Energy, poise: &'a Poise, @@ -280,6 +282,7 @@ impl<'a> Diary<'a> { skill_set, active_abilities, inventory, + char_state, health, energy, poise, @@ -838,6 +841,7 @@ impl<'a> Widget for Diary<'a> { self.inventory, self.skill_set, self.context, + Some(self.char_state), ), image_source: self.imgs, slot_manager: Some(self.slot_manager), @@ -851,9 +855,10 @@ impl<'a> Widget for Diary<'a> { AbilityInput::Auxiliary(i), Some(self.inventory), Some(self.skill_set), + Some(self.char_state), ) .ability_id( - None, + Some(self.char_state), Some(self.inventory), Some(self.skill_set), self.context, @@ -917,61 +922,24 @@ impl<'a> Widget for Diary<'a> { .set(state.ids.active_abilities_keys[i], ui); } - let same_weap_kinds = self - .inventory - .equipped(EquipSlot::ActiveMainhand) - .zip(self.inventory.equipped(EquipSlot::ActiveOffhand)) - .map_or(false, |(a, b)| { - if let (ItemKind::Tool(tool_a), ItemKind::Tool(tool_b)) = - (&*a.kind(), &*b.kind()) - { - (a.ability_spec(), tool_a.kind) == (b.ability_spec(), tool_b.kind) - } else { - false - } - }); - - let main_weap_abilities = ActiveAbilities::iter_available_abilities( + let abilities: Vec<_> = ActiveAbilities::all_available_abilities( Some(self.inventory), Some(self.skill_set), - EquipSlot::ActiveMainhand, + Some(self.char_state), ) - .map(AuxiliaryAbility::MainWeapon) + .into_iter() .map(|a| { ( Ability::from(a).ability_id( - None, + Some(self.char_state), Some(self.inventory), Some(self.skill_set), self.context, ), a, ) - }); - let off_weap_abilities = ActiveAbilities::iter_available_abilities( - Some(self.inventory), - Some(self.skill_set), - EquipSlot::ActiveOffhand, - ) - .map(AuxiliaryAbility::OffWeapon) - .map(|a| { - ( - Ability::from(a).ability_id( - None, - Some(self.inventory), - Some(self.skill_set), - self.context, - ), - a, - ) - }); - - let abilities: Vec<_> = if same_weap_kinds { - // When the weapons have the same ability kind take only the main weapons, - main_weap_abilities.collect() - } else { - main_weap_abilities.chain(off_weap_abilities).collect() - }; + }) + .collect(); const ABILITIES_PER_PAGE: usize = 12; @@ -1072,12 +1040,27 @@ impl<'a> Widget for Diary<'a> { self.inventory, self.skill_set, self.context, + Some(self.char_state), ), image_source: self.imgs, slot_manager: Some(self.slot_manager), pulse: 0.0, }; + let same_weap_kinds = self + .inventory + .equipped(EquipSlot::ActiveMainhand) + .zip(self.inventory.equipped(EquipSlot::ActiveOffhand)) + .map_or(false, |(a, b)| { + if let (ItemKind::Tool(tool_a), ItemKind::Tool(tool_b)) = + (&*a.kind(), &*b.kind()) + { + (a.ability_spec(), tool_a.kind) == (b.ability_spec(), tool_b.kind) + } else { + false + } + }); + for (id_index, (ability_id, ability)) in abilities .iter() .skip(ability_start) diff --git a/voxygen/src/hud/hotbar.rs b/voxygen/src/hud/hotbar.rs index f3fc7bab26..e38daf893f 100644 --- a/voxygen/src/hud/hotbar.rs +++ b/voxygen/src/hud/hotbar.rs @@ -81,6 +81,10 @@ impl State { .state() .read_storage::() .get(info.viewpoint_entity), + client + .state() + .read_storage::() + .get(info.viewpoint_entity), ) .iter() .enumerate() diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs index a5d5ec6a61..dbe3002a10 100755 --- a/voxygen/src/hud/mod.rs +++ b/voxygen/src/hud/mod.rs @@ -3684,6 +3684,7 @@ impl Hud { if let ( Some(skill_set), Some(inventory), + Some(char_state), Some(health), Some(energy), Some(body), @@ -3691,6 +3692,7 @@ impl Hud { ) = ( skill_sets.get(entity), inventories.get(entity), + char_states.get(entity), healths.get(entity), energies.get(entity), bodies.get(entity), @@ -3704,6 +3706,7 @@ impl Hud { skill_set, active_abilities.get(entity).unwrap_or(&Default::default()), inventory, + char_state, health, energy, poise, @@ -4002,12 +4005,20 @@ impl Hud { let me = info.viewpoint_entity; if let Some(active_abilities) = active_abilities.get(me) { let ability_a = active_abilities - .auxiliary_set(inventories.get(me), skill_sets.get(me)) + .auxiliary_set( + inventories.get(me), + skill_sets.get(me), + char_states.get(me), + ) .get(a) .copied() .unwrap_or(AuxiliaryAbility::Empty); let ability_b = active_abilities - .auxiliary_set(inventories.get(me), skill_sets.get(me)) + .auxiliary_set( + inventories.get(me), + skill_sets.get(me), + char_states.get(me), + ) .get(b) .copied() .unwrap_or(AuxiliaryAbility::Empty); @@ -4152,12 +4163,20 @@ impl Hud { let me = info.viewpoint_entity; if let Some(active_abilities) = active_abilities.get(me) { let ability_a = active_abilities - .auxiliary_set(inventories.get(me), skill_sets.get(me)) + .auxiliary_set( + inventories.get(me), + skill_sets.get(me), + char_states.get(me), + ) .get(a) .copied() .unwrap_or(AuxiliaryAbility::Empty); let ability_b = active_abilities - .auxiliary_set(inventories.get(me), skill_sets.get(me)) + .auxiliary_set( + inventories.get(me), + skill_sets.get(me), + char_states.get(me), + ) .get(b) .copied() .unwrap_or(AuxiliaryAbility::Empty); diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index 60bdfc21dd..184c668e39 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -1028,7 +1028,7 @@ impl<'a> Skillbar<'a> { // Helper let tooltip_text = |slot| { - let (hotbar, inventory, _, skill_set, active_abilities, _, contexts, _, _, _) = + let (hotbar, inventory, _, skill_set, active_abilities, _, contexts, _, char_state, _) = content_source; hotbar.get(slot).and_then(|content| match content { hotbar::SlotContents::Inventory(i, _) => inventory.get_by_hash(i).map(|item| { @@ -1039,7 +1039,7 @@ impl<'a> Skillbar<'a> { }), hotbar::SlotContents::Ability(i) => active_abilities .and_then(|a| { - a.auxiliary_set(Some(inventory), Some(skill_set)) + a.auxiliary_set(Some(inventory), Some(skill_set), char_state) .get(i) .and_then(|a| { Ability::from(*a).ability_id( diff --git a/voxygen/src/hud/slots.rs b/voxygen/src/hud/slots.rs index 032faf39ed..743a8c2690 100644 --- a/voxygen/src/hud/slots.rs +++ b/voxygen/src/hud/slots.rs @@ -165,7 +165,7 @@ impl<'a> SlotKey, HotbarImageSource<'a>> for HotbarSlot { }, hotbar::SlotContents::Ability(i) => { let ability_id = active_abilities.and_then(|a| { - a.auxiliary_set(Some(inventory), Some(skillset)) + a.auxiliary_set(Some(inventory), Some(skillset), *char_state) .get(i) .and_then(|a| { Ability::from(*a).ability_id( @@ -246,6 +246,7 @@ type AbilitiesSource<'a> = ( &'a Inventory, &'a SkillSet, &'a AbilityContext, + Option<&'a CharacterState>, ); impl<'a> SlotKey, img_ids::Imgs> for AbilitySlot { @@ -253,7 +254,7 @@ impl<'a> SlotKey, img_ids::Imgs> for AbilitySlot { fn image_key( &self, - (active_abilities, inventory, skillset, contexts): &AbilitiesSource<'a>, + (active_abilities, inventory, skillset, contexts, char_state): &AbilitiesSource<'a>, ) -> Option<(Self::ImageKey, Option)> { let ability_id = match self { Self::Slot(index) => active_abilities @@ -261,11 +262,15 @@ impl<'a> SlotKey, img_ids::Imgs> for AbilitySlot { AbilityInput::Auxiliary(*index), Some(inventory), Some(skillset), + *char_state, ) - .ability_id(None, Some(inventory), Some(skillset), contexts), - Self::Ability(ability) => { - Ability::from(*ability).ability_id(None, Some(inventory), Some(skillset), contexts) - }, + .ability_id(*char_state, Some(inventory), Some(skillset), contexts), + Self::Ability(ability) => Ability::from(*ability).ability_id( + *char_state, + Some(inventory), + Some(skillset), + contexts, + ), }; ability_id.map(|id| (String::from(id), None))