Ability sets now start with non-empty defaults.

This commit is contained in:
Sam
2021-11-27 23:32:24 -05:00
parent 4c3771a1a0
commit d0e487da8a
9 changed files with 125 additions and 83 deletions

View File

@ -74,41 +74,57 @@ impl ActiveAbilities {
slot: usize, slot: usize,
auxiliary_key: AuxiliaryKey, auxiliary_key: AuxiliaryKey,
new_ability: AuxiliaryAbility, new_ability: AuxiliaryAbility,
inventory: Option<&Inventory>,
skill_set: Option<&SkillSet>,
) { ) {
let auxiliary_set = self let auxiliary_set = self
.auxiliary_sets .auxiliary_sets
.entry(auxiliary_key) .entry(auxiliary_key)
.or_insert([AuxiliaryAbility::Empty; 5]); .or_insert(Self::default_ability_set(inventory, skill_set));
if let Some(ability) = auxiliary_set.get_mut(slot) { if let Some(ability) = auxiliary_set.get_mut(slot) {
*ability = new_ability; *ability = new_ability;
} }
} }
pub fn get_ability(&self, input: AbilityInput, inventory: Option<&Inventory>) -> Ability { pub fn auxiliary_set(
&self,
inv: Option<&Inventory>,
skill_set: Option<&SkillSet>,
) -> [AuxiliaryAbility; MAX_ABILITIES] {
let tool_kind = |slot| {
inv.and_then(|inv| 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)
.copied()
.unwrap_or_else(|| Self::default_ability_set(inv, skill_set))
}
pub fn get_ability(
&self,
input: AbilityInput,
inventory: Option<&Inventory>,
skill_set: Option<&SkillSet>,
) -> 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) => inventory AbilityInput::Auxiliary(index) => self
.and_then(|inv| { .auxiliary_set(inventory, skill_set)
let tool_kind = |slot| { .get(index)
inv.equipped(slot).and_then(|item| match item.kind() { .copied()
ItemKind::Tool(tool) => Some(tool.kind), .map(|a| a.into())
_ => 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),
} }
} }
@ -123,7 +139,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, inv); let ability = self.get_ability(input, inv, Some(skill_set));
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))
@ -193,44 +209,27 @@ impl ActiveAbilities {
}) })
} }
pub fn iter_aux_abilities<'a>( pub fn default_ability_set<'a>(
&'a self, inv: Option<&'a Inventory>,
inventory: Option<&'a Inventory>, skill_set: Option<&'a SkillSet>,
) -> impl Iterator<Item = &AuxiliaryAbility> + 'a { ) -> [AuxiliaryAbility; MAX_ABILITIES] {
inventory let mut iter = Self::iter_unlocked_abilities(inv, skill_set, EquipSlot::ActiveMainhand)
.and_then(|inv| { .map(AuxiliaryAbility::MainWeapon)
let tool_kind = |slot| { .chain(
inv.equipped(slot).and_then(|item| match item.kind() { Self::iter_unlocked_abilities(inv, skill_set, EquipSlot::ActiveOffhand)
ItemKind::Tool(tool) => Some(tool.kind), .map(AuxiliaryAbility::OffWeapon),
_ => None, );
})
};
let aux_key = ( let mut array = [AuxiliaryAbility::Empty; MAX_ABILITIES];
tool_kind(EquipSlot::ActiveMainhand),
tool_kind(EquipSlot::ActiveOffhand),
);
self.auxiliary_sets.get(&aux_key) for ability in array.iter_mut() {
}) if let Some(available_ability) = iter.next() {
.into_iter() *ability = available_ability;
.flatten() }
}
array
} }
// TODO: Maybe keep this for autopopulating a new combination of weapons?
// pub fn auto_update(&mut self, inv: Option<&Inventory>, skill_set:
// Option<&SkillSet>) { let main_abilities =
// Self::iter_unlocked_abilities(inv, skill_set, EquipSlot::ActiveMainhand)
// .map(AuxiliaryAbility::MainWeapon);
// let off_abilities = Self::iter_unlocked_abilities(inv, skill_set,
// EquipSlot::ActiveOffhand) .map(AuxiliaryAbility::OffWeapon);
// (0..MAX_ABILITIES)
// .zip(main_abilities.chain(off_abilities))
// .for_each(|(i, ability)| {
// self.change_ability(i, ability);
// })
// }
} }
pub enum AbilityInput { pub enum AbilityInput {

View File

@ -204,6 +204,12 @@ pub enum ServerEvent {
EntityAttackedHook { EntityAttackedHook {
entity: EcsEntity, entity: EcsEntity,
}, },
ChangeAbility {
entity: EcsEntity,
slot: usize,
auxiliary_key: comp::ability::AuxiliaryKey,
new_ability: comp::ability::AuxiliaryAbility,
},
} }
pub struct EventBus<E> { pub struct EventBus<E> {

View File

@ -1,7 +1,7 @@
use common::{ use common::{
comp::{ comp::{
agent::{Sound, SoundKind}, agent::{Sound, SoundKind},
ActiveAbilities, Body, BuffChange, ControlEvent, Controller, Pos, Body, BuffChange, ControlEvent, Controller, Pos,
}, },
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
uid::UidAllocator, uid::UidAllocator,
@ -27,20 +27,13 @@ pub struct ReadData<'a> {
pub struct Sys; pub struct Sys;
impl<'a> System<'a> for Sys { impl<'a> System<'a> for Sys {
type SystemData = ( type SystemData = (ReadData<'a>, WriteStorage<'a, Controller>);
ReadData<'a>,
WriteStorage<'a, Controller>,
WriteStorage<'a, ActiveAbilities>,
);
const NAME: &'static str = "controller"; const NAME: &'static str = "controller";
const ORIGIN: Origin = Origin::Common; const ORIGIN: Origin = Origin::Common;
const PHASE: Phase = Phase::Create; const PHASE: Phase = Phase::Create;
fn run( fn run(_job: &mut Job<Self>, (read_data, mut controllers): Self::SystemData) {
_job: &mut Job<Self>,
(read_data, mut controllers, mut active_abilities): Self::SystemData,
) {
let mut server_emitter = read_data.server_bus.emitter(); let mut server_emitter = read_data.server_bus.emitter();
for (entity, controller) in (&read_data.entities, &mut controllers).join() { for (entity, controller) in (&read_data.entities, &mut controllers).join() {
@ -115,9 +108,12 @@ impl<'a> System<'a> for Sys {
auxiliary_key, auxiliary_key,
new_ability, new_ability,
} => { } => {
if let Some(mut active_abilities) = active_abilities.get_mut(entity) { server_emitter.emit(ServerEvent::ChangeAbility {
active_abilities.change_ability(slot, auxiliary_key, new_ability); entity,
} slot,
auxiliary_key,
new_ability,
});
}, },
} }
} }

View File

@ -1,6 +1,7 @@
use crate::{ use crate::{
client::Client, client::Client,
comp::{ comp::{
ability,
agent::{Agent, AgentEvent, Sound, SoundKind}, agent::{Agent, AgentEvent, Sound, SoundKind},
skillset::SkillGroupKind, skillset::SkillGroupKind,
BuffKind, BuffSource, PhysicsState, BuffKind, BuffSource, PhysicsState,
@ -1260,3 +1261,26 @@ pub fn handle_entity_attacked_hook(server: &Server, entity: EcsEntity) {
buff_change: buff::BuffChange::RemoveByKind(buff::BuffKind::Saturation), buff_change: buff::BuffChange::RemoveByKind(buff::BuffKind::Saturation),
}); });
} }
pub fn handle_change_ability(
server: &Server,
entity: EcsEntity,
slot: usize,
auxiliary_key: ability::AuxiliaryKey,
new_ability: ability::AuxiliaryAbility,
) {
let ecs = &server.state.ecs();
let inventories = ecs.read_storage::<comp::Inventory>();
let skill_sets = ecs.read_storage::<comp::SkillSet>();
if let Some(mut active_abilities) = ecs.write_storage::<comp::ActiveAbilities>().get_mut(entity)
{
active_abilities.change_ability(
slot,
auxiliary_key,
new_ability,
inventories.get(entity),
skill_sets.get(entity),
);
}
}

View File

@ -6,10 +6,10 @@ use entity_creation::{
handle_initialize_character, handle_loaded_character_data, handle_shockwave, handle_shoot, handle_initialize_character, handle_loaded_character_data, handle_shockwave, handle_shoot,
}; };
use entity_manipulation::{ use entity_manipulation::{
handle_aura, handle_bonk, handle_buff, handle_combo_change, handle_delete, handle_destroy, handle_aura, handle_bonk, handle_buff, handle_change_ability, handle_combo_change,
handle_energy_change, handle_entity_attacked_hook, handle_explosion, handle_health_change, handle_delete, handle_destroy, handle_energy_change, handle_entity_attacked_hook,
handle_knockback, handle_land_on_ground, handle_parry, handle_poise, handle_respawn, handle_explosion, handle_health_change, handle_knockback, handle_land_on_ground, handle_parry,
handle_teleport_to, handle_poise, handle_respawn, handle_teleport_to,
}; };
use group_manip::handle_group; use group_manip::handle_group;
use information::handle_site_info; use information::handle_site_info;
@ -233,6 +233,12 @@ impl Server {
ServerEvent::EntityAttackedHook { entity } => { ServerEvent::EntityAttackedHook { entity } => {
handle_entity_attacked_hook(self, entity) handle_entity_attacked_hook(self, entity)
}, },
ServerEvent::ChangeAbility {
entity,
slot,
auxiliary_key,
new_ability,
} => handle_change_ability(self, entity, slot, auxiliary_key, new_ability),
} }
} }

View File

@ -780,7 +780,11 @@ 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), Some(self.inventory)) .get_ability(
AbilityInput::Auxiliary(i),
Some(self.inventory),
Some(self.skill_set),
)
.ability_id(Some(self.inventory)); .ability_id(Some(self.inventory));
let image_size = 50.0; let image_size = 50.0;

