Merge branch 'potion-animations' into 'master'

Consumable Animations

See merge request veloren/veloren!2512
This commit is contained in:
Samuel Keiffer 2021-06-29 16:36:41 +00:00
commit 0151e5aeed
53 changed files with 515 additions and 97 deletions

View File

@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Players can press H to greet others
- Ability to toggle chat visibility
- Added gem rings with various stat improvements.
- Animations for using consumables.
### Changed

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Potent Potion",
description: "A potent healing potion.",
kind: Consumable(
kind: "Potion",
kind: Potion,
effect: [
Buff((
kind: Potion,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Large Potion",
description: "",
kind: Consumable(
kind: "PotionLarge",
kind: Potion,
effect: [
Buff((
kind: Potion,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Medium Potion",
description: "",
kind: Consumable(
kind: "PotionMed",
kind: Potion,
effect: [
Buff((
kind: Potion,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Minor Potion",
description: "",
kind: Consumable(
kind: "PotionMinor",
kind: Potion,
effect: [
Buff((
kind: Potion,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Apple",
description: "Red and juicy",
kind: Consumable(
kind: "Apple",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Mushroom Curry",
description: "Who could say no to that?",
kind: Consumable(
kind: "AppleShroomCurry",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Apple Stick",
description: "The stick makes it easier to carry!",
kind: Consumable(
kind: "AppleStick",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Cactus Colada",
description: "Giving you that special prickle.",
kind: Consumable(
kind: "CactusColada",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Carrot",
description: "An orange root vegetable. They say it'll improve your vision!",
kind: Consumable(
kind: "Carrot",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Dwarven Cheese",
description: "Aromatic and nutritious",
kind: Consumable(
kind: "Cheese",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Coconut",
description: "Reliable source of water and fat.\n\nNaturally growing at the top of palm trees.",
kind: Consumable(
kind: "Coconut",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Coltsfoot",
description: "A daisy-like flower often used in herbal teas.",
kind: Consumable(
kind: "Coltsfoot",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Dandelion",
description: "A small, yellow flower. Uses the wind to spread its seeds.",
kind: Consumable(
kind: "Dandelion",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Garlic",
description: "Make sure to brush your teeth after eating.",
kind: Consumable(
kind: "Garlic",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Lettuce",
description: "A vibrant green leafy vegetable. Lettuce make some salads!",
kind: Consumable(
kind: "Lettuce",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Meat",
description: "Meat. The lifeblood of mankind.",
kind: Consumable(
kind: "Meat",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Cooked Meat Slab",
description: "Medium Rare.",
kind: Consumable(
kind: "BeastLargeCooked",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Raw Meat Slab",
description: "Chunk of beastly animal meat, best after cooking.",
kind: Consumable(
kind: "BeastLargeRaw",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Cooked Meat Sliver",
description: "Medium Rare.",
kind: Consumable(
kind: "BeastSmallCooked",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Raw Meat Sliver",
description: "Small hunk of beastly animal meat, best after cooking.",
kind: Consumable(
kind: "BeastSmallRaw",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Cooked Bird Meat",
description: "Best enjoyed with one in each hand.",
kind: Consumable(
kind: "BirdCooked",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Huge Cooked Drumstick",
description: "Makes for a legendary meal.",
kind: Consumable(
kind: "BirdLargeCooked",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Huge Raw Drumstick",
description: "It's magificent.",
kind: Consumable(
kind: "BirdLargeRaw",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Raw Bird Meat",
description: "A hefty drumstick.",
kind: Consumable(
kind: "BirdRaw",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Cooked Fish",
description: "A fresh cooked seafood steak.",
kind: Consumable(
kind: "FishCooked",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Raw Fish",
description: "A steak chopped from a fish, best after cooking.",
kind: Consumable(
kind: "FishRaw",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Cooked Tough Meat",
description: "Tastes exotic.",
kind: Consumable(
kind: "ToughCooked",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Raw Tough Meat",
description: "Peculiar bit of meat, best after cooking.",
kind: Consumable(
kind: "ToughRaw",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Mushroom",
description: "Hopefully this one is not poisonous",
kind: Consumable(
kind: "Mushroom",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Mushroom Stick",
description: "Roasted mushrooms on a stick for easy carrying",
kind: Consumable(
kind: "MushroomStick",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Onion",
description: "A vegetable that's made the toughest men cry.",
kind: Consumable(
kind: "Onion",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Plain Salad",
description: "Literally just chopped lettuce. Does this even count as a salad?",
kind: Consumable(
kind: "PlainSalad",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Sage",
description: "A herb commonly used in tea.",
kind: Consumable(
kind: "Sage",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Sunflower Ice Tea",
description: "Brewed from freshly shelled sunflower seeds",
kind: Consumable(
kind: "SunflowerTea",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Tomato",
description: "A red fruit. Not actually a vegetable.",
kind: Consumable(
kind: "Tomato",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2,7 +2,7 @@ ItemDef(
name: "Tomato Salad",
description: "Leafy salad with some chopped, juicy tomatoes mixed in.",
kind: Consumable(
kind: "TomatoSalad",
kind: Food,
effect: [
Buff((
kind: Saturation,

View File

@ -2235,40 +2235,36 @@
"element.items.item_bag_blue",
),
// Consumables
Consumable("Apple"): Png(
Consumable("common.items.food.apple"): Png(
"element.items.item_apple",
),
Consumable("Coconut"): Png(
Consumable("common.items.food.coconut"): Png(
"element.items.item_coconut",
),
Consumable("CactusColada"): VoxTrans(
Consumable("common.items.food.cactus_colada"): VoxTrans(
"voxel.object.cactus_drink",
(-1.0, 1.0, 0.0), (-50.0, 30.0, 20.0), 0.8,
),
Consumable("PotionMed"): VoxTrans(
Consumable("common.items.consumable.potion_med"): VoxTrans(
"voxel.object.potion_red",
(0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.7,
),
Consumable("PotionMinor"): VoxTrans(
Consumable("common.items.consumable.potion_minor"): VoxTrans(
"voxel.object.potion_red",
(0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.5,
),
Consumable("PotionLarge"): VoxTrans(
Consumable("common.items.consumable.potion_big"): VoxTrans(
"voxel.object.potion_red",
(0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.9,
),
Ingredient("PotionExp"): VoxTrans(
"voxel.object.potion_turq",
(0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.8,
Consumable("common.items.boss_drops.potions"): VoxTrans(
"voxel.object.potion_red",
(0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.9,
),
Consumable("Cheese"): Png(
Consumable("common.items.food.cheese"): Png(
"element.items.item_cheese",
),
Consumable("Potion"): VoxTrans(
"voxel.object.potion_red",
(0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 1.0,
),
Consumable("Mushroom"): VoxTrans(
Consumable("common.items.food.mushroom"): VoxTrans(
"voxel.sprite.mushrooms.mushroom-10",
(0.0, 0.0, 0.0), (-50.0, 70.0, 40.0), 1.0,
),
@ -2280,83 +2276,83 @@
"voxel.sprite.velorite.velorite_1",
(0.0, 0.0, 0.0), (-50.0, 40.0, 20.0), 0.8,
),
Consumable("AppleShroomCurry"): Png(
Consumable("common.items.food.apple_mushroom_curry"): Png(
"element.items.item_apple_curry",
),
Consumable("AppleStick"): Png(
Consumable("common.items.food.apple_stick"): Png(
"element.items.item_apple_stick",
),
Consumable("MushroomStick"): Png(
Consumable("common.items.food.mushroom_stick"): Png(
"element.items.item_shroom_stick",
),
Consumable("SunflowerTea"): Png(
Consumable("common.items.food.sunflower_icetea"): Png(
"element.items.item_sunflower_tea",
),
Consumable("Carrot"): VoxTrans(
Consumable("common.items.food.carrot"): VoxTrans(
"voxel.sprite.carrot.carrot",
(0.0, 0.0, 0.0), (-20.0, 10.0, 20.0), 0.9,
),
Consumable("Tomato"): VoxTrans(
Consumable("common.items.food.tomato"): VoxTrans(
"voxel.sprite.tomato.tomato",
(0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.8,
),
Consumable("Lettuce"): VoxTrans(
Consumable("common.items.food.lettuce"): VoxTrans(
"voxel.sprite.cabbage.cabbage",
(0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.8,
),
Consumable("FishRaw"): VoxTrans(
Consumable("common.items.food.meat.fish_raw"): VoxTrans(
"voxel.sprite.food.meat.fish_raw",
(0.0, 0.0, 0.0), (-20.0, 10.0, 20.0), 0.9,
),
Consumable("FishCooked"): VoxTrans(
Consumable("common.items.food.meat.fish_cooked"): VoxTrans(
"voxel.sprite.food.meat.fish_cooked",
(0.0, 0.0, 0.0), (-20.0, 10.0, 20.0), 0.9,
),
Consumable("BirdRaw"): VoxTrans(
Consumable("common.items.food.meat.bird_raw"): VoxTrans(
"voxel.sprite.food.meat.bird_raw",
(0.0, 0.0, 0.0), (-20.0, 10.0, 20.0), 0.9,
),
Consumable("BirdCooked"): VoxTrans(
Consumable("common.items.food.meat.bird_cooked"): VoxTrans(
"voxel.sprite.food.meat.bird_cooked",
(0.0, 0.0, 0.0), (-20.0, 10.0, 20.0), 0.9,
),
Consumable("BirdLargeRaw"): VoxTrans(
Consumable("common.items.food.meat.bird_large_raw"): VoxTrans(
"voxel.sprite.food.meat.bird_large_raw",
(0.0, 0.0, 0.0), (-20.0, 10.0, 20.0), 0.9,
),
Consumable("BirdLargeCooked"): VoxTrans(
Consumable("common.items.food.meat.bird_large_cooked"): VoxTrans(
"voxel.sprite.food.meat.bird_large_cooked",
(0.0, 0.0, 0.0), (-20.0, 10.0, 20.0), 0.9,
),
Consumable("BeastSmallRaw"): VoxTrans(
Consumable("common.items.food.meat.beast_small_raw"): VoxTrans(
"voxel.sprite.food.meat.beast_small_raw",
(0.0, 0.0, 0.0), (-50.0, 10.0, 0.0), 1.0,
),
Consumable("BeastSmallCooked"): VoxTrans(
Consumable("common.items.food.meat.beast_small_cooked"): VoxTrans(
"voxel.sprite.food.meat.beast_small_cooked",
(0.0, 0.0, 0.0), (-50.0, 20.0, 0.0), 1.0,
),
Consumable("ToughRaw"): VoxTrans(
Consumable("common.items.food.meat.tough_raw"): VoxTrans(
"voxel.sprite.food.meat.tough_raw",
(0.0, 0.0, 0.0), (-50.0, 10.0, 0.0), 1.0,
),
Consumable("ToughCooked"): VoxTrans(
Consumable("common.items.food.meat.tough_cooked"): VoxTrans(
"voxel.sprite.food.meat.tough_cooked",
(0.0, 0.0, 0.0), (-50.0, 20.0, 0.0), 1.0,
),
Consumable("BeastLargeRaw"): VoxTrans(
Consumable("common.items.food.meat.beast_large_raw"): VoxTrans(
"voxel.sprite.food.meat.beast_large_raw",
(-1.0, 0.0, 0.0), (-80.0, 40.0, 0.0), 0.8,
),
Consumable("BeastLargeCooked"): VoxTrans(
Consumable("common.items.food.meat.beast_large_cooked"): VoxTrans(
"voxel.sprite.food.meat.beast_large_cooked",
(-1.0, 0.0, 0.0), (-80.0, 40.0, 0.0), 0.8,
),
Consumable("PlainSalad"): VoxTrans(
Consumable("common.items.food.plainsalad"): VoxTrans(
"voxel.sprite.food.salad_plain",
(0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.8,
),
Consumable("TomatoSalad"): VoxTrans(
Consumable("common.items.food.tomatosalad"): VoxTrans(
"voxel.sprite.food.salad_tomato",
(0.0, 0.0, 0.0), (-50.0, 30.0, 20.0), 0.8,
),

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

@ -105,6 +105,8 @@ pub enum CharacterState {
SelfBuff(self_buff::Data),
/// Creates sprites around the caster
SpriteSummon(sprite_summon::Data),
/// Handles logic for using an item so it is not simply instant
UseItem(use_item::Data),
}
impl CharacterState {
@ -175,6 +177,7 @@ impl CharacterState {
| CharacterState::Shockwave(_)
| CharacterState::BasicBeam(_)
| CharacterState::Stunned(_)
| CharacterState::UseItem(_)
| CharacterState::Wielding
| CharacterState::Talk
| CharacterState::HealingBeam(_)

View File

@ -251,7 +251,7 @@ pub enum ItemKind {
Armor(armor::Armor),
Glider(Glider),
Consumable {
kind: String,
kind: ConsumableKind,
effect: Vec<Effect>,
},
Throwable {
@ -270,6 +270,12 @@ pub enum ItemKind {
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ConsumableKind {
Potion,
Food,
}
impl ItemKind {
pub fn is_equippable(&self) -> bool {
matches!(

View File

@ -9,7 +9,10 @@ use vek::Vec3;
use crate::{
comp::{
inventory::{
item::{tool::AbilityMap, ItemDef, ItemKind, MaterialStatManifest, TagExampleInfo},
item::{
tool::AbilityMap, ConsumableKind, ItemDef, ItemKind, MaterialStatManifest,
TagExampleInfo,
},
loadout::Loadout,
slot::{EquipSlot, Slot, SlotError},
},
@ -803,7 +806,7 @@ impl Component for Inventory {
pub enum InventoryUpdateEvent {
Init,
Used,
Consumed(String),
Consumed(ConsumableKind),
Gave,
Given,
Swapped,

View File

@ -29,5 +29,6 @@ pub mod spin_melee;
pub mod sprite_summon;
pub mod stunned;
pub mod talk;
pub mod use_item;
pub mod utils;
pub mod wielding;

View File

@ -0,0 +1,208 @@
use super::utils::*;
use crate::{
comp::{
buff::{BuffChange, BuffKind},
inventory::{
item::{ConsumableKind, ItemKind},
slot::{InvSlotId, Slot},
},
CharacterState, InventoryManip, StateUpdate,
},
event::ServerEvent,
states::behavior::{CharacterBehavior, JoinData},
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
/// Separated out to condense update portions of character state
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct StaticData {
/// Buildup to item use
pub buildup_duration: Duration,
/// Duration of item use
pub use_duration: Duration,
/// Recovery after item use
pub recover_duration: Duration,
/// Inventory slot to use item from
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
pub was_wielded: bool,
/// Was sneaking
pub was_sneak: bool,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Data {
/// Struct containing data that does not change over the course of the
/// character state
pub static_data: StaticData,
/// Timer for each stage
pub timer: Duration,
/// What section the character stage is in
pub stage_section: StageSection,
}
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData) -> StateUpdate {
let mut update = StateUpdate::from(data);
match self.static_data.item_kind {
ItemUseKind::Consumable(ConsumableKind::Potion) => {
handle_orientation(data, &mut update, 1.0);
handle_move(data, &mut update, 1.0);
},
ItemUseKind::Consumable(ConsumableKind::Food) => {
handle_orientation(data, &mut update, 0.0);
handle_move(data, &mut update, 0.0);
},
}
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,
});
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,
});
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
});
} else {
// Done
if self.static_data.was_wielded {
update.character = CharacterState::Wielding;
} else if self.static_data.was_sneak {
update.character = CharacterState::Sneak;
} else {
update.character = CharacterState::Idle;
}
}
},
_ => {
// If it somehow ends up in an incorrect stage section
update.character = CharacterState::Idle;
},
}
// 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
}
}
/// Used to control effects based off of the type of item used
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum ItemUseKind {
Consumable(ConsumableKind),
}
impl From<&ItemKind> for Option<ItemUseKind> {
fn from(item_kind: &ItemKind) -> Self {
match item_kind {
ItemKind::Consumable { kind, .. } => Some(ItemUseKind::Consumable(*kind)),
_ => None,
}
}
}
impl ItemUseKind {
/// Returns (buildup, use, recover)
pub fn durations(&self) -> (Duration, Duration, Duration) {
match self {
Self::Consumable(ConsumableKind::Potion) => (
Duration::from_secs_f32(0.1),
Duration::from_secs_f32(1.1),
Duration::from_secs_f32(0.1),
),
Self::Consumable(ConsumableKind::Food) => (
Duration::from_secs_f32(1.0),
Duration::from_secs_f32(5.0),
Duration::from_secs_f32(0.5),
),
}
}
}
/// 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

@ -2,7 +2,7 @@ use crate::{
combat,
comp::{
biped_large, biped_small,
inventory::slot::EquipSlot,
inventory::slot::{EquipSlot, Slot},
item::{Hands, ItemKind, Tool, ToolKind},
quadruped_low, quadruped_medium, quadruped_small,
skills::{Skill, SwimSkill},
@ -575,9 +575,45 @@ pub fn handle_manipulate_loadout(
update: &mut StateUpdate,
inv_action: InventoryAction,
) {
update
.server_events
.push_front(ServerEvent::InventoryManip(data.entity, inv_action.into()));
use use_item::ItemUseKind;
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, item)) = data
.inventory
.get(inv_slot)
.and_then(|item| Option::<ItemUseKind>::from(item.kind()).zip(Some(item)))
{
let (buildup_duration, use_duration, recover_duration) = item_kind.durations();
// If item returns a valid kind for item use, do into use item character state
update.character = CharacterState::UseItem(use_item::Data {
static_data: use_item::StaticData {
buildup_duration,
use_duration,
recover_duration,
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),
},
timer: Duration::default(),
stage_section: StageSection::Buildup,
});
} else {
// Else emit inventory action instantnaneously
update
.server_events
.push_front(ServerEvent::InventoryManip(data.entity, inv_action.into()));
}
} else {
// Else if inventory action is not item use, or if slot is in loadout, just do
// event instantaneously
update
.server_events
.push_front(ServerEvent::InventoryManip(data.entity, inv_action.into()));
}
}
/// Checks that player can wield the glider and updates `CharacterState` if so
@ -836,12 +872,15 @@ pub fn tick_attack_or_default(
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum StageSection {
Buildup,
Swing,
Recover,
Charge,
Cast,
Shoot,
Movement,
// TODO: Consolidate these to `Action`
// Code reviewers: comment here to remind me to open beginner issue
Swing,
Shoot,
Cast,
Use,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]

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 => {
@ -332,6 +340,7 @@ impl<'a> System<'a> for Sys {
CharacterState::BasicSummon(data) => data.handle_event(&j, action),
CharacterState::SelfBuff(data) => data.handle_event(&j, action),
CharacterState::SpriteSummon(data) => data.handle_event(&j, action),
CharacterState::UseItem(data) => data.handle_event(&j, action),
};
local_emitter.append(&mut state_update.local_events);
server_emitter.append(&mut state_update.server_events);
@ -388,6 +397,7 @@ impl<'a> System<'a> for Sys {
CharacterState::BasicSummon(data) => data.behavior(&j),
CharacterState::SelfBuff(data) => data.behavior(&j),
CharacterState::SpriteSummon(data) => data.behavior(&j),
CharacterState::UseItem(data) => data.behavior(&j),
};
local_emitter.append(&mut state_update.local_events);

View File

@ -289,7 +289,8 @@ impl<'a> System<'a> for Sys {
CharacterState::Roll { .. }
| CharacterState::Climb { .. }
| CharacterState::Stunned { .. }
| CharacterState::BasicBlock { .. } => {},
| CharacterState::BasicBlock { .. }
| CharacterState::UseItem { .. } => {},
}
}

View File

@ -287,7 +287,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
match item.kind() {
ItemKind::Consumable { kind, effect, .. } => {
maybe_effect = Some(effect.clone());
Some(comp::InventoryUpdateEvent::Consumed(kind.clone()))
Some(comp::InventoryUpdateEvent::Consumed(*kind))
},
ItemKind::Throwable { kind, .. } => {
if let Some(pos) =

View File

@ -0,0 +1,128 @@
use super::{
super::{vek::*, Animation},
CharacterSkeleton, SkeletonAttr,
};
use common::{
comp::item::ConsumableKind,
states::{use_item::ItemUseKind, utils::StageSection},
};
pub struct ConsumeAnimation;
impl Animation for ConsumeAnimation {
type Dependency<'a> = (f32, Option<StageSection>, Option<ItemUseKind>);
type Skeleton = CharacterSkeleton;
#[cfg(feature = "use-dyn-lib")]
const UPDATE_FN: &'static [u8] = b"character_consume\0";
#[cfg_attr(feature = "be-dyn-lib", export_name = "character_consume")]
fn update_skeleton_inner<'a>(
skeleton: &Self::Skeleton,
(_global_time, stage_section, item_kind): Self::Dependency<'a>,
anim_time: f32,
_rate: &mut f32,
s_a: &SkeletonAttr,
) -> Self::Skeleton {
let mut next = (*skeleton).clone();
match item_kind {
Some(ItemUseKind::Consumable(ConsumableKind::Potion)) => {
let (move1, move2, move3) = match stage_section {
Some(StageSection::Buildup) => (anim_time, 0.0, 0.0),
Some(StageSection::Use) => (1.0, (anim_time * 8.0).sin(), 0.0),
Some(StageSection::Recover) => (1.0, 1.0, anim_time.powf(0.25)),
_ => (0.0, 0.0, 0.0),
};
let pullback = 1.0 - move3;
let move2 = move2 * pullback;
let move1 = move1 * pullback;
next.head.orientation = Quaternion::rotation_x(move1 * 0.5 + move2 * -0.05);
next.hand_r.position = Vec3::new(
s_a.hand.0 + move1 * -4.0,
s_a.hand.1 + move1 * 6.0,
s_a.hand.2 + move1 * 10.0 + move2 * -1.0,
);
next.hand_r.orientation = Quaternion::rotation_x(move1 * 2.3 + move2 * -0.2)
* Quaternion::rotation_y(move1 * 1.2);
next.chest.orientation = Quaternion::rotation_x(move1 * 0.25);
next.hand_l.position = Vec3::new(
-s_a.hand.0 + move1 * 3.0,
s_a.hand.1 + move1 * 2.0,
s_a.hand.2,
);
next.hand_l.orientation =
Quaternion::rotation_x(move1 * 0.8) * Quaternion::rotation_y(move1 * -0.5);
},
Some(ItemUseKind::Consumable(ConsumableKind::Food)) => {
let (move1, move2, move3) = match stage_section {
Some(StageSection::Buildup) => (anim_time, 0.0, 0.0),
Some(StageSection::Use) => (1.0, (anim_time * 12.0).sin(), 0.0),
Some(StageSection::Recover) => (1.0, 1.0, anim_time.powf(0.25)),
_ => (0.0, 0.0, 0.0),
};
let pullback = 1.0 - move3;
let move2 = move2 * pullback;
let move1 = move1 * pullback;
next.head.position =
Vec3::new(0.0, s_a.head.0 + move1 * 2.0, s_a.head.1 + move1 * 1.0);
next.head.orientation =
Quaternion::rotation_x(move1 * -0.3) * Quaternion::rotation_z(move2 * -0.15);
next.chest.position = Vec3::new(0.0, s_a.chest.0, s_a.chest.1 + move1 * -3.0);
next.chest.orientation =
Quaternion::rotation_x(move1 * 0.3) * Quaternion::rotation_z(move2 * 0.05);
next.belt.position = Vec3::new(0.0, s_a.belt.0 + move1 * 1.0, s_a.belt.1);
next.belt.orientation = Quaternion::rotation_x(move1 * 0.2);
next.back.position = Vec3::new(0.0, s_a.back.0, s_a.back.1);
next.shorts.position = Vec3::new(0.0, s_a.shorts.0 + move1 * 3.0, s_a.shorts.1);
next.shorts.orientation = Quaternion::rotation_x(move1 * 0.7);
next.hand_l.position = Vec3::new(
-s_a.hand.0 + move1 * 3.0 + move2 * -1.0,
s_a.hand.1 + move1 * 5.0,
s_a.hand.2 + move1 * 3.0 + move2 * -2.0,
);
next.hand_l.orientation = Quaternion::rotation_x(move1 * 1.2)
* Quaternion::rotation_y(move1 * -0.5 + move2 * 0.3);
next.hand_r.position = Vec3::new(
s_a.hand.0 + move1 * -3.0 + move2 * -1.0,
s_a.hand.1 + move1 * 5.0,
s_a.hand.2 + move1 * 3.0 + move2 * 2.0,
);
next.hand_r.orientation = Quaternion::rotation_x(move1 * 1.2)
* Quaternion::rotation_y(move1 * 0.5 + move2 * 0.3);
next.foot_l.position = Vec3::new(
-s_a.foot.0,
s_a.foot.1 + move1 * 5.0,
s_a.foot.2 + move1 * 2.0,
);
next.foot_l.orientation = Quaternion::rotation_x(move1 * 1.2);
next.foot_r.position = Vec3::new(
s_a.foot.0,
s_a.foot.1 + move1 * 5.0,
s_a.foot.2 + move1 * 2.0,
);
next.foot_r.orientation = Quaternion::rotation_x(move1 * 1.2);
next.shoulder_l.position =
Vec3::new(-s_a.shoulder.0, s_a.shoulder.1, s_a.shoulder.2);
next.shoulder_r.position =
Vec3::new(s_a.shoulder.0, s_a.shoulder.1, s_a.shoulder.2);
},
_ => {},
}
next
}
}

View File

@ -4,6 +4,7 @@ pub mod beta;
pub mod block;
pub mod chargeswing;
pub mod climb;
pub mod consume;
pub mod dance;
pub mod dash;
pub mod equip;
@ -32,14 +33,14 @@ pub mod wield;
// Reexports
pub use self::{
alpha::AlphaAnimation, beam::BeamAnimation, beta::BetaAnimation, block::BlockAnimation,
chargeswing::ChargeswingAnimation, climb::ClimbAnimation, dance::DanceAnimation,
dash::DashAnimation, equip::EquipAnimation, glidewield::GlideWieldAnimation,
gliding::GlidingAnimation, idle::IdleAnimation, jump::JumpAnimation, leapmelee::LeapAnimation,
repeater::RepeaterAnimation, roll::RollAnimation, run::RunAnimation,
shockwave::ShockwaveAnimation, shoot::ShootAnimation, sit::SitAnimation, sneak::SneakAnimation,
spin::SpinAnimation, spinmelee::SpinMeleeAnimation, staggered::StaggeredAnimation,
stand::StandAnimation, stunned::StunnedAnimation, swim::SwimAnimation,
swimwield::SwimWieldAnimation, talk::TalkAnimation, wield::WieldAnimation,
chargeswing::ChargeswingAnimation, climb::ClimbAnimation, consume::ConsumeAnimation,
dance::DanceAnimation, dash::DashAnimation, equip::EquipAnimation,
glidewield::GlideWieldAnimation, gliding::GlidingAnimation, idle::IdleAnimation,
jump::JumpAnimation, leapmelee::LeapAnimation, repeater::RepeaterAnimation,
roll::RollAnimation, run::RunAnimation, shockwave::ShockwaveAnimation, shoot::ShootAnimation,
sit::SitAnimation, sneak::SneakAnimation, spin::SpinAnimation, spinmelee::SpinMeleeAnimation,
staggered::StaggeredAnimation, stand::StandAnimation, stunned::StunnedAnimation,
swim::SwimAnimation, swimwield::SwimWieldAnimation, talk::TalkAnimation, wield::WieldAnimation,
};
use super::{make_bone, vek::*, FigureBoneData, Skeleton};
use common::comp;

View File

@ -92,7 +92,7 @@ use common::{
assets::{self, AssetExt, AssetHandle},
comp::{
beam, biped_large, biped_small, humanoid,
item::{ItemKind, ToolKind},
item::{ConsumableKind, ItemKind, ToolKind},
object,
poise::PoiseState,
quadruped_low, quadruped_medium, quadruped_small, Body, CharacterAbilityType,
@ -276,7 +276,7 @@ pub enum SfxInventoryEvent {
CollectedTool(ToolKind),
CollectedItem(String),
CollectFailed,
Consumed(String),
Consumed(ConsumableKind),
Debug,
Dropped,
Given,
@ -308,7 +308,7 @@ impl From<&InventoryUpdateEvent> for SfxEvent {
SfxEvent::Inventory(SfxInventoryEvent::CollectFailed)
},
InventoryUpdateEvent::Consumed(consumable) => {
SfxEvent::Inventory(SfxInventoryEvent::Consumed(consumable.clone()))
SfxEvent::Inventory(SfxInventoryEvent::Consumed(*consumable))
},
InventoryUpdateEvent::Debug => SfxEvent::Inventory(SfxInventoryEvent::Debug),
InventoryUpdateEvent::Dropped => SfxEvent::Inventory(SfxInventoryEvent::Dropped),

View File

@ -49,7 +49,7 @@ impl<T: ItemDesc> From<&T> for ItemKey {
ItemKind::Glider(Glider { kind, .. }) => ItemKey::Glider(kind.clone()),
ItemKind::Armor(Armor { kind, .. }) => ItemKey::Armor(kind.clone()),
ItemKind::Utility { kind, .. } => ItemKey::Utility(*kind),
ItemKind::Consumable { kind, .. } => ItemKey::Consumable(kind.clone()),
ItemKind::Consumable { .. } => ItemKey::Consumable(item_definition_id.to_owned()),
ItemKind::Throwable { kind, .. } => ItemKey::Throwable(*kind),
ItemKind::Ingredient { kind, .. } => ItemKey::Ingredient(kind.clone()),
ItemKind::TagExamples { item_ids } => ItemKey::TagExamples(

View File

@ -1422,6 +1422,27 @@ impl FigureMgr {
skeleton_attr,
)
},
CharacterState::UseItem(s) => {
let stage_time = s.timer.as_secs_f32();
let item_kind = s.static_data.item_kind;
let stage_progress = match s.stage_section {
StageSection::Buildup => {
stage_time / s.static_data.buildup_duration.as_secs_f32()
},
StageSection::Use => stage_time,
StageSection::Recover => {
stage_time / s.static_data.recover_duration.as_secs_f32()
},
_ => 0.0,
};
anim::character::ConsumeAnimation::update_skeleton(
&target_base,
(time, Some(s.stage_section), Some(item_kind)),
stage_progress,
&mut state_animation_rate,
skeleton_attr,
)
},
CharacterState::Equipping { .. } => {
anim::character::EquipAnimation::update_skeleton(
&target_base,