Address UX issues of contextual abilities.

This commit is contained in:
Sam 2022-10-22 19:53:13 -04:00
parent e1f76e0127
commit 1782408676
5 changed files with 64 additions and 23 deletions

View File

@ -75,4 +75,6 @@ common-abilities-sword-reaching_charge = Charge
common-abilities-sword-reaching_flurry = Lunging Flurry common-abilities-sword-reaching_flurry = Lunging Flurry
.desc = Make multiple rapid thrusts at your foe. .desc = Make multiple rapid thrusts at your foe.
common-abilities-sword-reaching_skewer = Skewer common-abilities-sword-reaching_skewer = Skewer
.desc = Lunge forward with enough force to pierce multiple foes. .desc = Lunge forward with enough force to pierce multiple foes.
veloren-core-pseudo_abilities-sword-stance_ability = Sword Stance Ability
.desc = When in a sword stance, this ability will have different effects.

View File

@ -5,7 +5,7 @@ use crate::{
self, aura, beam, buff, self, aura, beam, buff,
inventory::{ inventory::{
item::{ item::{
tool::{AbilityContext, AbilityItem, Stats, ToolKind}, tool::{AbilityContext, AbilityItem, AuxiliaryAbilityKind, Stats, ToolKind},
ItemKind, ItemKind,
}, },
slot::EquipSlot, slot::EquipSlot,
@ -198,22 +198,25 @@ impl ActiveAbilities {
} }
} }
pub fn iter_unlocked_abilities<'a>( pub fn iter_available_abilities<'a>(
inv: Option<&'a Inventory>, inv: Option<&'a Inventory>,
skill_set: Option<&'a SkillSet>, skill_set: Option<&'a SkillSet>,
equip_slot: EquipSlot, equip_slot: EquipSlot,
context: Option<AbilityContext>,
) -> impl Iterator<Item = usize> + 'a { ) -> impl Iterator<Item = usize> + 'a {
inv.and_then(|inv| inv.equipped(equip_slot)) inv.and_then(|inv| inv.equipped(equip_slot))
.into_iter() .into_iter()
.flat_map(|i| &i.item_config_expect().abilities.abilities) .flat_map(|i| &i.item_config_expect().abilities.abilities)
.enumerate() .enumerate()
.filter_map(move |(i, a)| { .filter_map(move |(i, a)| match a {
a.ability(context).and_then(|(skill, _)| { AuxiliaryAbilityKind::Simple(skill, _) => skill
skill .map_or(true, |s| skill_set.map_or(false, |ss| ss.has_skill(s)))
.map_or(true, |s| skill_set.map_or(false, |ss| ss.has_skill(s))) .then_some(i),
.then_some(i) AuxiliaryAbilityKind::Contextualized(abilities) => abilities
}) .values()
.any(|(skill, _)| {
skill.map_or(true, |s| skill_set.map_or(false, |ss| ss.has_skill(s)))
})
.then_some(i),
}) })
} }
@ -221,13 +224,12 @@ impl ActiveAbilities {
inv: Option<&'a Inventory>, inv: Option<&'a Inventory>,
skill_set: Option<&'a SkillSet>, skill_set: Option<&'a SkillSet>,
) -> [AuxiliaryAbility; MAX_ABILITIES] { ) -> [AuxiliaryAbility; MAX_ABILITIES] {
let mut iter = let mut iter = Self::iter_available_abilities(inv, skill_set, EquipSlot::ActiveMainhand)
Self::iter_unlocked_abilities(inv, skill_set, EquipSlot::ActiveMainhand, None) .map(AuxiliaryAbility::MainWeapon)
.map(AuxiliaryAbility::MainWeapon) .chain(
.chain( Self::iter_available_abilities(inv, skill_set, EquipSlot::ActiveOffhand)
Self::iter_unlocked_abilities(inv, skill_set, EquipSlot::ActiveOffhand, None) .map(AuxiliaryAbility::OffWeapon),
.map(AuxiliaryAbility::OffWeapon), );
);
[(); MAX_ABILITIES].map(|()| iter.next().unwrap_or(AuxiliaryAbility::Empty)) [(); MAX_ABILITIES].map(|()| iter.next().unwrap_or(AuxiliaryAbility::Empty))
} }
@ -263,6 +265,28 @@ impl Ability {
.map(|i| &i.item_config_expect().abilities) .map(|i| &i.item_config_expect().abilities)
}; };
let contextual_id = |auxiliary_kind: Option<&AuxiliaryAbilityKind<_>>, equip_slot| {
matches!(
auxiliary_kind,
Some(AuxiliaryAbilityKind::Contextualized(_))
)
.then_some(
match inv.and_then(|inv| inv.equipped(equip_slot)).and_then(|i| {
if let ItemKind::Tool(tool) = &*i.kind() {
Some(tool.kind)
} else {
None
}
}) {
Some(ToolKind::Sword) => {
Some("veloren.core.pseudo_abilities.sword.stance_ability")
},
_ => None,
},
)
.flatten()
};
match self { match self {
Ability::ToolPrimary => ability_set(EquipSlot::ActiveMainhand) Ability::ToolPrimary => ability_set(EquipSlot::ActiveMainhand)
.map(|abilities| abilities.primary.id.as_str()), .map(|abilities| abilities.primary.id.as_str()),
@ -278,6 +302,10 @@ impl Ability {
abilities abilities
.auxiliary(index, context) .auxiliary(index, context)
.map(|(_, ability)| ability.id.as_str()) .map(|(_, ability)| ability.id.as_str())
.or_else(|| contextual_id(
abilities.abilities.get(index),
EquipSlot::ActiveMainhand,
))
}) })
}, },
Ability::OffWeaponAux(index) => { Ability::OffWeaponAux(index) => {
@ -285,6 +313,10 @@ impl Ability {
abilities abilities
.auxiliary(index, context) .auxiliary(index, context)
.map(|(_, ability)| ability.id.as_str()) .map(|(_, ability)| ability.id.as_str())
.or_else(|| contextual_id(
abilities.abilities.get(index),
EquipSlot::ActiveOffhand,
))
}) })
}, },
Ability::Empty => None, Ability::Empty => None,

