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
.desc = Make multiple rapid thrusts at your foe.
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,
inventory::{
item::{
tool::{AbilityContext, AbilityItem, Stats, ToolKind},
tool::{AbilityContext, AbilityItem, AuxiliaryAbilityKind, Stats, ToolKind},
ItemKind,
},
slot::EquipSlot,
@ -198,22 +198,25 @@ impl ActiveAbilities {
}
}
pub fn iter_unlocked_abilities<'a>(
pub fn iter_available_abilities<'a>(
inv: Option<&'a Inventory>,
skill_set: Option<&'a SkillSet>,
equip_slot: EquipSlot,
context: Option<AbilityContext>,
) -> impl Iterator<Item = usize> + 'a {
inv.and_then(|inv| inv.equipped(equip_slot))
.into_iter()
.flat_map(|i| &i.item_config_expect().abilities.abilities)
.enumerate()
.filter_map(move |(i, a)| {
a.ability(context).and_then(|(skill, _)| {
skill
.map_or(true, |s| skill_set.map_or(false, |ss| ss.has_skill(s)))
.then_some(i)
})
.filter_map(move |(i, a)| match a {
AuxiliaryAbilityKind::Simple(skill, _) => skill
.map_or(true, |s| skill_set.map_or(false, |ss| ss.has_skill(s)))
.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>,
skill_set: Option<&'a SkillSet>,
) -> [AuxiliaryAbility; MAX_ABILITIES] {
let mut iter =
Self::iter_unlocked_abilities(inv, skill_set, EquipSlot::ActiveMainhand, None)
.map(AuxiliaryAbility::MainWeapon)
.chain(
Self::iter_unlocked_abilities(inv, skill_set, EquipSlot::ActiveOffhand, None)
.map(AuxiliaryAbility::OffWeapon),
);
let mut iter = Self::iter_available_abilities(inv, skill_set, EquipSlot::ActiveMainhand)
.map(AuxiliaryAbility::MainWeapon)
.chain(
Self::iter_available_abilities(inv, skill_set, EquipSlot::ActiveOffhand)
.map(AuxiliaryAbility::OffWeapon),
);
[(); MAX_ABILITIES].map(|()| iter.next().unwrap_or(AuxiliaryAbility::Empty))
}
@ -263,6 +265,28 @@ impl Ability {
.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 {
Ability::ToolPrimary => ability_set(EquipSlot::ActiveMainhand)
.map(|abilities| abilities.primary.id.as_str()),
@ -278,6 +302,10 @@ impl Ability {
abilities
.auxiliary(index, context)
.map(|(_, ability)| ability.id.as_str())
.or_else(|| contextual_id(
abilities.abilities.get(index),
EquipSlot::ActiveMainhand,
))
})
},
Ability::OffWeaponAux(index) => {
@ -285,6 +313,10 @@ impl Ability {
abilities
.auxiliary(index, context)
.map(|(_, ability)| ability.id.as_str())
.or_else(|| contextual_id(
abilities.abilities.get(index),
EquipSlot::ActiveOffhand,
))
})
},
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_LEAVE_TIME: Duration = Duration::from_secs(3);
pub const STANCE_LEAVE_TIME: Duration = Duration::from_secs(20);
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
@ -382,7 +382,7 @@ impl Data {
}
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
.energy
.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.timer = Duration::default();
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.skill_set),
EquipSlot::ActiveMainhand,
self.context,
)
.map(AuxiliaryAbility::MainWeapon)
.map(|a| {
@ -915,11 +914,10 @@ impl<'a> Widget for Diary<'a> {
a,
)
});
let off_weap_abilities = ActiveAbilities::iter_unlocked_abilities(
let off_weap_abilities = ActiveAbilities::iter_available_abilities(
Some(self.inventory),
Some(self.skill_set),
EquipSlot::ActiveOffhand,
self.context,
)
.map(AuxiliaryAbility::OffWeapon)
.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_flurry" => imgs.sword_reaching_flurry,
"common.abilities.sword.reaching_skewer" => imgs.sword_reaching_skewer,
"veloren.core.pseudo_abilities.sword.stance_ability" => imgs.sword,
// Axe
"common.abilities.axe.doublestrike" => imgs.twohaxe_m1,
"common.abilities.axe.spin" => imgs.axespin,