View File

@ -1,5 +1,5 @@
use crate::hud::item_imgs::ItemKey; use crate::hud::item_imgs::ItemKey;
use common::comp::inventory::item::Item; use common::comp::{self, inventory::item::Item};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
@ -70,7 +70,14 @@ 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
.iter_aux_abilities(client.inventories().get(client.entity())) .auxiliary_set(
client.inventories().get(client.entity()),
client
.state()
.read_storage::<comp::SkillSet>()
.get(client.entity()),
)
.iter()
.enumerate() .enumerate()
.zip(self.slots.iter_mut()) .zip(self.slots.iter_mut())
{ {

View File

@ -601,14 +601,14 @@ impl<'a> Skillbar<'a> {
// Helper // Helper
let tooltip_text = |slot| { let tooltip_text = |slot| {
let (hotbar, inventory, _, _, active_abilities, _) = content_source; let (hotbar, inventory, _, skill_set, active_abilities, _) = content_source;
hotbar.get(slot).and_then(|content| match content { hotbar.get(slot).and_then(|content| match content {
hotbar::SlotContents::Inventory(i, _) => inventory hotbar::SlotContents::Inventory(i, _) => inventory
.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
.iter_aux_abilities(Some(inventory)) .auxiliary_set(Some(inventory), Some(skill_set))
.nth(i) .get(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
.iter_aux_abilities(Some(inventory)) .auxiliary_set(Some(inventory), Some(skillset))
.nth(i) .get(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