View File

@ -106,7 +106,7 @@ pub struct Data {
} }
pub const STANCE_ENTER_TIME: Duration = Duration::from_millis(250); pub const STANCE_ENTER_TIME: Duration = Duration::from_millis(250);
pub const STANCE_LEAVE_TIME: Duration = Duration::from_secs(3); pub const STANCE_LEAVE_TIME: Duration = Duration::from_secs(20);
impl CharacterBehavior for Data { impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate { fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
@ -382,7 +382,7 @@ impl Data {
} }
fn next_strike(update: &mut StateUpdate) { fn next_strike(update: &mut StateUpdate) {
if let CharacterState::ComboMelee2(c) = &mut update.character { let revert_to_wield = if let CharacterState::ComboMelee2(c) = &mut update.character {
if update if update
.energy .energy
.try_change_by(-c.static_data.energy_cost_per_strike) .try_change_by(-c.static_data.energy_cost_per_strike)
@ -392,6 +392,14 @@ fn next_strike(update: &mut StateUpdate) {
c.start_next_strike = false; c.start_next_strike = false;
c.timer = Duration::default(); c.timer = Duration::default();
c.stage_section = Some(StageSection::Buildup); c.stage_section = Some(StageSection::Buildup);
false
} else {
true
} }
} else {
false
};
if revert_to_wield {
update.character = CharacterState::Wielding(wielding::Data { is_sneaking: false });
} }
} }

View File

@ -902,11 +902,10 @@ impl<'a> Widget for Diary<'a> {
} }
}); });
let main_weap_abilities = ActiveAbilities::iter_unlocked_abilities( let main_weap_abilities = ActiveAbilities::iter_available_abilities(
Some(self.inventory), Some(self.inventory),
Some(self.skill_set), Some(self.skill_set),
EquipSlot::ActiveMainhand, EquipSlot::ActiveMainhand,
self.context,
) )
.map(AuxiliaryAbility::MainWeapon) .map(AuxiliaryAbility::MainWeapon)
.map(|a| { .map(|a| {
@ -915,11 +914,10 @@ impl<'a> Widget for Diary<'a> {
a, a,
) )
}); });
let off_weap_abilities = ActiveAbilities::iter_unlocked_abilities( let off_weap_abilities = ActiveAbilities::iter_available_abilities(
Some(self.inventory), Some(self.inventory),
Some(self.skill_set), Some(self.skill_set),
EquipSlot::ActiveOffhand, EquipSlot::ActiveOffhand,
self.context,
) )
.map(AuxiliaryAbility::OffWeapon) .map(AuxiliaryAbility::OffWeapon)
.map(|a| { .map(|a| {

View File

@ -368,6 +368,7 @@ pub fn ability_image(imgs: &img_ids::Imgs, ability_id: &str) -> image::Id {
"common.abilities.sword.reaching_charge" => imgs.sword_reaching_charge, "common.abilities.sword.reaching_charge" => imgs.sword_reaching_charge,
"common.abilities.sword.reaching_flurry" => imgs.sword_reaching_flurry, "common.abilities.sword.reaching_flurry" => imgs.sword_reaching_flurry,
"common.abilities.sword.reaching_skewer" => imgs.sword_reaching_skewer, "common.abilities.sword.reaching_skewer" => imgs.sword_reaching_skewer,
"veloren.core.pseudo_abilities.sword.stance_ability" => imgs.sword,
// Axe // Axe
"common.abilities.axe.doublestrike" => imgs.twohaxe_m1, "common.abilities.axe.doublestrike" => imgs.twohaxe_m1,
"common.abilities.axe.spin" => imgs.axespin, "common.abilities.axe.spin" => imgs.axespin,