From 18309fe4c62b5673800c1525d4bdfed92c4580b6 Mon Sep 17 00:00:00 2001 From: Sam Date: Sun, 14 Feb 2021 01:01:53 -0500 Subject: [PATCH] Added AbilityInfo struct to attack states --- common/src/comp/ability.rs | 28 +++++------ common/src/states/basic_beam.rs | 11 ++--- common/src/states/basic_melee.rs | 4 +- common/src/states/basic_ranged.rs | 6 +-- common/src/states/charged_melee.rs | 8 ++-- common/src/states/charged_ranged.rs | 10 ++-- common/src/states/combo_melee.rs | 6 +-- common/src/states/dash_melee.rs | 8 ++-- common/src/states/leap_melee.rs | 4 +- common/src/states/repeater_ranged.rs | 4 +- common/src/states/shockwave.rs | 4 +- common/src/states/spin_melee.rs | 6 +-- common/src/states/utils.rs | 72 ++++++++++++++++++++++------ 13 files changed, 105 insertions(+), 66 deletions(-) diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 5bfbda0453..5e9d904b17 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -6,7 +6,7 @@ use crate::{ }, states::{ behavior::JoinData, - utils::{AbilityKey, StageSection}, + utils::{AbilityInfo, StageSection}, *, }, Knockback, @@ -1060,8 +1060,8 @@ impl CharacterAbility { } } -impl From<(&CharacterAbility, AbilityKey)> for CharacterState { - fn from((ability, key): (&CharacterAbility, AbilityKey)) -> Self { +impl From<(&CharacterAbility, AbilityInfo)> for CharacterState { + fn from((ability, ability_info): (&CharacterAbility, AbilityInfo)) -> Self { match ability { CharacterAbility::BasicMelee { buildup_duration, @@ -1083,7 +1083,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { knockback: *knockback, range: *range, max_angle: *max_angle, - ability_key: key, + ability_info, }, timer: Duration::default(), stage_section: StageSection::Buildup, @@ -1109,7 +1109,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { projectile_gravity: *projectile_gravity, projectile_speed: *projectile_speed, can_continue: *can_continue, - ability_key: key, + ability_info, }, timer: Duration::default(), stage_section: StageSection::Buildup, @@ -1162,7 +1162,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { swing_duration: Duration::from_secs_f32(*swing_duration), recover_duration: Duration::from_secs_f32(*recover_duration), is_interruptible: *is_interruptible, - ability_key: key, + ability_info, }, auto_charge: false, timer: Duration::default(), @@ -1212,7 +1212,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { max_speed_increase: *max_speed_increase, scales_from_combo: *scales_from_combo, is_interruptible: *is_interruptible, - ability_key: key, + ability_info, }, stage: 1, combo: 0, @@ -1246,7 +1246,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { max_angle: *max_angle, forward_leap_strength: *forward_leap_strength, vertical_leap_strength: *vertical_leap_strength, - ability_key: key, + ability_info, }, timer: Duration::default(), stage_section: StageSection::Buildup, @@ -1281,7 +1281,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { is_interruptible: *is_interruptible, forward_speed: *forward_speed, num_spins: *num_spins, - ability_key: key, + ability_info, }, timer: Duration::default(), spins_remaining: *num_spins - 1, @@ -1321,7 +1321,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { swing_duration: Duration::from_secs_f32(*swing_duration), hit_timing: *hit_timing, recover_duration: Duration::from_secs_f32(*recover_duration), - ability_key: key, + ability_info, }, stage_section: StageSection::Charge, timer: Duration::default(), @@ -1362,7 +1362,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { initial_projectile_speed: *initial_projectile_speed, scaled_projectile_speed: *scaled_projectile_speed, move_speed: *move_speed, - ability_key: key, + ability_info, }, timer: Duration::default(), stage_section: StageSection::Buildup, @@ -1393,7 +1393,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { projectile_light: *projectile_light, projectile_gravity: *projectile_gravity, projectile_speed: *projectile_speed, - ability_key: key, + ability_info, }, timer: Duration::default(), stage_section: StageSection::Movement, @@ -1427,7 +1427,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { shockwave_duration: Duration::from_secs_f32(*shockwave_duration), requires_ground: *requires_ground, move_efficiency: *move_efficiency, - ability_key: key, + ability_info, }, timer: Duration::default(), stage_section: StageSection::Buildup, @@ -1459,7 +1459,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState { energy_regen: *energy_regen, energy_cost: *energy_cost, energy_drain: *energy_drain, - ability_key: key, + ability_info, }, timer: Duration::default(), stage_section: StageSection::Buildup, diff --git a/common/src/states/basic_beam.rs b/common/src/states/basic_beam.rs index da9973edf9..3e7028f53f 100644 --- a/common/src/states/basic_beam.rs +++ b/common/src/states/basic_beam.rs @@ -44,7 +44,7 @@ pub struct StaticData { /// Energy drained per pub energy_drain: f32, /// What key is used to press ability - pub ability_key: AbilityKey, + pub ability_info: AbilityInfo, } #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -68,7 +68,7 @@ impl CharacterBehavior for Data { handle_move(data, &mut update, 0.4); handle_jump(data, &mut update); - if !ability_key_is_pressed(data, self.static_data.ability_key) { + if !ability_key_is_pressed(data, self.static_data.ability_info.key) { handle_interrupt(data, &mut update, false); match update.character { CharacterState::BasicBeam(_) => {}, @@ -78,11 +78,6 @@ impl CharacterBehavior for Data { } } - if unwrap_tool_data(data).is_none() { - update.character = CharacterState::Idle; - return update; - } - match self.stage_section { StageSection::Buildup => { if self.timer < self.static_data.buildup_duration { @@ -119,7 +114,7 @@ impl CharacterBehavior for Data { } }, StageSection::Cast => { - if ability_key_is_pressed(data, self.static_data.ability_key) + if ability_key_is_pressed(data, self.static_data.ability_info.key) && (self.static_data.energy_drain <= f32::EPSILON || update.energy.current() > 0) { diff --git a/common/src/states/basic_melee.rs b/common/src/states/basic_melee.rs index fa645229c2..f9fbfeae1a 100644 --- a/common/src/states/basic_melee.rs +++ b/common/src/states/basic_melee.rs @@ -30,7 +30,7 @@ pub struct StaticData { /// Max angle (45.0 will give you a 90.0 angle window) pub max_angle: f32, /// What key is used to press ability - pub ability_key: AbilityKey, + pub ability_info: AbilityInfo, } #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -52,7 +52,7 @@ impl CharacterBehavior for Data { handle_move(data, &mut update, 0.7); handle_jump(data, &mut update); - if !ability_key_is_pressed(data, self.static_data.ability_key) { + if !ability_key_is_pressed(data, self.static_data.ability_info.key) { handle_interrupt(data, &mut update, false); match update.character { CharacterState::BasicMelee(_) => {}, diff --git a/common/src/states/basic_ranged.rs b/common/src/states/basic_ranged.rs index 5de4fa1794..a0b5541578 100644 --- a/common/src/states/basic_ranged.rs +++ b/common/src/states/basic_ranged.rs @@ -23,7 +23,7 @@ pub struct StaticData { pub projectile_gravity: Option, pub projectile_speed: f32, /// What key is used to press ability - pub ability_key: AbilityKey, + pub ability_info: AbilityInfo, /// Whether or not the ability can auto continue pub can_continue: bool, } @@ -50,7 +50,7 @@ impl CharacterBehavior for Data { handle_move(data, &mut update, 0.3); handle_jump(data, &mut update); - if !ability_key_is_pressed(data, self.static_data.ability_key) { + if !ability_key_is_pressed(data, self.static_data.ability_info.key) { handle_interrupt(data, &mut update, false); match update.character { CharacterState::BasicRanged(_) => {}, @@ -103,7 +103,7 @@ impl CharacterBehavior for Data { ..*self }); } else if self.timer < self.static_data.recover_duration { - if ability_key_is_pressed(data, self.static_data.ability_key) { + if ability_key_is_pressed(data, self.static_data.ability_info.key) { // Recovers update.character = CharacterState::BasicRanged(Data { timer: self diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs index ed1e752c75..9fb63b9f33 100644 --- a/common/src/states/charged_melee.rs +++ b/common/src/states/charged_melee.rs @@ -44,7 +44,7 @@ pub struct StaticData { /// How long the state has until exiting pub recover_duration: Duration, /// What key is used to press ability - pub ability_key: AbilityKey, + pub ability_info: AbilityInfo, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -68,7 +68,7 @@ impl CharacterBehavior for Data { handle_move(data, &mut update, 0.7); handle_jump(data, &mut update); - if !ability_key_is_pressed(data, self.static_data.ability_key) { + if !ability_key_is_pressed(data, self.static_data.ability_info.key) { handle_interrupt(data, &mut update, false); match update.character { CharacterState::ChargedMelee(_) => {}, @@ -80,7 +80,7 @@ impl CharacterBehavior for Data { match self.stage_section { StageSection::Charge => { - if ability_key_is_pressed(data, self.static_data.ability_key) + if ability_key_is_pressed(data, self.static_data.ability_info.key) && update.energy.current() as f32 >= self.static_data.energy_cost && self.timer < self.static_data.charge_duration { @@ -107,7 +107,7 @@ impl CharacterBehavior for Data { * self.static_data.speed) as i32, source: EnergySource::Ability, }); - } else if ability_key_is_pressed(data, self.static_data.ability_key) + } else if ability_key_is_pressed(data, self.static_data.ability_info.key) && update.energy.current() as f32 >= self.static_data.energy_cost { // Maintains charge diff --git a/common/src/states/charged_ranged.rs b/common/src/states/charged_ranged.rs index eb96d2907f..29c7ae6dbf 100644 --- a/common/src/states/charged_ranged.rs +++ b/common/src/states/charged_ranged.rs @@ -46,7 +46,7 @@ pub struct StaticData { /// Move speed efficiency pub move_speed: f32, /// What key is used to press ability - pub ability_key: AbilityKey, + pub ability_info: AbilityInfo, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -68,7 +68,7 @@ impl CharacterBehavior for Data { handle_move(data, &mut update, self.static_data.move_speed); handle_jump(data, &mut update); - if !ability_key_is_pressed(data, self.static_data.ability_key) { + if !ability_key_is_pressed(data, self.static_data.ability_info.key) { handle_interrupt(data, &mut update, false); match update.character { CharacterState::ChargedRanged(_) => {}, @@ -99,7 +99,7 @@ impl CharacterBehavior for Data { } }, StageSection::Charge => { - if !ability_key_is_pressed(data, self.static_data.ability_key) && !self.exhausted { + if !ability_key_is_pressed(data, self.static_data.ability_info.key) && !self.exhausted { let charge_frac = (self.timer.as_secs_f32() / self.static_data.charge_duration.as_secs_f32()) .min(1.0); @@ -156,7 +156,7 @@ impl CharacterBehavior for Data { ..*self }); } else if self.timer < self.static_data.charge_duration - && ability_key_is_pressed(data, self.static_data.ability_key) + && ability_key_is_pressed(data, self.static_data.ability_info.key) { // Charges update.character = CharacterState::ChargedRanged(Data { @@ -176,7 +176,7 @@ impl CharacterBehavior for Data { * self.static_data.speed) as i32, source: EnergySource::Ability, }); - } else if ability_key_is_pressed(data, self.static_data.ability_key) { + } else if ability_key_is_pressed(data, self.static_data.ability_info.key) { // Holds charge update.character = CharacterState::ChargedRanged(Data { timer: self diff --git a/common/src/states/combo_melee.rs b/common/src/states/combo_melee.rs index 7c5d88c45a..a16bf7edac 100644 --- a/common/src/states/combo_melee.rs +++ b/common/src/states/combo_melee.rs @@ -98,7 +98,7 @@ pub struct StaticData { /// Whether the state can be interrupted by other abilities pub is_interruptible: bool, /// What key is used to press ability - pub ability_key: AbilityKey, + pub ability_info: AbilityInfo, } /// A sequence of attacks that can incrementally become faster and more /// damaging. @@ -125,7 +125,7 @@ impl CharacterBehavior for Data { handle_orientation(data, &mut update, 1.0); handle_move(data, &mut update, 0.3); - if !ability_key_is_pressed(data, self.static_data.ability_key) { + if !ability_key_is_pressed(data, self.static_data.ability_info.key) { handle_interrupt(data, &mut update, self.static_data.is_interruptible); if let CharacterState::Roll(roll) = &mut update.character { roll.was_combo = Some((self.stage, self.combo)); @@ -259,7 +259,7 @@ impl CharacterBehavior for Data { StageSection::Recover => { if self.timer < self.static_data.stage_data[stage_index].base_recover_duration { // Recovers - if ability_key_is_pressed(data, self.static_data.ability_key) { + if ability_key_is_pressed(data, self.static_data.ability_info.key) { // Checks if state will transition to next stage after recover update.character = CharacterState::ComboMelee(Data { static_data: self.static_data.clone(), diff --git a/common/src/states/dash_melee.rs b/common/src/states/dash_melee.rs index 0f8bce5273..cca2f946c2 100644 --- a/common/src/states/dash_melee.rs +++ b/common/src/states/dash_melee.rs @@ -47,7 +47,7 @@ pub struct StaticData { /// Whether the state can be interrupted by other abilities pub is_interruptible: bool, /// What key is used to press ability - pub ability_key: AbilityKey, + pub ability_info: AbilityInfo, } #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -73,7 +73,7 @@ impl CharacterBehavior for Data { handle_orientation(data, &mut update, 1.0); handle_move(data, &mut update, 0.1); - if !ability_key_is_pressed(data, self.static_data.ability_key) { + if !ability_key_is_pressed(data, self.static_data.ability_info.key) { handle_interrupt(data, &mut update, self.static_data.is_interruptible); match update.character { CharacterState::DashMelee(_) => {}, @@ -97,7 +97,7 @@ impl CharacterBehavior for Data { } else { // Transitions to charge section of stage update.character = CharacterState::DashMelee(Data { - auto_charge: !ability_key_is_pressed(data, self.static_data.ability_key), + auto_charge: !ability_key_is_pressed(data, self.static_data.ability_info.key), timer: Duration::default(), stage_section: StageSection::Charge, ..*self @@ -107,7 +107,7 @@ impl CharacterBehavior for Data { StageSection::Charge => { if (self.static_data.infinite_charge || self.timer < self.static_data.charge_duration) - && (ability_key_is_pressed(data, self.static_data.ability_key) + && (ability_key_is_pressed(data, self.static_data.ability_info.key) || (self.auto_charge && self.timer < self.static_data.charge_duration)) && update.energy.current() > 0 { diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 76779906df..2bd2002e8b 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -36,7 +36,7 @@ pub struct StaticData { /// Affects how high the player leaps pub vertical_leap_strength: f32, /// What key is used to press ability - pub ability_key: AbilityKey, + pub ability_info: AbilityInfo, } #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -58,7 +58,7 @@ impl CharacterBehavior for Data { handle_move(data, &mut update, 0.3); handle_jump(data, &mut update); - if !ability_key_is_pressed(data, self.static_data.ability_key) { + if !ability_key_is_pressed(data, self.static_data.ability_info.key) { handle_interrupt(data, &mut update, false); match update.character { CharacterState::LeapMelee(_) => {}, diff --git a/common/src/states/repeater_ranged.rs b/common/src/states/repeater_ranged.rs index f130016509..9418336ca5 100644 --- a/common/src/states/repeater_ranged.rs +++ b/common/src/states/repeater_ranged.rs @@ -31,7 +31,7 @@ pub struct StaticData { pub projectile_gravity: Option, pub projectile_speed: f32, /// What key is used to press ability - pub ability_key: AbilityKey, + pub ability_info: AbilityInfo, } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -53,7 +53,7 @@ impl CharacterBehavior for Data { handle_move(data, &mut update, 1.0); handle_jump(data, &mut update); - if !ability_key_is_pressed(data, self.static_data.ability_key) { + if !ability_key_is_pressed(data, self.static_data.ability_info.key) { handle_interrupt(data, &mut update, false); match update.character { CharacterState::RepeaterRanged(_) => {}, diff --git a/common/src/states/shockwave.rs b/common/src/states/shockwave.rs index b77c2e283a..42d80b0216 100644 --- a/common/src/states/shockwave.rs +++ b/common/src/states/shockwave.rs @@ -41,7 +41,7 @@ pub struct StaticData { /// Movement speed efficiency pub move_efficiency: f32, /// What key is used to press ability - pub ability_key: AbilityKey, + pub ability_info: AbilityInfo, } #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -60,7 +60,7 @@ impl CharacterBehavior for Data { let mut update = StateUpdate::from(data); handle_move(data, &mut update, self.static_data.move_efficiency); - if !ability_key_is_pressed(data, self.static_data.ability_key) { + if !ability_key_is_pressed(data, self.static_data.ability_info.key) { handle_interrupt(data, &mut update, false); match update.character { CharacterState::Shockwave(_) => {}, diff --git a/common/src/states/spin_melee.rs b/common/src/states/spin_melee.rs index 42f0822afa..418007e000 100644 --- a/common/src/states/spin_melee.rs +++ b/common/src/states/spin_melee.rs @@ -42,7 +42,7 @@ pub struct StaticData { /// Number of spins pub num_spins: u32, /// What key is used to press ability - pub ability_key: AbilityKey, + pub ability_info: AbilityInfo, } #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -75,7 +75,7 @@ impl CharacterBehavior for Data { }, } - if !ability_key_is_pressed(data, self.static_data.ability_key) { + if !ability_key_is_pressed(data, self.static_data.ability_info.key) { handle_interrupt(data, &mut update, self.static_data.is_interruptible); match update.character { CharacterState::SpinMelee(_) => {}, @@ -176,7 +176,7 @@ impl CharacterBehavior for Data { } else if update.energy.current() as f32 >= self.static_data.energy_cost && (self.spins_remaining != 0 || (self.static_data.is_infinite - && ability_key_is_pressed(data, self.static_data.ability_key))) + && ability_key_is_pressed(data, self.static_data.ability_info.key))) { let new_spins_remaining = if self.static_data.is_infinite { self.spins_remaining diff --git a/common/src/states/utils.rs b/common/src/states/utils.rs index 3f97064219..182a54624d 100644 --- a/common/src/states/utils.rs +++ b/common/src/states/utils.rs @@ -1,7 +1,7 @@ use crate::{ comp::{ inventory::slot::EquipSlot, - item::{Hands, ItemKind, Tool}, + item::{Hands, ItemKind, Tool, ToolKind}, quadruped_low, quadruped_medium, theropod, Body, CharacterState, LoadoutManip, StateUpdate, }, consts::{FRIC_GROUND, GRAVITY}, @@ -407,12 +407,12 @@ pub fn handle_ability1_input(data: &JoinData, update: &mut StateUpdate) { .equipped(EquipSlot::Mainhand) .map(|i| &i.item_config_expect().abilities.primary) .map(|a| { - let tool = unwrap_tool_data(data).map(|t| t.kind); + let tool = unwrap_tool_data(data, EquipSlot::Mainhand).map(|t| t.kind); a.clone().adjusted_by_skills(&data.stats.skill_set, tool) }) .filter(|ability| ability.requirements_paid(data, update)) { - update.character = (&ability, AbilityKey::Mouse1).into(); + update.character = (&ability, AbilityInfo::from_key(data, AbilityKey::Mouse1, false)).into(); } } } @@ -450,12 +450,12 @@ pub fn handle_ability2_input(data: &JoinData, update: &mut StateUpdate) { .equipped(equip_slot) .map(|i| &i.item_config_expect().abilities.secondary) .map(|a| { - let tool = unwrap_tool_data(data).map(|t| t.kind); + let tool = unwrap_tool_data(data, equip_slot).map(|t| t.kind); a.clone().adjusted_by_skills(&data.stats.skill_set, tool) }) .filter(|ability| ability.requirements_paid(data, update)) { - update.character = (&ability, AbilityKey::Mouse2).into(); + update.character = (&ability, AbilityInfo::from_key(data, AbilityKey::Mouse2, matches!(equip_slot, EquipSlot::Offhand))).into(); } } } @@ -472,12 +472,12 @@ pub fn handle_ability3_input(data: &JoinData, update: &mut StateUpdate) { .then_some(a) }) .map(|a| { - let tool = unwrap_tool_data(data).map(|t| t.kind); + let tool = unwrap_tool_data(data, EquipSlot::Mainhand).map(|t| t.kind); a.clone().adjusted_by_skills(&data.stats.skill_set, tool) }) .filter(|ability| ability.requirements_paid(data, update)) { - update.character = (&ability, AbilityKey::Skill1).into(); + update.character = (&ability, AbilityInfo::from_key(data, AbilityKey::Skill1, false)).into(); } } } @@ -519,12 +519,12 @@ pub fn handle_ability4_input(data: &JoinData, update: &mut StateUpdate) { .then_some(a) }) .map(|a| { - let tool = unwrap_tool_data(data).map(|t| t.kind); + let tool = unwrap_tool_data(data, equip_slot).map(|t| t.kind); a.clone().adjusted_by_skills(&data.stats.skill_set, tool) }) .filter(|ability| ability.requirements_paid(data, update)) { - update.character = (&ability, AbilityKey::Skill1).into(); + update.character = (&ability, AbilityInfo::from_key(data, AbilityKey::Skill2, matches!(equip_slot, EquipSlot::Offhand))).into(); } } } @@ -546,26 +546,26 @@ pub fn handle_dodge_input(data: &JoinData, update: &mut StateUpdate) { .filter(|ability| ability.requirements_paid(data, update)) { if data.character.is_wield() { - update.character = (&ability, AbilityKey::Dodge).into(); + update.character = (&ability, AbilityInfo::from_key(data, AbilityKey::Dodge, false)).into(); if let CharacterState::Roll(roll) = &mut update.character { roll.was_wielded = true; } } else if data.character.is_stealthy() { - update.character = (&ability, AbilityKey::Dodge).into(); + update.character = (&ability, AbilityInfo::from_key(data, AbilityKey::Dodge, false)).into(); if let CharacterState::Roll(roll) = &mut update.character { roll.was_sneak = true; } } else { - update.character = (&ability, AbilityKey::Dodge).into(); + update.character = (&ability, AbilityInfo::from_key(data, AbilityKey::Dodge, false)).into(); } } } } -pub fn unwrap_tool_data<'a>(data: &'a JoinData) -> Option<&'a Tool> { +pub fn unwrap_tool_data<'a>(data: &'a JoinData, equip_slot: EquipSlot) -> Option<&'a Tool> { if let Some(ItemKind::Tool(tool)) = data .inventory - .equipped(EquipSlot::Mainhand) + .equipped(equip_slot) .map(|i| i.kind()) { Some(&tool) @@ -658,3 +658,47 @@ impl MovementDirection { .unwrap_or_default() } } + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +pub struct AbilityInfo { + pub tool: Option, + pub hand: Option, + pub key: AbilityKey, +} + +impl AbilityInfo { + pub fn from_key(data: &JoinData, key: AbilityKey, from_offhand: bool) -> Self { + let tool_data = if from_offhand { + unwrap_tool_data(data, EquipSlot::Offhand) + } else { + unwrap_tool_data(data, EquipSlot::Mainhand) + }; + let (tool, hand) = if from_offhand { + (tool_data.map(|t| t.kind), Some(HandInfo::OffHand)) + } else { + (tool_data.map(|t| t.kind), tool_data.map(|t| HandInfo::from_main_tool(t))) + }; + + Self { + tool, + hand, + key, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] +pub enum HandInfo { + TwoHanded, + MainHand, + OffHand, +} + +impl HandInfo { + pub fn from_main_tool(tool: &Tool) -> Self { + match tool.hands { + Hands::TwoHand => Self::TwoHanded, + Hands::OneHand => Self::MainHand, + } + } +}