Sprites now go through a character state to be picked up.

This commit is contained in:
Sam 2021-08-20 00:23:39 -04:00
parent 171e329609
commit 538cb56b87
7 changed files with 275 additions and 40 deletions

View File

@ -1369,8 +1369,8 @@ impl Client {
}
pub fn collect_block(&mut self, pos: Vec3<i32>) {
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::InventoryEvent(
InventoryEvent::Collect(pos),
self.control_action(ControlAction::InventoryAction(InventoryAction::Collect(
pos,
)));
}

View File

@ -108,6 +108,9 @@ pub enum CharacterState {
SpriteSummon(sprite_summon::Data),
/// Handles logic for using an item so it is not simply instant
UseItem(use_item::Data),
/// Handles logic for interacting with a sprite, e.g. using a chest or
/// picking a plant
SpriteInteract(sprite_interact::Data),
}
impl CharacterState {
@ -255,6 +258,7 @@ impl CharacterState {
CharacterState::SelfBuff(data) => data.behavior(j),
CharacterState::SpriteSummon(data) => data.behavior(j),
CharacterState::UseItem(data) => data.behavior(j),
CharacterState::SpriteInteract(data) => data.behavior(j),
}
}
@ -295,6 +299,7 @@ impl CharacterState {
CharacterState::SelfBuff(data) => data.handle_event(j, action),
CharacterState::SpriteSummon(data) => data.handle_event(j, action),
CharacterState::UseItem(data) => data.handle_event(j, action),
CharacterState::SpriteInteract(data) => data.handle_event(j, action),
}
}
}

View File

