Added in-memory persistence of ability sets per weapon kind pair.

This commit is contained in:
Sam 2021-11-25 23:30:28 -05:00
parent 89bf41574c
commit d86692c4fe
9 changed files with 96 additions and 30 deletions

View File

@ -9,15 +9,6 @@
// TODO: Remove these // TODO: Remove these
(Some(Axe(UnlockLeap)), "common.abilities.axe.leap"), (Some(Axe(UnlockLeap)), "common.abilities.axe.leap"),
(Some(Hammer(UnlockLeap)), "common.abilities.hammer.leap"), (Some(Hammer(UnlockLeap)), "common.abilities.hammer.leap"),
(Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"),
(Some(Staff(UnlockShockwave)), "common.abilities.staff.fireshockwave"),
(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
(Some(Sword(UnlockSpin)), "common.abilities.sword.spin"),
(Some(Axe(UnlockLeap)), "common.abilities.axe.leap"),
(Some(Hammer(UnlockLeap)), "common.abilities.hammer.leap"),
(Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"),
(Some(Staff(UnlockShockwave)), "common.abilities.staff.fireshockwave"),
(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
], ],
), ),
Tool(Axe): ( Tool(Axe): (
@ -39,6 +30,9 @@
secondary: "common.abilities.bow.repeater", secondary: "common.abilities.bow.repeater",
abilities: [ abilities: [
(Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"), (Some(Bow(UnlockShotgun)), "common.abilities.bow.shotgun"),
// TODO: Remove these
(Some(Staff(UnlockShockwave)), "common.abilities.staff.fireshockwave"),
(Some(Sceptre(UnlockAura)), "common.abilities.sceptre.wardingaura"),
], ],
), ),
Tool(Staff): ( Tool(Staff): (

View File

@ -27,7 +27,7 @@ use common::{
group, group,
invite::{InviteKind, InviteResponse}, invite::{InviteKind, InviteResponse},
skills::Skill, skills::Skill,
slot::{InvSlotId, Slot}, slot::{EquipSlot, InvSlotId, Slot},
CharacterState, ChatMode, ControlAction, ControlEvent, Controller, ControllerInputs, CharacterState, ChatMode, ControlAction, ControlEvent, Controller, ControllerInputs,
GroupManip, InputKind, InventoryAction, InventoryEvent, InventoryUpdateEvent, GroupManip, InputKind, InventoryAction, InventoryEvent, InventoryUpdateEvent,
UtteranceKind, UtteranceKind,
@ -1404,8 +1404,26 @@ impl Client {
} }
pub fn change_ability(&mut self, slot: usize, new_ability: comp::ability::AuxiliaryAbility) { pub fn change_ability(&mut self, slot: usize, new_ability: comp::ability::AuxiliaryAbility) {
let auxiliary_key = self
.inventories()
.get(self.entity())
.map_or((None, None), |inv| {
let tool_kind = |slot| {
inv.equipped(slot).and_then(|item| match item.kind() {
comp::item::ItemKind::Tool(tool) => Some(tool.kind),
_ => None,
})
};
(
tool_kind(EquipSlot::ActiveMainhand),
tool_kind(EquipSlot::ActiveOffhand),
)
});
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::ChangeAbility { self.send_msg(ClientGeneral::ControlEvent(ControlEvent::ChangeAbility {
slot, slot,
auxiliary_key,
new_ability, new_ability,
})) }))
} }

View File

@ -25,12 +25,14 @@ use crate::{
}, },
terrain::SpriteKind, terrain::SpriteKind,
}; };
use hashbrown::HashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use specs::{Component, DerefFlaggedStorage}; use specs::{Component, DerefFlaggedStorage};
use specs_idvs::IdvStorage; use specs_idvs::IdvStorage;
use std::{convert::TryFrom, time::Duration}; use std::{convert::TryFrom, time::Duration};
pub const MAX_ABILITIES: usize = 5; pub const MAX_ABILITIES: usize = 5;
pub type AuxiliaryKey = (Option<ToolKind>, Option<ToolKind>);
// TODO: Potentially look into storing previous ability sets for weapon // TODO: Potentially look into storing previous ability sets for weapon
// combinations and automatically reverting back to them on switching to that // combinations and automatically reverting back to them on switching to that
@ -41,7 +43,7 @@ pub struct ActiveAbilities {
pub primary: PrimaryAbility, pub primary: PrimaryAbility,
pub secondary: SecondaryAbility, pub secondary: SecondaryAbility,
pub movement: MovementAbility, pub movement: MovementAbility,
pub abilities: [AuxiliaryAbility; MAX_ABILITIES], pub auxiliary_sets: HashMap<AuxiliaryKey, [AuxiliaryAbility; MAX_ABILITIES]>,
} }
impl Component for ActiveAbilities { impl Component for ActiveAbilities {
@ -54,7 +56,7 @@ impl Default for ActiveAbilities {
primary: PrimaryAbility::Tool, primary: PrimaryAbility::Tool,
secondary: SecondaryAbility::Tool, secondary: SecondaryAbility::Tool,
movement: MovementAbility::Species, movement: MovementAbility::Species,
abilities: [AuxiliaryAbility::Empty; MAX_ABILITIES], auxiliary_sets: HashMap::new(),
} }
} }
} }
@ -68,22 +70,46 @@ impl ActiveAbilities {
Self::default() Self::default()
} }
pub fn change_ability(&mut self, slot: usize, new_ability: AuxiliaryAbility) { pub fn change_ability(
if let Some(ability) = self.abilities.get_mut(slot) { &mut self,
slot: usize,
auxiliary_key: AuxiliaryKey,
new_ability: AuxiliaryAbility,
) {
let auxiliary_set = self
.auxiliary_sets
.entry(auxiliary_key)
.or_insert([AuxiliaryAbility::Empty; 5]);
if let Some(ability) = auxiliary_set.get_mut(slot) {
*ability = new_ability; *ability = new_ability;
} }
} }
pub fn get_ability(&self, input: AbilityInput) -> Ability { pub fn get_ability(&self, input: AbilityInput, inventory: Option<&Inventory>) -> Ability {
match input { match input {
AbilityInput::Primary => self.primary.into(), AbilityInput::Primary => self.primary.into(),
AbilityInput::Secondary => self.secondary.into(), AbilityInput::Secondary => self.secondary.into(),
AbilityInput::Movement => self.movement.into(), AbilityInput::Movement => self.movement.into(),
AbilityInput::Auxiliary(index) => self AbilityInput::Auxiliary(index) => inventory
.abilities .and_then(|inv| {
.get(index) let tool_kind = |slot| {
.copied() inv.equipped(slot).and_then(|item| match item.kind() {
.map(|a| a.into()) ItemKind::Tool(tool) => Some(tool.kind),
_ => None,
})
};
let aux_key = (
tool_kind(EquipSlot::ActiveMainhand),
tool_kind(EquipSlot::ActiveOffhand),
);
self.auxiliary_sets
.get(&aux_key)
.and_then(|entry| entry.get(index))
.copied()
.map(|a| a.into())
})
.unwrap_or(Ability::Empty), .unwrap_or(Ability::Empty),
} }
} }
@ -98,7 +124,7 @@ impl ActiveAbilities {
body: Option<&Body>, body: Option<&Body>,
// bool is from_offhand // bool is from_offhand
) -> Option<(CharacterAbility, bool)> { ) -> Option<(CharacterAbility, bool)> {
let ability = self.get_ability(input); let ability = self.get_ability(input, inv);
let ability_set = |equip_slot| { let ability_set = |equip_slot| {
inv.and_then(|inv| inv.equipped(equip_slot)) inv.and_then(|inv| inv.equipped(equip_slot))
@ -168,6 +194,30 @@ impl ActiveAbilities {
}) })
} }
pub fn iter_aux_abilities<'a>(
&'a self,
inventory: Option<&'a Inventory>,
) -> impl Iterator<Item = &AuxiliaryAbility> + 'a {
inventory
.and_then(|inv| {
let tool_kind = |slot| {
inv.equipped(slot).and_then(|item| match item.kind() {
ItemKind::Tool(tool) => Some(tool.kind),
_ => None,
})
};
let aux_key = (
tool_kind(EquipSlot::ActiveMainhand),
tool_kind(EquipSlot::ActiveOffhand),
);
self.auxiliary_sets.get(&aux_key)
})
.into_iter()
.flatten()
}
// TODO: Maybe keep this for autopopulating a new combination of weapons? // TODO: Maybe keep this for autopopulating a new combination of weapons?
// pub fn auto_update(&mut self, inv: Option<&Inventory>, skill_set: // pub fn auto_update(&mut self, inv: Option<&Inventory>, skill_set:
// Option<&SkillSet>) { let main_abilities = // Option<&SkillSet>) { let main_abilities =

