Activated abilities can now be contextualized

This commit is contained in:
Sam 2022-10-22 14:36:41 -04:00
parent 07d273aa09
commit 8cfe62e6dc
10 changed files with 271 additions and 165 deletions

View File

@ -5,71 +5,80 @@
primary: "common.abilities.sword.balanced_combo",
secondary: "common.abilities.sword.balanced_thrust",
abilities: [
(Some(Sword(BalancedFinisher)), "common.abilities.sword.balanced_finisher"),
(Some(Sword(OffensiveCombo)), "common.abilities.sword.offensive_combo"),
(Some(Sword(OffensiveFinisher)), "common.abilities.sword.offensive_finisher"),
(Some(Sword(OffensiveAdvance)), "common.abilities.sword.offensive_advance"),
(Some(Sword(CripplingCombo)), "common.abilities.sword.crippling_combo"),
(Some(Sword(CripplingFinisher)), "common.abilities.sword.crippling_finisher"),
(Some(Sword(CripplingStrike)), "common.abilities.sword.crippling_strike"),
(Some(Sword(CripplingGouge)), "common.abilities.sword.crippling_gouge"),
(Some(Sword(CleavingCombo)), "common.abilities.sword.cleaving_combo"),
(Some(Sword(CleavingFinisher)), "common.abilities.sword.cleaving_finisher"),
(Some(Sword(CleavingSpin)), "common.abilities.sword.cleaving_spin"),
(Some(Sword(CleavingDive)), "common.abilities.sword.cleaving_dive"),
(Some(Sword(DefensiveCombo)), "common.abilities.sword.defensive_combo"),
(Some(Sword(DefensiveBulwark)), "common.abilities.sword.defensive_bulwark"),
(Some(Sword(DefensiveRetreat)), "common.abilities.sword.defensive_retreat"),
(Some(Sword(ParryingCombo)), "common.abilities.sword.parrying_combo"),
(Some(Sword(ParryingParry)), "common.abilities.sword.parrying_parry"),
(Some(Sword(ParryingRiposte)), "common.abilities.sword.parrying_riposte"),
(Some(Sword(ParryingCounter)), "common.abilities.sword.parrying_counter"),
(Some(Sword(HeavyCombo)), "common.abilities.sword.heavy_combo"),
(Some(Sword(HeavyFinisher)), "common.abilities.sword.heavy_finisher"),
(Some(Sword(HeavyPommelStrike)), "common.abilities.sword.heavy_pommelstrike"),
(Some(Sword(HeavyFortitude)), "common.abilities.sword.heavy_fortitude"),
(Some(Sword(MobilityCombo)), "common.abilities.sword.mobility_combo"),
(Some(Sword(MobilityFeint)), "common.abilities.sword.mobility_feint"),
(Some(Sword(MobilityAgility)), "common.abilities.sword.mobility_agility"),
(Some(Sword(ReachingCombo)), "common.abilities.sword.reaching_combo"),
(Some(Sword(ReachingCharge)), "common.abilities.sword.reaching_charge"),
(Some(Sword(ReachingFlurry)), "common.abilities.sword.reaching_flurry"),
(Some(Sword(ReachingSkewer)), "common.abilities.sword.reaching_skewer"),
Simple(Some(Sword(OffensiveCombo)), "common.abilities.sword.offensive_combo"),
Simple(Some(Sword(CripplingCombo)), "common.abilities.sword.crippling_combo"),
Simple(Some(Sword(CleavingCombo)), "common.abilities.sword.cleaving_combo"),
Simple(Some(Sword(DefensiveCombo)), "common.abilities.sword.defensive_combo"),
Simple(Some(Sword(ParryingCombo)), "common.abilities.sword.parrying_combo"),
Simple(Some(Sword(HeavyCombo)), "common.abilities.sword.heavy_combo"),
Simple(Some(Sword(MobilityCombo)), "common.abilities.sword.mobility_combo"),
Simple(Some(Sword(ReachingCombo)), "common.abilities.sword.reaching_combo"),
// Damagey ones
Contextualized({
Sword(Balanced): (Some(Sword(BalancedFinisher)), "common.abilities.sword.balanced_finisher"),
Sword(Offensive): (Some(Sword(OffensiveFinisher)), "common.abilities.sword.offensive_finisher"),
Sword(Crippling): (Some(Sword(CripplingFinisher)), "common.abilities.sword.crippling_finisher"),
Sword(Cleaving): (Some(Sword(CleavingFinisher)), "common.abilities.sword.cleaving_finisher"),
Sword(Parrying): (Some(Sword(ParryingCounter)), "common.abilities.sword.parrying_counter"),
Sword(Heavy): (Some(Sword(HeavyFinisher)), "common.abilities.sword.heavy_finisher"),
Sword(Reaching): (Some(Sword(ReachingFlurry)), "common.abilities.sword.reaching_flurry"),
}),
// Movementy ones
Contextualized({
Sword(Offensive): (Some(Sword(OffensiveAdvance)), "common.abilities.sword.offensive_advance"),
Sword(Crippling): (Some(Sword(CripplingStrike)), "common.abilities.sword.crippling_strike"),
Sword(Cleaving): (Some(Sword(CleavingDive)), "common.abilities.sword.cleaving_dive"),
Sword(Defensive): (Some(Sword(DefensiveRetreat)), "common.abilities.sword.defensive_retreat"),
Sword(Parrying): (Some(Sword(ParryingRiposte)), "common.abilities.sword.parrying_riposte"),
Sword(Heavy): (Some(Sword(HeavyFortitude)), "common.abilities.sword.heavy_fortitude"),
Sword(Mobility): (Some(Sword(MobilityFeint)), "common.abilities.sword.mobility_feint"),
Sword(Reaching): (Some(Sword(ReachingCharge)), "common.abilities.sword.reaching_charge"),
}),
// Utilityy ones
Contextualized({
Sword(Crippling): (Some(Sword(CripplingGouge)), "common.abilities.sword.crippling_gouge"),
Sword(Cleaving): (Some(Sword(CleavingSpin)), "common.abilities.sword.cleaving_spin"),
Sword(Defensive): (Some(Sword(DefensiveBulwark)), "common.abilities.sword.defensive_bulwark"),
Sword(Parrying): (Some(Sword(ParryingParry)), "common.abilities.sword.parrying_parry"),
Sword(Heavy): (Some(Sword(HeavyPommelStrike)), "common.abilities.sword.heavy_pommelstrike"),
Sword(Mobility): (Some(Sword(MobilityAgility)), "common.abilities.sword.mobility_agility"),
Sword(Reaching): (Some(Sword(ReachingSkewer)), "common.abilities.sword.reaching_skewer"),
}),
],
),
Tool(Axe): (
primary: "common.abilities.axe.doublestrike",
secondary: "common.abilities.axe.spin",
abilities: [
(Some(Axe(UnlockLeap)), "common.abilities.axe.leap"),
Simple(Some(Axe(UnlockLeap)), "common.abilities.axe.leap"),
],
),
Tool(Hammer): (
primary: "common.abilities.hammer.singlestrike",
secondary: "common.abilities.hammer.charged",
abilities: [
(Some(Hammer(UnlockLeap)), "common.abilities.hammer.leap"),
Simple(Some(Hammer(UnlockLeap)), "common.abilities.hammer.leap"),
],
),
Tool(Bow): (
primary: "common.abilities.bow.charged",
secondary: "common.abilities.bow.repeater",
abilities: [
(Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"),
Simple(Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"),
],
),
Tool(Staff): (
primary: "common.abilities.staff.firebomb",
secondary: "common.abilities.staff.flamethrower",
abilities: [
(Some(Staff(UnlockShockwave)), "common.abilities.staff.fireshockwave"),
Simple(Some(Staff(UnlockShockwave)), "common.abilities.staff.fireshockwave"),
],
),
Tool(Sceptre): (
primary: "common.abilities.sceptre.lifestealbeam",
secondary: "common.abilities.sceptre.healingaura",
abilities: [
(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
Simple(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
],
),
Custom("Husk"): (
@ -112,10 +121,10 @@
primary: "common.abilities.gnarling.chieftain.flamestrike",
secondary: "common.abilities.gnarling.chieftain.firebarrage",
abilities: [
(None, "common.abilities.gnarling.chieftain.fireshockwave"),
(None, "common.abilities.gnarling.chieftain.redtotem"),
(None, "common.abilities.gnarling.chieftain.greentotem"),
(None, "common.abilities.gnarling.chieftain.whitetotem"),
Simple(None, "common.abilities.gnarling.chieftain.fireshockwave"),
Simple(None, "common.abilities.gnarling.chieftain.redtotem"),
Simple(None, "common.abilities.gnarling.chieftain.greentotem"),
Simple(None, "common.abilities.gnarling.chieftain.whitetotem"),
],
),
Custom("Gnarling Totem Red"): (
@ -147,7 +156,7 @@
primary: "common.abilities.custom.woodgolem.strike",
secondary: "common.abilities.custom.woodgolem.spin",
abilities: [
(None, "common.abilities.custom.woodgolem.shockwave")
Simple(None, "common.abilities.custom.woodgolem.shockwave")
],
),
Custom("Simple Flying Melee"): (
@ -194,7 +203,7 @@
primary: "common.abilities.custom.stonegolemfist.singlestrike",
secondary: "common.abilities.custom.stonegolemfist.shockwave",
abilities: [
(None, "common.abilities.custom.stonegolemfist.spin"),
Simple(None, "common.abilities.custom.stonegolemfist.spin"),
],
),
Custom("Beast Claws"): (
@ -211,8 +220,8 @@
primary: "common.abilities.custom.tidalwarrior.pincer",
secondary: "common.abilities.custom.tidalwarrior.scuttle",
abilities: [
(None, "common.abilities.custom.tidalwarrior.bubbles"),
(None, "common.abilities.custom.tidalwarrior.totem"),
Simple(None, "common.abilities.custom.tidalwarrior.bubbles"),
Simple(None, "common.abilities.custom.tidalwarrior.totem"),
],
),
Custom("Tidal Totem"): (
@ -231,7 +240,7 @@
primary: "common.abilities.custom.quadmedjump.leap",
secondary: "common.abilities.custom.quadmedjump.doublestrike",
abilities: [
(None, "common.abilities.custom.quadmedjump.quickleap"),
Simple(None, "common.abilities.custom.quadmedjump.quickleap"),
],
),
Custom("Quad Med Charge"): (
@ -253,7 +262,7 @@
primary: "common.abilities.custom.basilisk.petrify",
secondary: "common.abilities.custom.basilisk.triplestrike",
abilities: [
(None, "common.abilities.custom.basilisk.dash"),
Simple(None, "common.abilities.custom.basilisk.dash"),
],
),
Custom("Asp"): (
@ -270,7 +279,7 @@
primary: "common.abilities.custom.quadlowbreathe.flamethrower",
secondary: "common.abilities.custom.quadlowbreathe.triplestrike",
abilities: [
(None, "common.abilities.custom.quadlowbreathe.dash"),
Simple(None, "common.abilities.custom.quadlowbreathe.dash"),
],
),
Custom("Quad Low Tail"): (
@ -292,7 +301,7 @@
primary: "common.abilities.custom.quadlowbeam.lifestealbeam",
secondary: "common.abilities.custom.quadlowbreathe.triplestrike",
abilities: [
(None, "common.abilities.custom.quadlowbreathe.dash"),
Simple(None, "common.abilities.custom.quadlowbreathe.dash"),
],
),
Custom("Quad Small Basic"): (
@ -330,28 +339,28 @@
primary: "common.abilities.custom.arthropods.blackwidow.singlestrike",
secondary: "common.abilities.custom.arthropods.blackwidow.ensnaringwebs",
abilities: [
(None, "common.abilities.custom.arthropods.blackwidow.poisonball"),
Simple(None, "common.abilities.custom.arthropods.blackwidow.poisonball"),
],
),
Custom("Horn Beetle"): (
primary: "common.abilities.custom.arthropods.hornbeetle.singlestrike",
secondary: "common.abilities.custom.arthropods.hornbeetle.harden",
abilities: [
(None, "common.abilities.custom.arthropods.hornbeetle.leap"),
Simple(None, "common.abilities.custom.arthropods.hornbeetle.leap"),
],
),
Custom("Tarantula"): (
primary: "common.abilities.custom.arthropods.tarantula.singlestrike",
secondary: "common.abilities.custom.arthropods.tarantula.ensnaringwebs",
abilities: [
(None, "common.abilities.custom.arthropods.tarantula.leap"),
Simple(None, "common.abilities.custom.arthropods.tarantula.leap"),
],
),
Custom("Weevil"): (
primary: "common.abilities.custom.arthropods.weevil.singlestrike",
secondary: "common.abilities.custom.arthropods.weevil.harden",
abilities: [
(None, "common.abilities.custom.arthropods.weevil.threadshot"),
Simple(None, "common.abilities.custom.arthropods.weevil.threadshot"),
],
),
/// TODO: Organize the rest into further catagories and give purple tier droppers+ custom skillsets
@ -374,41 +383,41 @@
primary: "common.abilities.custom.mindflayer.cursedflames",
secondary: "common.abilities.custom.mindflayer.necroticvortex",
abilities: [
(None, "common.abilities.custom.mindflayer.dimensionaldoor"),
(None, "common.abilities.custom.mindflayer.necroticsphere"),
(None, "common.abilities.custom.mindflayer.summonminions"),
Simple(None, "common.abilities.custom.mindflayer.dimensionaldoor"),
Simple(None, "common.abilities.custom.mindflayer.necroticsphere"),
Simple(None, "common.abilities.custom.mindflayer.summonminions"),
],
),
Custom("Minotaur"): (
primary: "common.abilities.custom.minotaur.cleave",
secondary: "common.abilities.custom.minotaur.cripplingstrike",
abilities: [
(None, "common.abilities.custom.minotaur.charge"),
(None, "common.abilities.custom.minotaur.frenzy"),
Simple(None, "common.abilities.custom.minotaur.charge"),
Simple(None, "common.abilities.custom.minotaur.frenzy"),
],
),
Custom("Clay Golem"): (
primary: "common.abilities.custom.claygolem.strike",
secondary: "common.abilities.custom.claygolem.laser",
abilities: [
(None, "common.abilities.custom.claygolem.shockwave"),
(None, "common.abilities.custom.claygolem.rocket"),
Simple(None, "common.abilities.custom.claygolem.shockwave"),
Simple(None, "common.abilities.custom.claygolem.rocket"),
],
),
Custom("Yeti"): (
primary: "common.abilities.custom.yeti.strike",
secondary: "common.abilities.custom.yeti.icespikes",
abilities: [
(None, "common.abilities.custom.yeti.frostbreath"),
(None, "common.abilities.custom.yeti.snowball"),
Simple(None, "common.abilities.custom.yeti.frostbreath"),
Simple(None, "common.abilities.custom.yeti.snowball"),
],
),
Custom("Harvester"): (
primary: "common.abilities.custom.harvester.scythe",
secondary: "common.abilities.custom.harvester.firebreath",
abilities: [
(None, "common.abilities.custom.harvester.ensnaringvines"),
(None, "common.abilities.custom.harvester.explodingpumpkin"),
Simple(None, "common.abilities.custom.harvester.ensnaringvines"),
Simple(None, "common.abilities.custom.harvester.explodingpumpkin"),
],
),
// TODO: Allow ability sets to expand other ability sets
@ -416,17 +425,17 @@
primary: "common.abilities.custom.dagon.dagonbombs",
secondary: "common.abilities.custom.dagon.seaurchins",
abilities: [
(None, "common.abilities.custom.dagon.steamwave"),
(None, "common.abilities.custom.cardinal.steambeam"),
(None, "common.abilities.custom.dagon.steamheal"),
Simple(None, "common.abilities.custom.dagon.steamwave"),
Simple(None, "common.abilities.custom.cardinal.steambeam"),
Simple(None, "common.abilities.custom.dagon.steamheal"),
],
),
Custom("Cardinal"): (
primary: "common.abilities.sceptre.lifestealbeam",
secondary: "common.abilities.sceptre.healingaura",
abilities: [
(None, "common.abilities.custom.cardinal.steambeam"),
(None, "common.abilities.custom.cardinal.summonseacrocs"),
Simple(None, "common.abilities.custom.cardinal.steambeam"),
Simple(None, "common.abilities.custom.cardinal.summonseacrocs"),
],
),
Custom("Oni"): (
@ -438,21 +447,21 @@
primary: "common.abilities.custom.birdlargebreathe.firebomb",
secondary: "common.abilities.custom.birdlargebreathe.triplestrike",
abilities: [
(None, "common.abilities.custom.birdlargebreathe.flamethrower"),
Simple(None, "common.abilities.custom.birdlargebreathe.flamethrower"),
],
),
Custom("Bird Large Fire"): (
primary: "common.abilities.custom.birdlargefire.firebomb",
secondary: "common.abilities.custom.birdlargefire.triplestrike",
abilities: [
(None, "common.abilities.custom.birdlargefire.fireshockwave"),
Simple(None, "common.abilities.custom.birdlargefire.fireshockwave"),
],
),
Custom("Bird Large Basic"): (
primary: "common.abilities.custom.birdlargebasic.triplestrike",
secondary: "common.abilities.custom.birdlargebasic.summontornadoes",
abilities: [
(None, "common.abilities.custom.birdlargebasic.dash"),
Simple(None, "common.abilities.custom.birdlargebasic.dash"),
],
),
Custom("Tornado"): (
@ -514,7 +523,7 @@
primary: "common.abilities.debug.forwardboost",
secondary: "common.abilities.debug.upboost",
abilities: [
(None, "common.abilities.debug.possess"),
Simple(None, "common.abilities.debug.possess"),
],
),
Tool(Farming): (

View File

@ -5,7 +5,7 @@ use crate::{
self, aura, beam, buff,
inventory::{
item::{
tool::{AbilityItem, Stats, ToolKind},
tool::{AbilityContext, AbilityItem, Stats, ToolKind},
ItemKind,
},
slot::EquipSlot,
@ -141,6 +141,7 @@ impl ActiveAbilities {
inv: Option<&Inventory>,
skill_set: &SkillSet,
body: Option<&Body>,
context: Option<AbilityContext>,
// bool is from_offhand
) -> Option<(CharacterAbility, bool)> {
let ability = self.get_ability(input, inv, Some(skill_set));
@ -160,8 +161,8 @@ impl ActiveAbilities {
ability.adjusted_by_skills(skill_set, tool_kind)
};
let unwrap_ability = |(skill_req, ability): &(Option<Skill>, AbilityItem)| {
(*skill_req, ability.ability.clone())
let unwrap_ability = |(skill_req, ability): (Option<Skill>, &AbilityItem)| {
(skill_req, ability.ability.clone())
};
let unlocked = |(s, a): (Option<Skill>, CharacterAbility)| {
@ -186,11 +187,11 @@ impl ActiveAbilities {
.then(CharacterAbility::default_roll)
.map(|ability| (ability.adjusted_by_skills(skill_set, None), false)),
Ability::MainWeaponAux(index) => ability_set(EquipSlot::ActiveMainhand)
.and_then(|abilities| abilities.abilities.get(index).map(unwrap_ability))
.and_then(|abilities| abilities.auxiliary(index, context).map(unwrap_ability))
.and_then(unlocked)
.map(|ability| (scale_ability(ability, EquipSlot::ActiveMainhand), false)),
Ability::OffWeaponAux(index) => ability_set(EquipSlot::ActiveOffhand)
.and_then(|abilities| abilities.abilities.get(index).map(unwrap_ability))
.and_then(|abilities| abilities.auxiliary(index, context).map(unwrap_ability))
.and_then(unlocked)
.map(|ability| (scale_ability(ability, EquipSlot::ActiveOffhand), true)),
Ability::Empty => None,
@ -201,15 +202,18 @@ impl ActiveAbilities {
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, (skill, _))| {
skill
.map_or(true, |s| skill_set.map_or(false, |ss| ss.has_skill(s)))
.then_some(i)
.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)
})
})
}
@ -217,12 +221,13 @@ 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)
.map(AuxiliaryAbility::MainWeapon)
.chain(
Self::iter_unlocked_abilities(inv, skill_set, EquipSlot::ActiveOffhand)
.map(AuxiliaryAbility::OffWeapon),
);
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),
);
[(); MAX_ABILITIES].map(|()| iter.next().unwrap_or(AuxiliaryAbility::Empty))
}
@ -248,7 +253,11 @@ pub enum Ability {
}
impl Ability {
pub fn ability_id(self, inv: Option<&Inventory>) -> Option<&str> {
pub fn ability_id(
self,
inv: Option<&Inventory>,
context: Option<AbilityContext>,
) -> Option<&str> {
let ability_set = |equip_slot| {
inv.and_then(|inv| inv.equipped(equip_slot))
.map(|i| &i.item_config_expect().abilities)
@ -267,16 +276,14 @@ impl Ability {
Ability::MainWeaponAux(index) => {
ability_set(EquipSlot::ActiveMainhand).and_then(|abilities| {
abilities
.abilities
.get(index)
.auxiliary(index, context)
.map(|(_, ability)| ability.id.as_str())
})
},
Ability::OffWeaponAux(index) => {
ability_set(EquipSlot::ActiveOffhand).and_then(|abilities| {
abilities
.abilities
.get(index)
.auxiliary(index, context)
.map(|(_, ability)| ability.id.as_str())
})
},
@ -1381,48 +1388,6 @@ impl CharacterAbility {
}
}
#[must_use]
pub fn contextualize(mut self, data: &JoinData) -> Self {
if let Some(AbilityKind::Sword(old_stance)) = data
.character
.ability_info()
.and_then(|info| info.ability_meta)
.and_then(|meta| meta.kind)
{
if let Some(AbilityKind::Sword(new_stance)) = self.ability_meta().kind {
let energy_reduction = if old_stance == new_stance {
0.75
} else if old_stance == SwordStance::Balanced {
1.0
} else {
1.5
};
use CharacterAbility::*;
match &mut self {
BasicMelee { energy_cost, .. }
| ComboMelee2 {
energy_cost_per_strike: energy_cost,
..
}
| FinisherMelee { energy_cost, .. }
| DashMelee { energy_cost, .. }
| SpinMelee { energy_cost, .. }
| ChargedMelee { energy_cost, .. }
| Shockwave { energy_cost, .. }
| BasicBlock { energy_cost, .. }
| SelfBuff { energy_cost, .. }
| DiveMelee { energy_cost, .. }
| RiposteMelee { energy_cost, .. }
| RapidMelee { energy_cost, .. } => {
*energy_cost *= energy_reduction;
},
_ => {},
}
}
}
self
}
#[must_use = "method returns new ability and doesn't mutate the original value"]
pub fn adjusted_by_skills(mut self, skillset: &SkillSet, tool: Option<ToolKind>) -> Self {
match tool {
@ -2596,7 +2561,7 @@ pub enum AbilityKind {
Sword(SwordStance),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)]
pub enum SwordStance {
Balanced,
Offensive,

View File

@ -3,7 +3,11 @@
use crate::{
assets::{self, Asset, AssetExt, AssetHandle},
comp::{skills::Skill, CharacterAbility},
comp::{
ability::{AbilityKind, SwordStance},
skills::Skill,
CharacterAbility, CharacterState,
},
};
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
@ -283,7 +287,68 @@ impl Tool {
pub struct AbilitySet<T> {
pub primary: T,
pub secondary: T,
pub abilities: Vec<(Option<Skill>, T)>,
pub abilities: Vec<AuxiliaryAbilityKind<T>>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum AuxiliaryAbilityKind<T> {
Simple(Option<Skill>, T),
Contextualized(HashMap<AbilityContext, (Option<Skill>, T)>),
}
impl<T> AuxiliaryAbilityKind<T> {
pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> AuxiliaryAbilityKind<U> {
match self {
Self::Simple(s, x) => AuxiliaryAbilityKind::<U>::Simple(s, f(x)),
Self::Contextualized(abilities) => AuxiliaryAbilityKind::<U>::Contextualized(
abilities
.into_iter()
.map(|(c, (s, x))| (c, (s, f(x))))
.collect(),
),
}
}
pub fn map_ref<U, F: FnMut(&T) -> U>(&self, mut f: F) -> AuxiliaryAbilityKind<U> {
match self {
Self::Simple(s, x) => AuxiliaryAbilityKind::<U>::Simple(*s, f(x)),
Self::Contextualized(abilities) => AuxiliaryAbilityKind::<U>::Contextualized(
abilities
.into_iter()
.map(|(c, (s, x))| (*c, (*s, f(x))))
.collect(),
),
}
}
pub fn ability(&self, context: Option<AbilityContext>) -> Option<(Option<Skill>, &T)> {
match self {
AuxiliaryAbilityKind::Simple(s, a) => Some((*s, a)),
AuxiliaryAbilityKind::Contextualized(abilities) => {
context.and_then(|c| abilities.get(&c).map(|(s, a)| (*s, a)))
},
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize, Copy, Eq, PartialEq, Hash)]
pub enum AbilityContext {
Sword(SwordStance),
}
impl AbilityContext {
// TODO: Come up with better name for function
pub fn yeet(char_state: Option<&CharacterState>) -> Option<Self> {
if let Some(AbilityKind::Sword(stance)) = char_state
.and_then(|cs| cs.ability_info())
.and_then(|info| info.ability_meta)
.and_then(|meta| meta.kind)
{
Some(Self::Sword(stance))
} else {
None
}
}
}
impl AbilitySet<AbilityItem> {
@ -301,7 +366,7 @@ impl<T> AbilitySet<T> {
AbilitySet {
primary: f(self.primary),
secondary: f(self.secondary),
abilities: self.abilities.into_iter().map(|(s, x)| (s, f(x))).collect(),
abilities: self.abilities.into_iter().map(|x| x.map(&mut f)).collect(),
}
}
@ -309,9 +374,17 @@ impl<T> AbilitySet<T> {
AbilitySet {
primary: f(&self.primary),
secondary: f(&self.secondary),
abilities: self.abilities.iter().map(|(s, x)| (*s, f(x))).collect(),
abilities: self.abilities.iter().map(|x| x.map_ref(&mut f)).collect(),
}
}
pub fn auxiliary(
&self,
index: usize,
context: Option<AbilityContext>,
) -> Option<(Option<Skill>, &T)> {
self.abilities.get(index).and_then(|a| a.ability(context))
}
}
impl Default for AbilitySet<AbilityItem> {

View File

@ -6,7 +6,7 @@ use crate::{
arthropod, biped_large, biped_small,
character_state::OutputEvents,
inventory::slot::{ArmorSlot, EquipSlot, Slot},
item::{armor::Friction, Hands, ItemKind, ToolKind},
item::{armor::Friction, Hands, ItemKind, ToolKind, tool::AbilityContext},
quadruped_low, quadruped_medium, quadruped_small,
skills::{Skill, SwimSkill, SKILL_MODIFIERS},
theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind,
@ -975,6 +975,7 @@ pub fn handle_jump(
}
fn handle_ability(data: &JoinData<'_>, update: &mut StateUpdate, input: InputKind) -> bool {
let context = AbilityContext::yeet(Some(data.character));
if let Some(ability_input) = input.into() {
if let Some((ability, from_offhand)) = data
.active_abilities
@ -984,9 +985,9 @@ fn handle_ability(data: &JoinData<'_>, update: &mut StateUpdate, input: InputKin
data.inventory,
data.skill_set,
Some(data.body),
context,
)
})
.map(|(ability, from_offhand)| (ability.contextualize(data), from_offhand))
.filter(|(ability, _)| ability.requirements_paid(data, update))
{
update.character = CharacterState::from((

View File

@ -7,6 +7,7 @@ use common::{
comp::{
ability::{self, Ability, AbilityKind, ActiveAbilities, AuxiliaryAbility, Capability},
buff::BuffKind,
item::tool::AbilityContext,
skills::{AxeSkill, BowSkill, HammerSkill, SceptreSkill, Skill, StaffSkill, SwordSkill},
AbilityInput, Agent, CharacterAbility, CharacterState, ControlAction, ControlEvent,
Controller, InputKind,
@ -1754,9 +1755,16 @@ impl<'a> AgentData<'a> {
enum ActionStateConditions {
ConditionStaffCanShockwave = 0,
}
let context = AbilityContext::yeet(Some(self.char_state));
let extract_ability = |input: AbilityInput| {
self.active_abilities
.activate_ability(input, Some(self.inventory), self.skill_set, self.body)
.activate_ability(
input,
Some(self.inventory),
self.skill_set,
self.body,
context,
)
.unwrap_or_default()
.0
};

View File

@ -33,7 +33,11 @@ use common::{
self,
ability::{Ability, ActiveAbilities, AuxiliaryAbility, MAX_ABILITIES},
inventory::{
item::{item_key::ItemKey, tool::ToolKind, ItemKind, MaterialStatManifest},
item::{
item_key::ItemKey,
tool::{AbilityContext, ToolKind},
ItemKind, MaterialStatManifest,
},
slot::EquipSlot,
},
skills::{
@ -236,6 +240,7 @@ pub struct Diary<'a> {
tooltip_manager: &'a mut TooltipManager,
slot_manager: &'a mut SlotManager,
pulse: f32,
context: Option<AbilityContext>,
#[conrod(common_builder)]
common: widget::CommonBuilder,
@ -281,6 +286,7 @@ impl<'a> Diary<'a> {
tooltip_manager: &'a mut TooltipManager,
slot_manager: &'a mut SlotManager,
pulse: f32,
context: Option<AbilityContext>,
) -> Self {
Self {
show,
@ -302,6 +308,7 @@ impl<'a> Diary<'a> {
tooltip_manager,
slot_manager,
pulse,
context,
common: widget::CommonBuilder::default(),
created_btns_top_l: 0,
created_btns_top_r: 0,
@ -802,7 +809,12 @@ impl<'a> Widget for Diary<'a> {
amount_margins: Vec2::new(-4.0, 0.0),
amount_font_size: self.fonts.cyri.scale(12),
amount_text_color: TEXT_COLOR,
content_source: &(self.active_abilities, self.inventory, self.skill_set),
content_source: &(
self.active_abilities,
self.inventory,
self.skill_set,
self.context,
),
image_source: self.imgs,
slot_manager: Some(self.slot_manager),
pulse: 0.0,
@ -816,7 +828,7 @@ impl<'a> Widget for Diary<'a> {
Some(self.inventory),
Some(self.skill_set),
)
.ability_id(Some(self.inventory));
.ability_id(Some(self.inventory), self.context);
let (ability_title, ability_desc) = if let Some(ability_id) = ability_id {
util::ability_description(ability_id, self.localized_strings)
} else {
@ -894,16 +906,28 @@ impl<'a> Widget for Diary<'a> {
Some(self.inventory),
Some(self.skill_set),
EquipSlot::ActiveMainhand,
self.context,
)
.map(AuxiliaryAbility::MainWeapon)
.map(|a| (Ability::from(a).ability_id(Some(self.inventory)), a));
.map(|a| {
(
Ability::from(a).ability_id(Some(self.inventory), self.context),
a,
)
});
let off_weap_abilities = ActiveAbilities::iter_unlocked_abilities(
Some(self.inventory),
Some(self.skill_set),
EquipSlot::ActiveOffhand,
self.context,
)
.map(AuxiliaryAbility::OffWeapon)
.map(|a| (Ability::from(a).ability_id(Some(self.inventory)), a));
.map(|a| {
(
Ability::from(a).ability_id(Some(self.inventory), self.context),
a,
)
});
let abilities: Vec<_> = if same_weap_kinds {
// When the weapons have the same ability kind take only the main weapons,
@ -1006,7 +1030,12 @@ impl<'a> Widget for Diary<'a> {
amount_margins: Vec2::new(-4.0, 0.0),
amount_font_size: self.fonts.cyri.scale(12),
amount_text_color: TEXT_COLOR,
content_source: &(self.active_abilities, self.inventory, self.skill_set),
content_source: &(
self.active_abilities,
self.inventory,
self.skill_set,
self.context,
),
image_source: self.imgs,
slot_manager: Some(self.slot_manager),
pulse: 0.0,

View File

@ -87,7 +87,10 @@ use common::{
ability::{self, AuxiliaryAbility},
fluid_dynamics,
inventory::{slot::InvSlotId, trade_pricing::TradePricing, CollectFailedReason},
item::{tool::ToolKind, ItemDesc, MaterialStatManifest, Quality},
item::{
tool::{AbilityContext, ToolKind},
ItemDesc, MaterialStatManifest, Quality,
},
loot_owner::LootOwnerKind,
pet::is_mountable,
skillset::{skills::Skill, SkillGroupKind, SkillsPersistenceError},
@ -2909,6 +2912,7 @@ impl Hud {
skillsets.get(entity),
bodies.get(entity),
) {
let context = AbilityContext::yeet(char_states.get(entity));
Skillbar::new(
client,
&info,
@ -2934,6 +2938,7 @@ impl Hud {
i18n,
&msm,
self.floaters.combo_floater,
context,
)
.set(self.ids.skillbar, ui_widgets);
}
@ -3338,6 +3343,7 @@ impl Hud {
bodies.get(entity),
poises.get(entity),
) {
let context = AbilityContext::yeet(char_states.get(entity));
for event in Diary::new(
&self.show,
client,
@ -3358,6 +3364,7 @@ impl Hud {
tooltip_manager,
&mut self.slot_manager,
self.pulse,
context,
)
.set(self.ids.diary, ui_widgets)
{

View File

@ -24,7 +24,7 @@ use client::{self, Client};
use common::comp::{
self,
ability::AbilityInput,
item::{ItemDesc, MaterialStatManifest},
item::{tool::AbilityContext, ItemDesc, MaterialStatManifest},
Ability, ActiveAbilities, Body, Energy, Health, Inventory, Poise, PoiseState, SkillSet,
};
use conrod_core::{
@ -277,6 +277,7 @@ pub struct Skillbar<'a> {
common: widget::CommonBuilder,
msm: &'a MaterialStatManifest,
combo: Option<ComboFloater>,
context: Option<AbilityContext>,
}
impl<'a> Skillbar<'a> {
@ -306,6 +307,7 @@ impl<'a> Skillbar<'a> {
localized_strings: &'a Localization,
msm: &'a MaterialStatManifest,
combo: Option<ComboFloater>,
context: Option<AbilityContext>,
) -> Self {
Self {
client,
@ -333,6 +335,7 @@ impl<'a> Skillbar<'a> {
localized_strings,
msm,
combo,
context,
}
}
@ -601,6 +604,7 @@ impl<'a> Skillbar<'a> {
self.skillset,
self.active_abilities,
self.body,
self.context,
);
let image_source = (self.item_imgs, self.imgs);
@ -682,7 +686,7 @@ impl<'a> Skillbar<'a> {
// Helper
let tooltip_text = |slot| {
let (hotbar, inventory, _, skill_set, active_abilities, _) = content_source;
let (hotbar, inventory, _, skill_set, active_abilities, _, context) = content_source;
hotbar.get(slot).and_then(|content| match content {
hotbar::SlotContents::Inventory(i, _) => inventory
.get_by_hash(i)
@ -691,7 +695,7 @@ impl<'a> Skillbar<'a> {
.and_then(|a| {
a.auxiliary_set(Some(inventory), Some(skill_set))
.get(i)
.and_then(|a| Ability::from(*a).ability_id(Some(inventory)))
.and_then(|a| Ability::from(*a).ability_id(Some(inventory), context))
})
.map(|id| util::ability_description(id, self.localized_strings)),
})
@ -762,7 +766,7 @@ impl<'a> Skillbar<'a> {
let primary_ability_id = self
.active_abilities
.and_then(|a| Ability::from(a.primary).ability_id(Some(self.inventory)));
.and_then(|a| Ability::from(a.primary).ability_id(Some(self.inventory), self.context));
Button::image(
primary_ability_id.map_or(self.imgs.nothing, |id| util::ability_image(self.imgs, id)),
@ -776,9 +780,9 @@ impl<'a> Skillbar<'a> {
.right_from(state.ids.m1_slot_bg, slot_offset)
.set(state.ids.m2_slot_bg, ui);
let secondary_ability_id = self
.active_abilities
.and_then(|a| Ability::from(a.secondary).ability_id(Some(self.inventory)));
let secondary_ability_id = self.active_abilities.and_then(|a| {
Ability::from(a.secondary).ability_id(Some(self.inventory), self.context)
});
Button::image(
secondary_ability_id.map_or(self.imgs.nothing, |id| util::ability_image(self.imgs, id)),
@ -795,6 +799,7 @@ impl<'a> Skillbar<'a> {
Some(self.inventory),
self.skillset,
Some(self.body),
self.context,
)
})
.map_or(0.0, |(a, _)| a.get_energy_cost())

View File

@ -8,7 +8,7 @@ use crate::ui::slot::{self, SlotKey, SumSlot};
use common::{
comp::{
ability::{Ability, AbilityInput, AuxiliaryAbility},
item::tool::ToolKind,
item::tool::{AbilityContext, ToolKind},
slot::InvSlotId,
ActiveAbilities, Body, Energy, Inventory, Item, ItemKey, SkillSet,
},
@ -128,6 +128,7 @@ type HotbarSource<'a> = (
&'a SkillSet,
Option<&'a ActiveAbilities>,
&'a Body,
Option<AbilityContext>,
);
type HotbarImageSource<'a> = (&'a ItemImgs, &'a img_ids::Imgs);
@ -136,7 +137,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
fn image_key(
&self,
(hotbar, inventory, energy, skillset, active_abilities, body): &HotbarSource<'a>,
(hotbar, inventory, energy, skillset, active_abilities, body, context): &HotbarSource<'a>,
) -> Option<(Self::ImageKey, Option<Color>)> {
const GREYED_OUT: Color = Color::Rgba(0.3, 0.3, 0.3, 0.8);
hotbar.get(*self).and_then(|contents| match contents {
@ -151,7 +152,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
let ability_id = active_abilities.and_then(|a| {
a.auxiliary_set(Some(inventory), Some(skillset))
.get(i)
.and_then(|a| Ability::from(*a).ability_id(Some(inventory)))
.and_then(|a| Ability::from(*a).ability_id(Some(inventory), *context))
});
ability_id
@ -164,6 +165,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
Some(inventory),
skillset,
Some(body),
*context,
)
})
.map(|(ability, _)| {
@ -181,7 +183,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
})
}
fn amount(&self, (hotbar, inventory, _, _, _, _): &HotbarSource<'a>) -> Option<u32> {
fn amount(&self, (hotbar, inventory, _, _, _, _, _): &HotbarSource<'a>) -> Option<u32> {
hotbar
.get(*self)
.and_then(|content| match content {
@ -209,14 +211,19 @@ pub enum AbilitySlot {
Ability(AuxiliaryAbility),
}
type AbilitiesSource<'a> = (&'a ActiveAbilities, &'a Inventory, &'a SkillSet);
type AbilitiesSource<'a> = (
&'a ActiveAbilities,
&'a Inventory,
&'a SkillSet,
Option<AbilityContext>,
);
impl<'a> SlotKey<AbilitiesSource<'a>, img_ids::Imgs> for AbilitySlot {
type ImageKey = String;
fn image_key(
&self,
(active_abilities, inventory, skillset): &AbilitiesSource<'a>,
(active_abilities, inventory, skillset, context): &AbilitiesSource<'a>,
) -> Option<(Self::ImageKey, Option<Color>)> {
let ability_id = match self {
Self::Slot(index) => active_abilities
@ -225,8 +232,8 @@ impl<'a> SlotKey<AbilitiesSource<'a>, img_ids::Imgs> for AbilitySlot {
Some(inventory),
Some(skillset),
)
.ability_id(Some(inventory)),
Self::Ability(ability) => Ability::from(*ability).ability_id(Some(inventory)),
.ability_id(Some(inventory), *context),
Self::Ability(ability) => Ability::from(*ability).ability_id(Some(inventory), *context),
};
ability_id.map(|id| (String::from(id), None))

View File

@ -32,7 +32,7 @@ use anim::{
use common::{
comp::{
inventory::slot::EquipSlot,
item::{Hands, ItemKind, ToolKind},
item::{tool::AbilityContext, Hands, ItemKind, ToolKind},
Body, CharacterState, Collider, Controller, Health, Inventory, Item, ItemKey, Last,
LightAnimation, LightEmitter, Ori, PhysicsState, PoiseState, Pos, Scale, Vel,
},
@ -917,10 +917,12 @@ impl FigureMgr {
let second_tool_spec = second_tool_spec.as_deref();
let hands = (active_tool_hand, second_tool_hand);
let context = AbilityContext::yeet(character);
let ability_id = character.and_then(|c| {
c.ability_info()
.and_then(|a| a.ability)
.and_then(|a| a.ability_id(inventory))
.and_then(|a| a.ability_id(inventory, context))
});
let move_dir = {