@ -17,7 +17,6 @@ use vek::*;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum InventoryEvent {
Pickup(Uid),
Collect(Vec3<i32>),
Swap(InvSlotId, InvSlotId),
SplitSwap(InvSlotId, InvSlotId),
Drop(InvSlotId),
@ -35,6 +34,7 @@ pub enum InventoryAction {
Drop(EquipSlot),
Use(Slot),
Sort,
Collect(Vec3<i32>),
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -61,6 +61,7 @@ impl From<InventoryAction> for InventoryManip {
InventoryAction::Swap(equip, slot) => Self::Swap(Slot::Equip(equip), slot),
InventoryAction::Drop(equip) => Self::Drop(Slot::Equip(equip)),
InventoryAction::Sort => Self::Sort,
InventoryAction::Collect(collect) => Self::Collect(collect),
}
}
}
@ -69,7 +70,6 @@ impl From<InventoryEvent> for InventoryManip {
fn from(inv_event: InventoryEvent) -> Self {
match inv_event {
InventoryEvent::Pickup(pickup) => Self::Pickup(pickup),
InventoryEvent::Collect(collect) => Self::Collect(collect),
InventoryEvent::Swap(inv1, inv2) => {
Self::Swap(Slot::Inventory(inv1), Slot::Inventory(inv2))
},

View File

@ -25,6 +25,7 @@ pub mod shockwave;
pub mod sit;
pub mod sneak;
pub mod spin_melee;
pub mod sprite_interact;
pub mod sprite_summon;
pub mod stunned;
pub mod talk;

View File

@ -0,0 +1,184 @@
use super::utils::*;
use crate::{
comp::{CharacterState, InventoryManip, StateUpdate},
event::ServerEvent,
states::behavior::{CharacterBehavior, JoinData},
terrain::SpriteKind,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use vek::Vec3;
/// Separated out to condense update portions of character state
#[derive(Clone, Copy, 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,
/// Position sprite is located at
pub sprite_pos: Vec3<i32>,
/// Kind of sprite interacted with
pub sprite_kind: SpriteInteractKind,
/// Had weapon wielded
pub was_wielded: bool,
/// Was sneaking
pub was_sneak: bool,
}
#[derive(Clone, Copy, 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);
handle_orientation(data, &mut update, 0.0);
handle_move(data, &mut update, 0.0);
match self.stage_section {
StageSection::Buildup => {
if self.timer < self.static_data.buildup_duration {
// Build up
update.character = CharacterState::SpriteInteract(Data {
timer: tick_attack_or_default(data, self.timer, None),
..*self
});
} else {
// Transitions to use section of stage
update.character = CharacterState::SpriteInteract(Data {
timer: Duration::default(),
stage_section: StageSection::Action,
..*self
});
}
},
StageSection::Action => {
if self.timer < self.static_data.use_duration {
// Item use
update.character = CharacterState::SpriteInteract(Data {
timer: tick_attack_or_default(data, self.timer, None),
..*self
});
} else {
// Transitions to recover section of stage
update.character = CharacterState::SpriteInteract(Data {
timer: Duration::default(),
stage_section: StageSection::Recover,
..*self
});
}
},
StageSection::Recover => {
if self.timer < self.static_data.recover_duration {
// Recovery
update.character = CharacterState::SpriteInteract(Data {
timer: tick_attack_or_default(data, self.timer, None),
..*self
});
} else {
// Create inventory manipulation event
let inv_manip = InventoryManip::Collect(self.static_data.sprite_pos);
update
.server_events
.push_front(ServerEvent::InventoryManip(data.entity, inv_manip));
// 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);
update
}
}
/// Used to control effects based off of the type of item used
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum SpriteInteractKind {
Chest,
Harvestable,
Collectible,
}
impl From<SpriteKind> for Option<SpriteInteractKind> {
fn from(sprite_kind: SpriteKind) -> Self {
match sprite_kind {
SpriteKind::Apple
| SpriteKind::Mushroom
| SpriteKind::RedFlower
| SpriteKind::Sunflower
| SpriteKind::Coconut
| SpriteKind::Beehive
| SpriteKind::Cotton
| SpriteKind::Moonbell
| SpriteKind::Pyrebloom
| SpriteKind::WildFlax
| SpriteKind::RoundCactus
| SpriteKind::ShortFlatCactus
| SpriteKind::MedFlatCactus => Some(SpriteInteractKind::Harvestable),
SpriteKind::Stones
| SpriteKind::Twigs
| SpriteKind::VialEmpty
| SpriteKind::Bowl
| SpriteKind::PotionMinor
| SpriteKind::Seashells => Some(SpriteInteractKind::Collectible),
SpriteKind::DungeonChest0
| SpriteKind::DungeonChest1
| SpriteKind::DungeonChest2
| SpriteKind::DungeonChest3
| SpriteKind::DungeonChest4
| SpriteKind::DungeonChest5
| SpriteKind::Chest
| SpriteKind::ChestBuried
| SpriteKind::Mud
| SpriteKind::Crate => Some(SpriteInteractKind::Chest),
_ => None,
}
}
}
impl SpriteInteractKind {
/// Returns (buildup, use, recover)
pub fn durations(&self) -> (Duration, Duration, Duration) {
match self {
Self::Chest => (
Duration::from_secs_f32(0.5),
Duration::from_secs_f32(2.0),
Duration::from_secs_f32(0.5),
),
Self::Collectible => (
Duration::from_secs_f32(0.1),
Duration::from_secs_f32(0.3),
Duration::from_secs_f32(0.1),
),
Self::Harvestable => (
Duration::from_secs_f32(0.3),
Duration::from_secs_f32(0.5),
Duration::from_secs_f32(0.2),
),
}
}
}

View File

@ -13,6 +13,7 @@ use crate::{
event::{LocalEvent, ServerEvent},
states::{behavior::JoinData, *},
util::Dir,
vol::ReadVol,
};
use serde::{Deserialize, Serialize};
use std::{
@ -584,11 +585,12 @@ pub fn handle_manipulate_loadout(
update: &mut StateUpdate,
inv_action: InventoryAction,
) {
use use_item::ItemUseKind;
if let InventoryAction::Use(Slot::Inventory(inv_slot)) = inv_action {
match inv_action {
InventoryAction::Use(Slot::Inventory(inv_slot)) => {
// 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
use use_item::ItemUseKind;
if let Some((item_kind, item)) = data
.inventory
.and_then(|inv| inv.get(inv_slot))
@ -616,13 +618,55 @@ pub fn handle_manipulate_loadout(
.server_events
.push_front(ServerEvent::InventoryManip(data.entity, inv_action.into()));
}
},
InventoryAction::Collect(pos) => {
// First, get sprite data for position, if there is a sprite
use sprite_interact::SpriteInteractKind;
let sprite_at_pos = data
.terrain
.get(pos)
.ok()
.copied()
.and_then(|b| b.get_sprite());
// Checks if position has a collectible sprite as wella s what sprite is at the
// position
let sprite_interact = sprite_at_pos.and_then(Option::<SpriteInteractKind>::from);
if let Some(sprite_interact) = sprite_interact {
// If the sprite is collectible, enter the sprite interaction character state
// TODO: Handle cases for sprite being interactible, but not collectible (none
// currently exist)
let (buildup_duration, use_duration, recover_duration) =
sprite_interact.durations();
update.character = CharacterState::SpriteInteract(sprite_interact::Data {
static_data: sprite_interact::StaticData {
buildup_duration,
use_duration,
recover_duration,
sprite_pos: pos,
sprite_kind: sprite_interact,
was_wielded: matches!(data.character, CharacterState::Wielding),
was_sneak: matches!(data.character, CharacterState::Sneak),
},
timer: Duration::default(),
stage_section: StageSection::Buildup,
})
} else {
// Else if inventory action is not item use, or if slot is in loadout, just do
// event instantaneously
// Otherwise send server event immediately
update
.server_events
.push_front(ServerEvent::InventoryManip(data.entity, inv_action.into()));
}
},
_ => {
// Else 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

View File

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