Food now applies buff at end of state.

Rolling and poise states now cancel the potion buff.
UseItem character state now checks that the item in the slot has the same item_definition_id as when the entity enters the state.
Rolling can no cancel item use.
Rolling now checks for move_dir instead of velocity.
This commit is contained in:
Sam 2021-06-26 22:42:44 -05:00
parent c4c183cdf0
commit 8bf3109133
4 changed files with 76 additions and 17 deletions

View File

@ -339,7 +339,7 @@ impl CharacterAbility {
match self { match self {
CharacterAbility::Roll { energy_cost, .. } => { CharacterAbility::Roll { energy_cost, .. } => {
data.physics.on_ground.is_some() data.physics.on_ground.is_some()
&& data.vel.0.xy().magnitude_squared() > 0.5 && data.inputs.move_dir.magnitude_squared() > 0.25
&& update && update
.energy .energy
.try_change_by(-(*energy_cost as i32), EnergySource::Ability) .try_change_by(-(*energy_cost as i32), EnergySource::Ability)

View File

@ -1,9 +1,10 @@
use super::utils::*; use super::utils::*;
use crate::{ use crate::{
comp::{ comp::{
buff::{BuffChange, BuffKind},
inventory::{ inventory::{
item::{ConsumableKind, ItemKind}, item::{ConsumableKind, ItemKind},
slot::Slot, slot::{InvSlotId, Slot},
}, },
CharacterState, InventoryManip, StateUpdate, CharacterState, InventoryManip, StateUpdate,
}, },
@ -14,7 +15,7 @@ use serde::{Deserialize, Serialize};
use std::time::Duration; use std::time::Duration;
/// Separated out to condense update portions of character state /// Separated out to condense update portions of character state
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct StaticData { pub struct StaticData {
/// Buildup to item use /// Buildup to item use
pub buildup_duration: Duration, pub buildup_duration: Duration,
@ -23,7 +24,9 @@ pub struct StaticData {
/// Recovery after item use /// Recovery after item use
pub recover_duration: Duration, pub recover_duration: Duration,
/// Inventory slot to use item from /// Inventory slot to use item from
pub inv_slot: Slot, pub inv_slot: InvSlotId,
/// Item definition id, used to verify that slot still has the correct item
pub item_definition_id: String,
/// Kind of item used /// Kind of item used
pub item_kind: ItemUseKind, pub item_kind: ItemUseKind,
/// Had weapon wielded /// Had weapon wielded
@ -32,7 +35,7 @@ pub struct StaticData {
pub was_sneak: bool, pub was_sneak: bool,
} }
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Data { pub struct Data {
/// Struct containing data that does not change over the course of the /// Struct containing data that does not change over the course of the
/// character state /// character state
@ -58,48 +61,59 @@ impl CharacterBehavior for Data {
}, },
} }
let use_point = match self.static_data.item_kind {
ItemUseKind::Consumable(ConsumableKind::Potion) => UsePoint::BuildupUse,
ItemUseKind::Consumable(ConsumableKind::Food) => UsePoint::UseRecover,
};
match self.stage_section { match self.stage_section {
StageSection::Buildup => { StageSection::Buildup => {
if self.timer < self.static_data.buildup_duration { if self.timer < self.static_data.buildup_duration {
// Build up // Build up
update.character = CharacterState::UseItem(Data { update.character = CharacterState::UseItem(Data {
static_data: self.static_data.clone(),
timer: tick_attack_or_default(data, self.timer, None), timer: tick_attack_or_default(data, self.timer, None),
..*self ..*self
}); });
} else { } else {
// Transitions to use section of stage // Transitions to use section of stage
update.character = CharacterState::UseItem(Data { update.character = CharacterState::UseItem(Data {
static_data: self.static_data.clone(),
timer: Duration::default(), timer: Duration::default(),
stage_section: StageSection::Use, stage_section: StageSection::Use,
..*self
}); });
if let UsePoint::BuildupUse = use_point {
// Create inventory manipulation event // Create inventory manipulation event
let inv_manip = InventoryManip::Use(self.static_data.inv_slot); use_item(data, &mut update, self);
update }
.server_events
.push_front(ServerEvent::InventoryManip(data.entity, inv_manip));
} }
}, },
StageSection::Use => { StageSection::Use => {
if self.timer < self.static_data.use_duration { if self.timer < self.static_data.use_duration {
// Item use // Item use
update.character = CharacterState::UseItem(Data { update.character = CharacterState::UseItem(Data {
static_data: self.static_data.clone(),
timer: tick_attack_or_default(data, self.timer, None), timer: tick_attack_or_default(data, self.timer, None),
..*self ..*self
}); });
} else { } else {
// Transitions to recover section of stage // Transitions to recover section of stage
update.character = CharacterState::UseItem(Data { update.character = CharacterState::UseItem(Data {
static_data: self.static_data.clone(),
timer: Duration::default(), timer: Duration::default(),
stage_section: StageSection::Recover, stage_section: StageSection::Recover,
..*self
}); });
if let UsePoint::UseRecover = use_point {
// Create inventory manipulation event
use_item(data, &mut update, self);
}
} }
}, },
StageSection::Recover => { StageSection::Recover => {
if self.timer < self.static_data.recover_duration { if self.timer < self.static_data.recover_duration {
// Recovery // Recovery
update.character = CharacterState::UseItem(Data { update.character = CharacterState::UseItem(Data {
static_data: self.static_data.clone(),
timer: tick_attack_or_default(data, self.timer, None), timer: tick_attack_or_default(data, self.timer, None),
..*self ..*self
}); });
@ -120,6 +134,17 @@ impl CharacterBehavior for Data {
}, },
} }
// At end of state logic so an interrupt isn't overwritten
handle_state_interrupt(data, &mut update, false);
if matches!(update.character, CharacterState::Roll(_)) {
// Remove potion effect if left the use item state early by rolling
update.server_events.push_front(ServerEvent::Buff {
entity: data.entity,
buff_change: BuffChange::RemoveByKind(BuffKind::Potion),
});
}
update update
} }
} }
@ -138,3 +163,28 @@ impl From<&ItemKind> for Option<ItemUseKind> {
} }
} }
} }
/// Used to control when the item is used in the state
enum UsePoint {
/// Between buildup and use
BuildupUse,
/// Between use and recover
UseRecover,
}
fn use_item(data: &JoinData, update: &mut StateUpdate, state: &Data) {
// Check if the same item is in the slot
let item_is_same = data
.inventory
.get(state.static_data.inv_slot)
.map_or(false, |item| {
item.item_definition_id() == state.static_data.item_definition_id
});
if item_is_same {
// Create inventory manipulation event
let inv_manip = InventoryManip::Use(Slot::Inventory(state.static_data.inv_slot));
update
.server_events
.push_front(ServerEvent::InventoryManip(data.entity, inv_manip));
}
}

View File

@ -576,14 +576,14 @@ pub fn handle_manipulate_loadout(
inv_action: InventoryAction, inv_action: InventoryAction,
) { ) {
use use_item::ItemUseKind; use use_item::ItemUseKind;
if let InventoryAction::Use(Slot::Inventory(slot)) = inv_action { if let InventoryAction::Use(Slot::Inventory(inv_slot)) = inv_action {
// If inventory action is using a slot, and slot is in the inventory // If inventory action is using a slot, and slot is in the inventory
// TODO: Do some non lazy way of handling the possibility that items equipped in // TODO: Do some non lazy way of handling the possibility that items equipped in
// the loadout will have effects that are desired to be non-instantaneous // the loadout will have effects that are desired to be non-instantaneous
if let Some(item_kind) = data if let Some((item_kind, item)) = data
.inventory .inventory
.get(slot) .get(inv_slot)
.and_then(|item| Option::<ItemUseKind>::from(item.kind())) .and_then(|item| Option::<ItemUseKind>::from(item.kind()).zip(Some(item)))
{ {
// (buildup, use, recover) // (buildup, use, recover)
let durations = match item_kind { let durations = match item_kind {
@ -604,8 +604,9 @@ pub fn handle_manipulate_loadout(
buildup_duration: durations.0, buildup_duration: durations.0,
use_duration: durations.1, use_duration: durations.1,
recover_duration: durations.2, recover_duration: durations.2,
inv_slot: Slot::Inventory(slot), inv_slot,
item_kind, item_kind,
item_definition_id: item.item_definition_id().to_string(),
was_wielded: matches!(data.character, CharacterState::Wielding), was_wielded: matches!(data.character, CharacterState::Wielding),
was_sneak: matches!(data.character, CharacterState::Sneak), was_sneak: matches!(data.character, CharacterState::Sneak),
}, },

View File

@ -165,6 +165,14 @@ impl<'a> System<'a> for Sys {
let was_wielded = char_state.get_unchecked().is_wield(); let was_wielded = char_state.get_unchecked().is_wield();
let poise_state = poise.poise_state(); let poise_state = poise.poise_state();
let pos = pos.0; let pos = pos.0;
// Remove potion buff if knocked into poise state
if !matches!(poise_state, PoiseState::Normal) {
use comp::buff::{BuffChange, BuffKind};
server_emitter.emit(ServerEvent::Buff {
entity,
buff_change: BuffChange::RemoveByKind(BuffKind::Potion),
});
}
match poise_state { match poise_state {
PoiseState::Normal => {}, PoiseState::Normal => {},
PoiseState::Interrupted => { PoiseState::Interrupted => {