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 37c6fe9ee8
commit 8a9de26132
4 changed files with 76 additions and 17 deletions

View File

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

View File

@ -1,9 +1,10 @@
use super::utils::*;
use crate::{
comp::{
buff::{BuffChange, BuffKind},
inventory::{
item::{ConsumableKind, ItemKind},
slot::Slot,
slot::{InvSlotId, Slot},
},
CharacterState, InventoryManip, StateUpdate,
},
@ -14,7 +15,7 @@ use serde::{Deserialize, Serialize};
use std::time::Duration;
/// 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 {
/// Buildup to item use
pub buildup_duration: Duration,
@ -23,7 +24,9 @@ pub struct StaticData {
/// Recovery after item use
pub recover_duration: Duration,
/// 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
pub item_kind: ItemUseKind,
/// Had weapon wielded
@ -32,7 +35,7 @@ pub struct StaticData {
pub was_sneak: bool,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Data {
/// Struct containing data that does not change over the course of the
/// 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 {
StageSection::Buildup => {
if self.timer < self.static_data.buildup_duration {
// Build up
update.character = CharacterState::UseItem(Data {
static_data: self.static_data.clone(),
timer: tick_attack_or_default(data, self.timer, None),
..*self
});
} else {
// Transitions to use section of stage
update.character = CharacterState::UseItem(Data {
static_data: self.static_data.clone(),
timer: Duration::default(),
stage_section: StageSection::Use,
..*self
});
// Create inventory manipulation event
let inv_manip = InventoryManip::Use(self.static_data.inv_slot);
update
.server_events
.push_front(ServerEvent::InventoryManip(data.entity, inv_manip));
if let UsePoint::BuildupUse = use_point {
// Create inventory manipulation event
use_item(data, &mut update, self);
}
}
},
StageSection::Use => {
if self.timer < self.static_data.use_duration {
// Item use
update.character = CharacterState::UseItem(Data {
static_data: self.static_data.clone(),
timer: tick_attack_or_default(data, self.timer, None),
..*self
});
} else {
// Transitions to recover section of stage
update.character = CharacterState::UseItem(Data {
static_data: self.static_data.clone(),
timer: Duration::default(),
stage_section: StageSection::Recover,
..*self
});
if let UsePoint::UseRecover = use_point {
// Create inventory manipulation event
use_item(data, &mut update, self);
}
}
},
StageSection::Recover => {
if self.timer < self.static_data.recover_duration {
// Recovery
update.character = CharacterState::UseItem(Data {
static_data: self.static_data.clone(),
timer: tick_attack_or_default(data, self.timer, None),
..*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
}
}
@ -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,
) {
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
// 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
if let Some(item_kind) = data
if let Some((item_kind, item)) = data
.inventory
.get(slot)
.and_then(|item| Option::<ItemUseKind>::from(item.kind()))
.get(inv_slot)
.and_then(|item| Option::<ItemUseKind>::from(item.kind()).zip(Some(item)))
{
// (buildup, use, recover)
let durations = match item_kind {
@ -604,8 +604,9 @@ pub fn handle_manipulate_loadout(
buildup_duration: durations.0,
use_duration: durations.1,
recover_duration: durations.2,
inv_slot: Slot::Inventory(slot),
inv_slot,
item_kind,
item_definition_id: item.item_definition_id().to_string(),
was_wielded: matches!(data.character, CharacterState::Wielding),
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 poise_state = poise.poise_state();
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 {
PoiseState::Normal => {},
PoiseState::Interrupted => {