From 4309e1ff9bbb8c2bec124e281b51625740940e9e Mon Sep 17 00:00:00 2001
From: Sam <samuelkeiffer@gmail.com>
Date: Tue, 9 Nov 2021 21:20:41 -0500
Subject: [PATCH] Did voxygen stuff for ability pool.

---
 common/src/comp/ability.rs  |   8 +-
 server/src/sys/sentinel.rs  |  17 ++++-
 voxygen/src/hud/hotbar.rs   | 140 ++++++----------------------------
 voxygen/src/hud/mod.rs      |  36 ++++-----
 voxygen/src/hud/skillbar.rs |  68 ++++++++---------
 voxygen/src/hud/slots.rs    | 148 ++++++++++++------------------------
 voxygen/src/session/mod.rs  |  12 +--
 7 files changed, 140 insertions(+), 289 deletions(-)

diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs
index 981ce44140..000f839f91 100644
--- a/common/src/comp/ability.rs
+++ b/common/src/comp/ability.rs
@@ -40,10 +40,10 @@ pub const MAX_ABILITIES: usize = 5;
 // considerations.
 #[derive(Serialize, Deserialize, Debug, Clone)]
 pub struct AbilityPool {
-    primary: Ability,
-    secondary: Ability,
-    movement: Ability,
-    abilities: [Ability; MAX_ABILITIES],
+    pub primary: Ability,
+    pub secondary: Ability,
+    pub movement: Ability,
+    pub abilities: [Ability; MAX_ABILITIES],
 }
 
 impl Component for AbilityPool {
diff --git a/server/src/sys/sentinel.rs b/server/src/sys/sentinel.rs
index f8bcb57265..966ec59d45 100644
--- a/server/src/sys/sentinel.rs
+++ b/server/src/sys/sentinel.rs
@@ -1,9 +1,9 @@
 use common::{
     comp::{
         item::{tool::AbilityMap, MaterialStatManifest},
-        Auras, BeamSegment, Body, Buffs, CanBuild, CharacterState, Collider, Combo, Density,
-        Energy, Group, Health, Inventory, Item, LightEmitter, Mass, MountState, Mounting, Ori,
-        Player, Poise, Pos, Scale, Shockwave, SkillSet, Stats, Sticky, Vel,
+        AbilityPool, Auras, BeamSegment, Body, Buffs, CanBuild, CharacterState, Collider, Combo,
+        Density, Energy, Group, Health, Inventory, Item, LightEmitter, Mass, MountState, Mounting,
+        Ori, Player, Poise, Pos, Scale, Shockwave, SkillSet, Stats, Sticky, Vel,
     },
     uid::Uid,
 };
@@ -44,6 +44,7 @@ pub struct TrackedComps<'a> {
     pub player: ReadStorage<'a, Player>,
     pub stats: ReadStorage<'a, Stats>,
     pub skill_set: ReadStorage<'a, SkillSet>,
+    pub ability_pool: ReadStorage<'a, AbilityPool>,
     pub buffs: ReadStorage<'a, Buffs>,
     pub auras: ReadStorage<'a, Auras>,
     pub energy: ReadStorage<'a, Energy>,
@@ -91,6 +92,10 @@ impl<'a> TrackedComps<'a> {
             .get(entity)
             .cloned()
             .map(|c| comps.push(c.into()));
+        self.ability_pool
+            .get(entity)
+            .cloned()
+            .map(|c| comps.push(c.into()));
         self.buffs
             .get(entity)
             .cloned()
@@ -187,6 +192,7 @@ pub struct ReadTrackers<'a> {
     pub player: ReadExpect<'a, UpdateTracker<Player>>,
     pub stats: ReadExpect<'a, UpdateTracker<Stats>>,
     pub skill_set: ReadExpect<'a, UpdateTracker<SkillSet>>,
+    pub ability_pool: ReadExpect<'a, UpdateTracker<AbilityPool>>,
     pub buffs: ReadExpect<'a, UpdateTracker<Buffs>>,
     pub auras: ReadExpect<'a, UpdateTracker<Auras>>,
     pub energy: ReadExpect<'a, UpdateTracker<Energy>>,
@@ -223,6 +229,7 @@ impl<'a> ReadTrackers<'a> {
             .with_component(&comps.uid, &*self.player, &comps.player, filter)
             .with_component(&comps.uid, &*self.stats, &comps.stats, filter)
             .with_component(&comps.uid, &*self.skill_set, &comps.skill_set, filter)
+            .with_component(&comps.uid, &*self.ability_pool, &comps.ability_pool, filter)
             .with_component(&comps.uid, &*self.buffs, &comps.buffs, filter)
             .with_component(&comps.uid, &*self.auras, &comps.auras, filter)
             .with_component(&comps.uid, &*self.energy, &comps.energy, filter)
@@ -266,6 +273,7 @@ pub struct WriteTrackers<'a> {
     player: WriteExpect<'a, UpdateTracker<Player>>,
     stats: WriteExpect<'a, UpdateTracker<Stats>>,
     skill_set: WriteExpect<'a, UpdateTracker<SkillSet>>,
+    ability_pool: WriteExpect<'a, UpdateTracker<AbilityPool>>,
     buffs: WriteExpect<'a, UpdateTracker<Buffs>>,
     auras: WriteExpect<'a, UpdateTracker<Auras>>,
     energy: WriteExpect<'a, UpdateTracker<Energy>>,
@@ -296,6 +304,7 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) {
     trackers.player.record_changes(&comps.player);
     trackers.stats.record_changes(&comps.stats);
     trackers.skill_set.record_changes(&comps.skill_set);
+    trackers.ability_pool.record_changes(&comps.ability_pool);
     trackers.buffs.record_changes(&comps.buffs);
     trackers.auras.record_changes(&comps.auras);
     trackers.energy.record_changes(&comps.energy);
@@ -341,6 +350,7 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) {
     log_counts!(player, "Players");
     log_counts!(stats, "Stats");
     log_counts!(skill_set, "SkillSet");
+    log_counts!(ability_pool, "AbilityPool");
     log_counts!(energy, "Energies");
     log_counts!(combo, "Combos");
     log_vounts!(health, "Healths");
@@ -367,6 +377,7 @@ pub fn register_trackers(world: &mut World) {
     world.register_tracker::<Player>();
     world.register_tracker::<Stats>();
     world.register_tracker::<SkillSet>();
+    world.register_tracker::<AbilityPool>();
     world.register_tracker::<Buffs>();
     world.register_tracker::<Auras>();
     world.register_tracker::<Energy>();
diff --git a/voxygen/src/hud/hotbar.rs b/voxygen/src/hud/hotbar.rs
index 8bbcd07a15..df0d886a70 100644
--- a/voxygen/src/hud/hotbar.rs
+++ b/voxygen/src/hud/hotbar.rs
@@ -1,9 +1,4 @@
-use crate::hud::slots::EquipSlot;
-use common::comp::{
-    item::{tool::Hands, ItemKind},
-    slot::InvSlotId,
-    Inventory,
-};
+use common::comp::slot::InvSlotId;
 use serde::{Deserialize, Serialize};
 
 #[derive(Clone, Copy, Debug, PartialEq)]
@@ -23,8 +18,7 @@ pub enum Slot {
 #[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
 pub enum SlotContents {
     Inventory(InvSlotId),
-    Ability3,
-    Ability4,
+    Ability(usize),
 }
 
 #[derive(Clone, Copy, Default)]
@@ -59,120 +53,32 @@ impl State {
         self.slots[slot as usize] = Some(SlotContents::Inventory(inventory_pos));
     }
 
-    // TODO: remove
-    // Adds ability3 slot if it is missing and should be present
-    // Removes if it is there and shouldn't be present
-    pub fn maintain_ability3(&mut self, client: &client::Client) {
+    // TODO: remove pending UI
+    // Adds ability slots if missing and should be present
+    // Removes ability slots if not there and shouldn't be present
+    pub fn maintain_abilities(&mut self, client: &client::Client) {
         use specs::WorldExt;
-        let inventories = client.state().ecs().read_storage::<Inventory>();
-        let inventory = inventories.get(client.entity());
-        let skill_sets = client
+        if let Some(ability_pool) = client
             .state()
             .ecs()
-            .read_storage::<common::comp::SkillSet>();
-        let skill_set = skill_sets.get(client.entity());
-
-        let hands =
-            |equip_slot| match inventory.and_then(|i| i.equipped(equip_slot).map(|i| i.kind())) {
-                Some(ItemKind::Tool(tool)) => Some(tool.hands),
-                _ => None,
-            };
-
-        let equip_slot = match (
-            hands(EquipSlot::ActiveMainhand),
-            hands(EquipSlot::ActiveOffhand),
-        ) {
-            (Some(_), _) => Some(EquipSlot::ActiveMainhand),
-            (_, Some(_)) => Some(EquipSlot::ActiveOffhand),
-            _ => None,
-        };
-
-        let should_be_present = if let (Some(inventory), Some(skill_set), Some(equip_slot)) =
-            (inventory, skill_set, equip_slot)
+            .read_storage::<common::comp::AbilityPool>()
+            .get(client.entity())
         {
-            inventory.equipped(equip_slot).map_or(false, |i| {
-                i.item_config_expect()
-                    .abilities
-                    .abilities
-                    .get(0)
-                    .as_ref()
-                    .map_or(false, |(s, _)| s.map_or(true, |s| skill_set.has_skill(s)))
-            })
-        } else {
-            false
-        };
-
-        if should_be_present {
-            if !self
-                .slots
-                .iter()
-                .any(|s| matches!(s, Some(SlotContents::Ability3)))
-            {
-                self.slots[0] = Some(SlotContents::Ability3);
+            use common::comp::Ability;
+            for (i, ability) in ability_pool.abilities.iter().enumerate() {
+                if matches!(ability, Ability::Empty) {
+                    self.slots
+                        .iter_mut()
+                        .filter(|s| matches!(s, Some(SlotContents::Ability(index)) if *index == i))
+                        .for_each(|s| *s = None)
+                } else if let Some(slot) = self
+                    .slots
+                    .iter_mut()
+                    .find(|s| !matches!(s, Some(SlotContents::Ability(index)) if *index != i))
+                {
+                    *slot = Some(SlotContents::Ability(i));
+                }
             }
-        } else {
-            self.slots
-                .iter_mut()
-                .filter(|s| matches!(s, Some(SlotContents::Ability3)))
-                .for_each(|s| *s = None)
-        }
-    }
-
-    pub fn maintain_ability4(&mut self, client: &client::Client) {
-        use specs::WorldExt;
-        let inventories = client.state().ecs().read_storage::<Inventory>();
-        let inventory = inventories.get(client.entity());
-        let skill_sets = client
-            .state()
-            .ecs()
-            .read_storage::<common::comp::SkillSet>();
-        let skill_set = skill_sets.get(client.entity());
-        let should_be_present = if let (Some(inventory), Some(skill_set)) = (inventory, skill_set) {
-            let hands = |equip_slot| match inventory.equipped(equip_slot).map(|i| i.kind()) {
-                Some(ItemKind::Tool(tool)) => Some(tool.hands),
-                _ => None,
-            };
-
-            let active_tool_hands = hands(EquipSlot::ActiveMainhand);
-            let second_tool_hands = hands(EquipSlot::ActiveOffhand);
-
-            let (equip_slot, skill_index) = match (active_tool_hands, second_tool_hands) {
-                (Some(Hands::Two), _) => (Some(EquipSlot::ActiveMainhand), 1),
-                (Some(_), Some(Hands::One)) => (Some(EquipSlot::ActiveOffhand), 0),
-                (Some(Hands::One), _) => (Some(EquipSlot::ActiveMainhand), 1),
-                (None, Some(_)) => (Some(EquipSlot::ActiveOffhand), 1),
-                (_, _) => (None, 0),
-            };
-
-            if let Some(equip_slot) = equip_slot {
-                inventory.equipped(equip_slot).map_or(false, |i| {
-                    i.item_config_expect()
-                        .abilities
-                        .abilities
-                        .get(skill_index)
-                        .as_ref()
-                        .map_or(false, |(s, _)| s.map_or(true, |s| skill_set.has_skill(s)))
-                })
-            } else {
-                false
-            }
-        } else {
-            false
-        };
-
-        if should_be_present {
-            if !self
-                .slots
-                .iter()
-                .any(|s| matches!(s, Some(SlotContents::Ability4)))
-            {
-                self.slots[1] = Some(SlotContents::Ability4);
-            }
-        } else {
-            self.slots
-                .iter_mut()
-                .filter(|s| matches!(s, Some(SlotContents::Ability4)))
-                .for_each(|s| *s = None)
         }
     }
 }
diff --git a/voxygen/src/hud/mod.rs b/voxygen/src/hud/mod.rs
index f6561f66db..fa1b32170e 100644
--- a/voxygen/src/hud/mod.rs
+++ b/voxygen/src/hud/mod.rs
@@ -512,8 +512,7 @@ pub enum Event {
     SortInventory,
     ChangeHotbarState(Box<HotbarState>),
     TradeAction(TradeAction),
-    Ability3(bool),
-    Ability4(bool),
+    Ability(usize, bool),
     Logout,
     Quit,
 
@@ -2639,6 +2638,7 @@ impl Hud {
         let inventories = ecs.read_storage::<comp::Inventory>();
         let energies = ecs.read_storage::<comp::Energy>();
         let skillsets = ecs.read_storage::<comp::SkillSet>();
+        let ability_pools = ecs.read_storage::<comp::AbilityPool>();
         let character_states = ecs.read_storage::<comp::CharacterState>();
         let controllers = ecs.read_storage::<comp::Controller>();
         let bodies = ecs.read_storage::<comp::Body>();
@@ -2664,6 +2664,8 @@ impl Hud {
             Some(inventory),
             Some(energy),
             Some(skillset),
+            Some(ability_pool),
+            Some(body),
             Some(_character_state),
             Some(_controller),
         ) = (
@@ -2671,6 +2673,8 @@ impl Hud {
             inventories.get(entity),
             energies.get(entity),
             skillsets.get(entity),
+            ability_pools.get(entity),
+            bodies.get(entity),
             character_states.get(entity),
             controllers.get(entity).map(|c| &c.inputs),
         ) {
@@ -2685,6 +2689,8 @@ impl Hud {
                 inventory,
                 energy,
                 skillset,
+                ability_pool,
+                body,
                 //&character_state,
                 self.pulse,
                 //&controller,
@@ -3358,18 +3364,14 @@ impl Hud {
                         }
                     } else if let Hotbar(h) = from {
                         // Used from hotbar
-                        self.hotbar.get(h).map(|s| {
-                            match s {
-                                hotbar::SlotContents::Inventory(i) => {
-                                    events.push(Event::UseSlot {
-                                        slot: comp::slot::Slot::Inventory(i),
-                                        bypass_dialog: false,
-                                    });
-                                },
-                                hotbar::SlotContents::Ability3 | hotbar::SlotContents::Ability4 => {
-                                }, /* Event::Ability3(true),
-                                    * sticks */
-                            }
+                        self.hotbar.get(h).map(|s| match s {
+                            hotbar::SlotContents::Inventory(i) => {
+                                events.push(Event::UseSlot {
+                                    slot: comp::slot::Slot::Inventory(i),
+                                    bypass_dialog: false,
+                                });
+                            },
+                            hotbar::SlotContents::Ability(_) => {},
                         });
                     }
                 },
@@ -3496,8 +3498,7 @@ impl Hud {
                 },
             }
         }
-        self.hotbar.maintain_ability3(client);
-        self.hotbar.maintain_ability4(client);
+        self.hotbar.maintain_abilities(client);
 
         events
     }
@@ -3577,8 +3578,7 @@ impl Hud {
                             });
                         }
                     },
-                    hotbar::SlotContents::Ability3 => events.push(Event::Ability3(state)),
-                    hotbar::SlotContents::Ability4 => events.push(Event::Ability4(state)),
+                    hotbar::SlotContents::Ability(i) => events.push(Event::Ability(i, state)),
                 });
             }
         }
diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs
index 93157d3aa6..57c540c85a 100644
--- a/voxygen/src/hud/skillbar.rs
+++ b/voxygen/src/hud/skillbar.rs
@@ -26,7 +26,7 @@ use common::comp::{
         tool::{Tool, ToolKind},
         Hands, Item, ItemDesc, ItemKind, MaterialStatManifest,
     },
-    Energy, Health, Inventory, SkillSet,
+    AbilityPool, Body, Energy, Health, Inventory, SkillSet,
 };
 use conrod_core::{
     color,
@@ -253,6 +253,8 @@ pub struct Skillbar<'a> {
     inventory: &'a Inventory,
     energy: &'a Energy,
     skillset: &'a SkillSet,
+    ability_pool: &'a AbilityPool,
+    body: &'a Body,
     // character_state: &'a CharacterState,
     // controller: &'a ControllerInputs,
     hotbar: &'a hotbar::State,
@@ -280,6 +282,8 @@ impl<'a> Skillbar<'a> {
         inventory: &'a Inventory,
         energy: &'a Energy,
         skillset: &'a SkillSet,
+        ability_pool: &'a AbilityPool,
+        body: &'a Body,
         // character_state: &'a CharacterState,
         pulse: f32,
         // controller: &'a ControllerInputs,
@@ -302,6 +306,8 @@ impl<'a> Skillbar<'a> {
             inventory,
             energy,
             skillset,
+            ability_pool,
+            body,
             common: widget::CommonBuilder::default(),
             // character_state,
             pulse,
@@ -511,7 +517,14 @@ impl<'a> Skillbar<'a> {
         let key_layout = &self.global_state.window.key_layout;
 
         // TODO: avoid this
-        let content_source = (self.hotbar, self.inventory, self.energy, self.skillset);
+        let content_source = (
+            self.hotbar,
+            self.inventory,
+            self.energy,
+            self.skillset,
+            self.ability_pool,
+            self.body,
+        );
 
         let image_source = (self.item_imgs, self.imgs);
         let mut slot_maker = SlotMaker {
@@ -591,45 +604,26 @@ impl<'a> Skillbar<'a> {
 
         // Helper
         let tooltip_text = |slot| {
-            let (hotbar, inventory, ..) = content_source;
+            let (hotbar, inventory, _, _, ability_pool, _) = content_source;
             hotbar.get(slot).and_then(|content| match content {
                 hotbar::SlotContents::Inventory(i) => inventory
                     .get(i)
                     .map(|item| (item.name(), item.description())),
-                hotbar::SlotContents::Ability3 => inventory
-                    .equipped(EquipSlot::ActiveMainhand)
-                    .map(|i| i.kind())
-                    .and_then(|kind| match kind {
-                        ItemKind::Tool(Tool { kind, .. }) => ability_description(kind),
-                        _ => None,
-                    }),
-                hotbar::SlotContents::Ability4 => {
-                    let hands = |equip_slot| match inventory.equipped(equip_slot).map(|i| i.kind())
-                    {
-                        Some(ItemKind::Tool(tool)) => Some(tool.hands),
-                        _ => None,
-                    };
-
-                    let active_tool_hands = hands(EquipSlot::ActiveMainhand);
-                    let second_tool_hands = hands(EquipSlot::ActiveOffhand);
-
-                    let equip_slot = match (active_tool_hands, second_tool_hands) {
-                        (Some(Hands::Two), _) => Some(EquipSlot::ActiveMainhand),
-                        (Some(_), Some(Hands::One)) => Some(EquipSlot::ActiveOffhand),
-                        (Some(Hands::One), _) => Some(EquipSlot::ActiveMainhand),
-                        (None, Some(_)) => Some(EquipSlot::ActiveOffhand),
-                        (_, _) => None,
-                    };
-
-                    equip_slot.and_then(|equip_slot| {
-                        inventory
-                            .equipped(equip_slot)
-                            .map(|i| i.kind())
-                            .and_then(|kind| match kind {
-                                ItemKind::Tool(Tool { kind, .. }) => ability_description(kind),
-                                _ => None,
-                            })
-                    })
+                hotbar::SlotContents::Ability(i) => {
+                    use comp::Ability;
+                    ability_pool
+                        .abilities
+                        .get(i)
+                        .and_then(|a| match a {
+                            Ability::MainWeaponAbility(_) => Some(EquipSlot::ActiveMainhand),
+                            Ability::OffWeaponAbility(_) => Some(EquipSlot::ActiveOffhand),
+                            _ => None,
+                        })
+                        .and_then(|equip_slot| inventory.equipped(equip_slot))
+                        .and_then(|item| match &item.kind {
+                            ItemKind::Tool(Tool { kind, .. }) => ability_description(kind),
+                            _ => None,
+                        })
                 },
             })
         };
diff --git a/voxygen/src/hud/slots.rs b/voxygen/src/hud/slots.rs
index d77bebb153..8a3a2707d7 100644
--- a/voxygen/src/hud/slots.rs
+++ b/voxygen/src/hud/slots.rs
@@ -5,12 +5,14 @@ use super::{
 };
 use crate::ui::slot::{self, SlotKey, SumSlot};
 use common::comp::{
+    self,
+    controller::InputKind,
     item::{
-        tool::{Hands, ToolKind},
+        tool::{Tool, ToolKind},
         ItemKind,
     },
     slot::InvSlotId,
-    Energy, Inventory, SkillSet,
+    AbilityPool, Body, Energy, Inventory, SkillSet,
 };
 use conrod_core::{image, Color};
 use specs::Entity as EcsEntity;
@@ -122,7 +124,14 @@ pub enum HotbarImage {
     SceptreAura,
 }
 
-type HotbarSource<'a> = (&'a hotbar::State, &'a Inventory, &'a Energy, &'a SkillSet);
+type HotbarSource<'a> = (
+    &'a hotbar::State,
+    &'a Inventory,
+    &'a Energy,
+    &'a SkillSet,
+    &'a AbilityPool,
+    &'a Body,
+);
 type HotbarImageSource<'a> = (&'a ItemImgs, &'a img_ids::Imgs);
 
 impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
@@ -130,120 +139,59 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
 
     fn image_key(
         &self,
-        (hotbar, inventory, energy, skillset): &HotbarSource<'a>,
+        (hotbar, inventory, energy, skillset, ability_pool, body): &HotbarSource<'a>,
     ) -> Option<(Self::ImageKey, Option<Color>)> {
         hotbar.get(*self).and_then(|contents| match contents {
             hotbar::SlotContents::Inventory(idx) => inventory
                 .get(idx)
                 .map(|item| HotbarImage::Item(item.into()))
                 .map(|i| (i, None)),
-            hotbar::SlotContents::Ability3 => {
-                let hands = |equip_slot| match inventory.equipped(equip_slot).map(|i| i.kind()) {
-                    Some(ItemKind::Tool(tool)) => Some(tool.hands),
-                    _ => None,
-                };
-
-                let active_tool_hands = hands(EquipSlot::ActiveMainhand);
-                let second_tool_hands = hands(EquipSlot::ActiveOffhand);
-
-                let equip_slot = match (active_tool_hands, second_tool_hands) {
-                    (Some(_), _) => Some(EquipSlot::ActiveMainhand),
-                    (None, Some(_)) => Some(EquipSlot::ActiveOffhand),
-                    (_, _) => None,
-                };
-
-                let tool =
-                    equip_slot.and_then(|es| match inventory.equipped(es).map(|i| (i, i.kind())) {
-                        Some((item, ItemKind::Tool(tool))) => Some((item, tool)),
+            hotbar::SlotContents::Ability(i) => {
+                use comp::Ability;
+                let tool_kind = ability_pool
+                    .abilities
+                    .get(i)
+                    .and_then(|a| match a {
+                        Ability::MainWeaponAbility(_) => Some(EquipSlot::ActiveMainhand),
+                        Ability::OffWeaponAbility(_) => Some(EquipSlot::ActiveOffhand),
+                        _ => None,
+                    })
+                    .and_then(|equip_slot| inventory.equipped(equip_slot))
+                    .and_then(|item| match &item.kind {
+                        ItemKind::Tool(Tool { kind, .. }) => Some(kind),
                         _ => None,
                     });
-
-                tool.and_then(|(item, tool)| {
-                    hotbar_image(tool.kind).map(|i| {
-                        (
-                            i,
-                            if let Some(skill) =
-                                item.item_config_expect().abilities.abilities.get(0)
-                            {
-                                if energy.current()
-                                    >= skill
-                                        .1
-                                        .clone()
-                                        .adjusted_by_skills(skillset, Some(tool.kind))
-                                        .get_energy_cost()
-                                {
-                                    Some(Color::Rgba(1.0, 1.0, 1.0, 1.0))
-                                } else {
-                                    Some(Color::Rgba(0.3, 0.3, 0.3, 0.8))
-                                }
-                            } else {
-                                Some(Color::Rgba(1.0, 1.0, 1.0, 1.0))
-                            },
-                        )
+                tool_kind
+                    .and_then(|kind| hotbar_image(*kind))
+                    .and_then(|image| {
+                        ability_pool
+                            .activate_ability(
+                                InputKind::Ability(i),
+                                Some(inventory),
+                                skillset,
+                                body,
+                            )
+                            .map(|(ability, _)| {
+                                (
+                                    image,
+                                    if energy.current() > ability.get_energy_cost() {
+                                        Some(Color::Rgba(1.0, 1.0, 1.0, 1.0))
+                                    } else {
+                                        Some(Color::Rgba(0.3, 0.3, 0.3, 0.8))
+                                    },
+                                )
+                            })
                     })
-                })
-            },
-            hotbar::SlotContents::Ability4 => {
-                let hands = |equip_slot| match inventory.equipped(equip_slot).map(|i| i.kind()) {
-                    Some(ItemKind::Tool(tool)) => Some(tool.hands),
-                    _ => None,
-                };
-
-                let active_tool_hands = hands(EquipSlot::ActiveMainhand);
-                let second_tool_hands = hands(EquipSlot::ActiveOffhand);
-
-                let (equip_slot, skill_index) = match (active_tool_hands, second_tool_hands) {
-                    (Some(Hands::Two), _) => (Some(EquipSlot::ActiveMainhand), 1),
-                    (Some(_), Some(Hands::One)) => (Some(EquipSlot::ActiveOffhand), 0),
-                    (Some(Hands::One), _) => (Some(EquipSlot::ActiveMainhand), 1),
-                    (None, Some(_)) => (Some(EquipSlot::ActiveOffhand), 1),
-                    (_, _) => (None, 0),
-                };
-
-                let tool =
-                    match equip_slot.and_then(|es| inventory.equipped(es).map(|i| (i, i.kind()))) {
-                        Some((item, ItemKind::Tool(tool))) => Some((item, tool)),
-                        _ => None,
-                    };
-
-                tool.and_then(|(item, tool)| {
-                    hotbar_image(tool.kind).map(|i| {
-                        (
-                            i,
-                            if let Some(skill) = item
-                                .item_config_expect()
-                                .abilities
-                                .abilities
-                                .get(skill_index)
-                            {
-                                if energy.current()
-                                    >= skill
-                                        .1
-                                        .clone()
-                                        .adjusted_by_skills(skillset, Some(tool.kind))
-                                        .get_energy_cost()
-                                {
-                                    Some(Color::Rgba(1.0, 1.0, 1.0, 1.0))
-                                } else {
-                                    Some(Color::Rgba(0.3, 0.3, 0.3, 0.8))
-                                }
-                            } else {
-                                Some(Color::Rgba(1.0, 1.0, 1.0, 1.0))
-                            },
-                        )
-                    })
-                })
             },
         })
     }
 
-    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 {
                 hotbar::SlotContents::Inventory(idx) => inventory.get(idx),
-                hotbar::SlotContents::Ability3 => None,
-                hotbar::SlotContents::Ability4 => None,
+                hotbar::SlotContents::Ability(_) => None,
             })
             .map(|item| item.amount())
             .filter(|amount| *amount > 1)
diff --git a/voxygen/src/session/mod.rs b/voxygen/src/session/mod.rs
index b891446e8d..620bb3f905 100644
--- a/voxygen/src/session/mod.rs
+++ b/voxygen/src/session/mod.rs
@@ -1370,17 +1370,9 @@ impl PlayState for SessionState {
                     HudEvent::TradeAction(action) => {
                         self.client.borrow_mut().perform_trade_action(action);
                     },
-                    HudEvent::Ability3(state) => {
+                    HudEvent::Ability(i, state) => {
                         self.client.borrow_mut().handle_input(
-                            InputKind::Ability(0),
-                            state,
-                            default_select_pos,
-                            self.target_entity,
-                        );
-                    },
-                    HudEvent::Ability4(state) => {
-                        self.client.borrow_mut().handle_input(
-                            InputKind::Ability(1),
+                            InputKind::Ability(i),
                             state,
                             default_select_pos,
                             self.target_entity,