View File

@ -138,6 +138,7 @@ pub enum ControlEvent {
Utterance(UtteranceKind), Utterance(UtteranceKind),
ChangeAbility { ChangeAbility {
slot: usize, slot: usize,
auxiliary_key: ability::AuxiliaryKey,
new_ability: ability::AuxiliaryAbility, new_ability: ability::AuxiliaryAbility,
}, },
} }

View File

@ -110,9 +110,13 @@ impl<'a> System<'a> for Sys {
server_emitter.emit(ServerEvent::Sound { sound }); server_emitter.emit(ServerEvent::Sound { sound });
} }
}, },
ControlEvent::ChangeAbility { slot, new_ability } => { ControlEvent::ChangeAbility {
slot,
auxiliary_key,
new_ability,
} => {
if let Some(mut active_abilities) = active_abilities.get_mut(entity) { if let Some(mut active_abilities) = active_abilities.get_mut(entity) {
active_abilities.change_ability(slot, new_ability); active_abilities.change_ability(slot, auxiliary_key, new_ability);
} }
}, },
} }

View File

@ -780,7 +780,7 @@ impl<'a> Widget for Diary<'a> {
for i in 0..MAX_ABILITIES { for i in 0..MAX_ABILITIES {
let ability_id = self let ability_id = self
.active_abilities .active_abilities
.get_ability(AbilityInput::Auxiliary(i)) .get_ability(AbilityInput::Auxiliary(i), Some(self.inventory))
.ability_id(Some(self.inventory)); .ability_id(Some(self.inventory));
let image_size = 50.0; let image_size = 50.0;

View File

@ -70,8 +70,7 @@ impl State {
{ {
use common::comp::ability::AuxiliaryAbility; use common::comp::ability::AuxiliaryAbility;
for ((i, ability), hotbar_slot) in active_abilities for ((i, ability), hotbar_slot) in active_abilities
.abilities .iter_aux_abilities(client.inventories().get(client.entity()))
.iter()
.enumerate() .enumerate()
.zip(self.slots.iter_mut()) .zip(self.slots.iter_mut())
{ {

View File

@ -607,8 +607,8 @@ impl<'a> Skillbar<'a> {
.get_by_hash(i) .get_by_hash(i)
.map(|item| (item.name(), item.description())), .map(|item| (item.name(), item.description())),
hotbar::SlotContents::Ability(i) => active_abilities hotbar::SlotContents::Ability(i) => active_abilities
.abilities .iter_aux_abilities(Some(inventory))
.get(i) .nth(i)
.and_then(|a| Ability::from(*a).ability_id(Some(inventory))) .and_then(|a| Ability::from(*a).ability_id(Some(inventory)))
.map(util::ability_description), .map(util::ability_description),
}) })

View File

@ -141,8 +141,8 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
}, },
hotbar::SlotContents::Ability(i) => { hotbar::SlotContents::Ability(i) => {
let ability_id = active_abilities let ability_id = active_abilities
.abilities .iter_aux_abilities(Some(inventory))
.get(i) .nth(i)
.and_then(|a| Ability::from(*a).ability_id(Some(inventory))); .and_then(|a| Ability::from(*a).ability_id(Some(inventory)));
ability_id ability_id