sit on sprites

This commit is contained in:
Isse 2023-02-08 21:12:22 +01:00
parent 1cc221f653
commit 2928eb8d15
24 changed files with 288 additions and 18 deletions

View File

@ -112,3 +112,4 @@ common-material-stone = Stone
common-material-cloth = Cloth
common-material-hide = Hide
common-sprite-chest = Chest
common-sprite-chair = Chair

View File

@ -1530,7 +1530,7 @@ impl Client {
.ecs()
.read_storage::<CharacterState>()
.get(self.entity())
.map(|cs| matches!(cs, CharacterState::Sit));
.map(|cs| matches!(cs, CharacterState::Sit | CharacterState::MountSprite(_)));
match is_sitting {
Some(true) => self.control_action(ControlAction::Stand),
@ -1539,6 +1539,21 @@ impl Client {
}
}
pub fn stand_if_mounted(&mut self) {
let is_sitting = self
.state
.ecs()
.read_storage::<CharacterState>()
.get(self.entity())
.map(|cs| matches!(cs, CharacterState::MountSprite(_)));
match is_sitting {
Some(true) => self.control_action(ControlAction::Stand),
Some(false) => {},
None => warn!("Can't stand, client entity doesn't have a `CharacterState`"),
}
}
pub fn toggle_dance(&mut self) {
let is_dancing = self
.state
@ -1722,6 +1737,10 @@ impl Client {
)));
}
pub fn mount_sprite(&mut self, pos: Vec3<i32>) {
self.control_action(ControlAction::MountSprite(pos));
}
pub fn change_ability(&mut self, slot: usize, new_ability: comp::ability::AuxiliaryAbility) {
let auxiliary_key = self
.inventories()

View File

@ -470,6 +470,7 @@ impl From<&CharacterState> for CharacterAbilityType {
| CharacterState::SpriteSummon(_)
| CharacterState::UseItem(_)
| CharacterState::SpriteInteract(_)
| CharacterState::MountSprite(_)
| CharacterState::Skate(_)
| CharacterState::Wallrun(_) => Self::Other,
}

View File

@ -130,6 +130,8 @@ pub enum CharacterState {
/// Handles logic for interacting with a sprite, e.g. using a chest or
/// picking a plant
SpriteInteract(sprite_interact::Data),
// Mounted to a sprite
MountSprite(mount_sprite::Data),
/// Runs on the wall
Wallrun(wallrun::Data),
/// Ice skating or skiing
@ -470,6 +472,7 @@ impl CharacterState {
CharacterState::SpriteSummon(data) => data.behavior(j, output_events),
CharacterState::UseItem(data) => data.behavior(j, output_events),
CharacterState::SpriteInteract(data) => data.behavior(j, output_events),
CharacterState::MountSprite(data) => data.behavior(j, output_events),
CharacterState::Skate(data) => data.behavior(j, output_events),
CharacterState::Music(data) => data.behavior(j, output_events),
CharacterState::FinisherMelee(data) => data.behavior(j, output_events),
@ -524,6 +527,7 @@ impl CharacterState {
CharacterState::SpriteSummon(data) => data.handle_event(j, output_events, action),
CharacterState::UseItem(data) => data.handle_event(j, output_events, action),
CharacterState::SpriteInteract(data) => data.handle_event(j, output_events, action),
CharacterState::MountSprite(data) => data.handle_event(j, output_events, action),
CharacterState::Skate(data) => data.handle_event(j, output_events, action),
CharacterState::Music(data) => data.handle_event(j, output_events, action),
CharacterState::FinisherMelee(data) => data.handle_event(j, output_events, action),
@ -578,6 +582,7 @@ impl CharacterState {
CharacterState::SpriteSummon(data) => Some(data.static_data.ability_info),
CharacterState::UseItem(_) => None,
CharacterState::SpriteInteract(_) => None,
CharacterState::MountSprite(_) => None,
CharacterState::FinisherMelee(data) => Some(data.static_data.ability_info),
CharacterState::Music(data) => Some(data.static_data.ability_info),
CharacterState::DiveMelee(data) => Some(data.static_data.ability_info),
@ -623,6 +628,7 @@ impl CharacterState {
CharacterState::SpriteSummon(data) => Some(data.stage_section),
CharacterState::UseItem(data) => Some(data.stage_section),
CharacterState::SpriteInteract(data) => Some(data.stage_section),
CharacterState::MountSprite(_) => None,
CharacterState::FinisherMelee(data) => Some(data.stage_section),
CharacterState::Music(data) => Some(data.stage_section),
CharacterState::DiveMelee(data) => Some(data.stage_section),
@ -794,6 +800,7 @@ impl CharacterState {
recover: Some(data.static_data.recover_duration),
..Default::default()
}),
CharacterState::MountSprite(_) => None,
CharacterState::FinisherMelee(data) => Some(DurationsInfo {
buildup: Some(data.static_data.buildup_duration),
action: Some(data.static_data.swing_duration),
@ -862,6 +869,7 @@ impl CharacterState {
CharacterState::SpriteSummon(data) => Some(data.timer),
CharacterState::UseItem(data) => Some(data.timer),
CharacterState::SpriteInteract(data) => Some(data.timer),
CharacterState::MountSprite(_) => None,
CharacterState::FinisherMelee(data) => Some(data.timer),
CharacterState::Music(data) => Some(data.timer),
CharacterState::DiveMelee(data) => Some(data.timer),
@ -881,6 +889,7 @@ impl CharacterState {
CharacterState::GlideWield(_) => None,
CharacterState::Stunned(_) => None,
CharacterState::Sit => None,
CharacterState::MountSprite(_) => None,
CharacterState::Dance => None,
CharacterState::BasicBlock(_) => None,
CharacterState::Roll(_) => None,

View File

@ -165,6 +165,7 @@ pub enum ControlAction {
GlideWield,
Unwield,
Sit,
MountSprite(Vec3<i32>),
Dance,
Sneak,
Stand,

View File

@ -59,6 +59,9 @@ pub trait CharacterBehavior {
fn talk(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate {
StateUpdate::from(data)
}
fn mount_sprite(&self, data: &JoinData, _output_events: &mut OutputEvents, _pos: Vec3<i32>) -> StateUpdate {
StateUpdate::from(data)
}
// start_input has custom implementation in the following character states that
// may also need to be modified when changes are made here: ComboMelee2
fn start_input(
@ -111,6 +114,7 @@ pub trait CharacterBehavior {
select_pos,
} => self.start_input(data, input, target_entity, select_pos),
ControlAction::CancelInput(input) => self.cancel_input(data, input),
ControlAction::MountSprite(pos) => self.mount_sprite(data, output_events, pos),
}
}
}

View File

@ -7,6 +7,7 @@ use crate::{
},
};
use serde::{Deserialize, Serialize};
use vek::Vec3;
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct Data;
@ -50,6 +51,12 @@ impl CharacterBehavior for Data {
update
}
fn mount_sprite(&self, data: &JoinData, _: &mut OutputEvents, pos: Vec3<i32>) -> StateUpdate {
let mut update = StateUpdate::from(data);
attempt_mount_sprite(data, &mut update, pos);
update
}
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
let mut update = StateUpdate::from(data);
// Try to Fall/Stand up/Move

View File

@ -12,6 +12,7 @@ use crate::{
},
};
use serde::{Deserialize, Serialize};
use vek::Vec3;
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Data {
@ -110,6 +111,12 @@ impl CharacterBehavior for Data {
update
}
fn mount_sprite(&self, data: &JoinData, _: &mut OutputEvents, pos: Vec3<i32>) -> StateUpdate {
let mut update = StateUpdate::from(data);
attempt_mount_sprite(data, &mut update, pos);
update
}
fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
let mut update = StateUpdate::from(data);
attempt_dance(data, &mut update);

View File

@ -8,6 +8,7 @@ use crate::{
states::behavior::{CharacterBehavior, JoinData},
};
use serde::{Deserialize, Serialize};
use vek::Vec3;
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Default)]
pub struct Data {
@ -86,6 +87,12 @@ impl CharacterBehavior for Data {
update
}
fn mount_sprite(&self, data: &JoinData, _: &mut OutputEvents, pos: Vec3<i32>) -> StateUpdate {
let mut update = StateUpdate::from(data);
attempt_mount_sprite(data, &mut update, pos);
update
}
fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
let mut update = StateUpdate::from(data);
attempt_dance(data, &mut update);

View File

@ -40,3 +40,4 @@ pub mod use_item;
pub mod utils;
pub mod wallrun;
pub mod wielding;
pub mod mount_sprite;

View File

@ -0,0 +1,48 @@
use serde::{Serialize, Deserialize};
use vek::Vec3;
use crate::{comp::{character_state::OutputEvents, StateUpdate, CharacterState}, util::Dir};
use super::{behavior::{CharacterBehavior, JoinData}, utils::{handle_orientation, end_ability, handle_wield}, idle};
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct StaticData {
pub mount_pos: Vec3<f32>,
pub mount_dir: Vec3<f32>,
/// Position sprite is located at
pub sprite_pos: Vec3<i32>,
}
#[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,
}
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate {
let mut update = StateUpdate::from(data);
update.pos.0 = self.static_data.mount_pos;
handle_orientation(data, &mut update, 1.0, Some(Dir::new(self.static_data.mount_dir)));
handle_wield(data, &mut update);
// Try to Fall/Stand up/Move
if data.physics.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 {
update.character = CharacterState::Idle(idle::Data::default());
}
update
}
fn stand(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate {
let mut update = StateUpdate::from(data);
end_ability(data, &mut update);
update
}
}

View File

@ -10,6 +10,7 @@ use crate::{
},
};
use serde::{Deserialize, Serialize};
use vek::Vec3;
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Data {
@ -108,6 +109,12 @@ impl CharacterBehavior for Data {
update
}
fn mount_sprite(&self, data: &JoinData, _: &mut OutputEvents, pos: Vec3<i32>) -> StateUpdate {
let mut update = StateUpdate::from(data);
attempt_mount_sprite(data, &mut update, pos);
update
}
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
let mut update = StateUpdate::from(data);
// Try to Fall/Stand up/Move

View File

@ -7,6 +7,7 @@ use crate::{
},
};
use serde::{Deserialize, Serialize};
use vek::Vec3;
const TURN_RATE: f32 = 40.0;
@ -47,6 +48,13 @@ impl CharacterBehavior for Data {
update
}
fn mount_sprite(&self, data: &JoinData, _: &mut OutputEvents, pos: Vec3<i32>) -> StateUpdate {
let mut update = StateUpdate::from(data);
update.character = CharacterState::Idle(idle::Data::default());
attempt_mount_sprite(data, &mut update, pos);
update
}
fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
let mut update = StateUpdate::from(data);
update.character = CharacterState::Idle(idle::Data::default());

View File

@ -23,10 +23,11 @@ use crate::{
states::{behavior::JoinData, utils::CharacterState::Idle, *},
terrain::{TerrainGrid, UnlockKind},
util::Dir,
vol::ReadVol,
vol::ReadVol, terrain::SpriteKind,
};
use core::hash::BuildHasherDefault;
use fxhash::FxHasher64;
use ordered_float::OrderedFloat;
use serde::{Deserialize, Serialize};
use std::{
f32::consts::PI,
@ -784,6 +785,37 @@ pub fn attempt_sit(data: &JoinData<'_>, update: &mut StateUpdate) {
}
}
pub fn sprite_mount_points(sprite_kind: SpriteKind, pos: Vec3<i32>, ori: u8) -> impl ExactSizeIterator<Item = (Vec3<f32>, Vec3<f32>)> {
let mat = Mat4::identity()
.rotated_z(std::f32::consts::PI * 0.25 * ori as f32)
.translated_3d(
pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0)
);
sprite_kind.mount_offsets().iter().map(move |(pos, dir)| {
((mat * pos.with_w(1.0)).xyz(), (mat * dir.with_w(0.0)).xyz())
})
}
pub const SPRITE_MOUNT_DIST_SQR: f32 = 5.0;
pub fn attempt_mount_sprite(data: &JoinData<'_>, update: &mut StateUpdate, pos: Vec3<i32>) {
if let Some((kind, ori)) = data.terrain.get(pos).ok().and_then(|block| block.get_sprite().zip(block.get_ori())) {
if let Some((mount_pos, mount_dir)) = sprite_mount_points(kind, pos, ori).min_by_key(|(pos, _)| {
OrderedFloat(data.pos.0.distance_squared(*pos))
}) {
if mount_pos.distance_squared(data.pos.0) < SPRITE_MOUNT_DIST_SQR {
update.character = CharacterState::MountSprite(mount_sprite::Data {
static_data: mount_sprite::StaticData {
mount_pos,
mount_dir,
sprite_pos: pos,
},
});
}
}
}
}
pub fn attempt_dance(data: &JoinData<'_>, update: &mut StateUpdate) {
if data.physics.on_ground.is_some() && data.body.is_humanoid() {
update.character = CharacterState::Dance;

View File

@ -11,6 +11,7 @@ use crate::{
},
};
use serde::{Deserialize, Serialize};
use vek::Vec3;
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Data {
@ -93,6 +94,12 @@ impl CharacterBehavior for Data {
update
}
fn mount_sprite(&self, data: &JoinData, _: &mut OutputEvents, pos: Vec3<i32>) -> StateUpdate {
let mut update = StateUpdate::from(data);
attempt_mount_sprite(data, &mut update, pos);
update
}
fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
let mut update = StateUpdate::from(data);
attempt_dance(data, &mut update);

View File

@ -420,6 +420,12 @@ impl Block {
self.collectible_id().is_some() && self.mine_tool().is_none()
}
#[inline]
pub fn is_mountable(&self) -> bool {
self.get_sprite()
.map_or(false, |s| s.is_mountable())
}
#[inline]
pub fn is_bonkable(&self) -> bool {
match self.get_sprite() {

View File

@ -12,6 +12,7 @@ use num_derive::FromPrimitive;
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, fmt};
use strum::EnumIter;
use vek::Vec3;
make_case_elim!(
sprite_kind,
@ -474,6 +475,39 @@ impl SpriteKind {
matches!(self.collectible_id(), Some(Some(LootSpec::LootTable(_))))
}
/// Is the sprite a container that will emit a mystery item?
#[inline]
pub fn mount_offsets(&self) -> &'static [(Vec3<f32>, Vec3<f32>)] {
const UNIT_Y: Vec3<f32> = Vec3 {
x: 0.0,
y: -1.0,
z: 0.0,
};
match self {
SpriteKind::ChairSingle => &[(Vec3 {
x: 0.0,
y: 0.0,
z: 0.5,
}, UNIT_Y)],
SpriteKind::ChairDouble => &[(Vec3 {
x: -0.5,
y: 0.0,
z: 0.6,
}, UNIT_Y),
(Vec3 {
x: 0.5,
y: -0.1,
z: 0.6,
}, UNIT_Y)],
_ => &[],
}
}
#[inline]
pub fn is_mountable(&self) -> bool {
!self.mount_offsets().is_empty()
}
/// Which tool (if any) is needed to collect this sprite?
#[inline]
pub fn mine_tool(&self) -> Option<ToolKind> {

View File

@ -366,7 +366,8 @@ impl<'a> PhysicsData<'a> {
char_state_maybe,
)| {
let is_sticky = sticky.is_some();
let is_immovable = immovable.is_some();
let is_immovable = immovable.is_some()
|| matches!(char_state_maybe, Some(CharacterState::MountSprite(_)));
let is_mid_air = physics.on_surface().is_none();
let mut entity_entity_collision_checks = 0;
let mut entity_entity_collisions = 0;
@ -621,6 +622,7 @@ impl<'a> PhysicsData<'a> {
!&read.is_ridings,
)
.par_join()
.filter(|tuple| !matches!(tuple.4, Some(CharacterState::MountSprite(_))))
.for_each_init(
|| {
prof_span!(guard, "velocity update rayon job");
@ -751,7 +753,10 @@ impl<'a> PhysicsData<'a> {
!&read.is_ridings,
)
.par_join()
.filter(|tuple| tuple.3.is_voxel() == terrain_like_entities)
.filter(|tuple| {
tuple.3.is_voxel() == terrain_like_entities
&& !matches!(tuple.8, Some(CharacterState::MountSprite(_)))
})
.map_init(
|| {
prof_span!(guard, "physics e<>t rayon job");

View File

@ -145,7 +145,7 @@ impl<'a> System<'a> for Sys {
{
match character_state {
// Sitting accelerates recharging energy the most
CharacterState::Sit => {
CharacterState::Sit | CharacterState::MountSprite(_) => {
if energy.needs_regen() {
energy.regen(SIT_ENERGY_REGEN_ACCEL, dt);
}

View File

@ -71,10 +71,9 @@ use crate::{
render::UiDrawer,
scene::camera::{self, Camera},
session::{
interactable::{BlockInteraction, Interactable},
settings_change::{
Audio, Chat as ChatChange, Interface as InterfaceChange, SettingsChange,
},
}, interactable::{Interactable, BlockInteraction},
},
settings::chat::ChatFilter,
ui::{
@ -2107,10 +2106,34 @@ impl Hud {
}
}
},
BlockInteraction::Mount(_) => vec![(
Some(GameInput::Interact),
i18n.get_msg("hud-sit").to_string(),
)],
};
// This is only done once per frame, so it's not a performance issue
if let Some(desc) = block
.get_sprite()
.filter(|s| s.is_mountable())
.and_then(|s| get_sprite_desc(s, i18n))
{
overitem::Overitem::new(
desc,
overitem::TEXT_COLOR,
pos.distance_squared(player_pos),
&self.fonts,
i18n,
&global_state.settings.controls,
overitem_properties,
self.pulse,
&global_state.window.key_layout,
vec![(Some(GameInput::Interact), i18n.get_msg("hud-sit").to_string())],
)
.x_y(0.0, 100.0)
.position_ingame(over_pos)
.set(overitem_id, ui_widgets);
} else if let Some(desc) = block
.get_sprite()
.filter(|s| s.is_container())
.and_then(|s| get_sprite_desc(s, i18n))
@ -5130,6 +5153,7 @@ pub fn get_sprite_desc(sprite: SpriteKind, localized_strings: &Localization) ->
| SpriteKind::DungeonChest3
| SpriteKind::DungeonChest4
| SpriteKind::DungeonChest5 => "common-sprite-chest",
SpriteKind::ChairSingle | SpriteKind::ChairDouble => "common-sprite-chair",
sprite => return Some(Cow::Owned(format!("{:?}", sprite))),
};
Some(localized_strings.get_msg(i18n_key))

View File

@ -1977,7 +1977,7 @@ impl FigureMgr {
skeleton_attr,
)
},
CharacterState::Sit { .. } => {
CharacterState::Sit { .. } | CharacterState::MountSprite(_) => {
anim::character::SitAnimation::update_skeleton(
&target_base,
(active_tool_kind, second_tool_kind, time),

View File

@ -1,16 +1,20 @@
use crate::hud::CraftingTab;
use common::terrain::{BlockKind, SpriteKind, TerrainChunk};
use common::{
states::utils::sprite_mount_points,
terrain::{BlockKind, SpriteKind, TerrainChunk},
};
use common_base::span;
use rand::prelude::*;
use rand_chacha::ChaCha8Rng;
use vek::*;
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Debug)]
pub enum Interaction {
/// This covers mining, unlocking, and regular collectable things (e.g.
/// twigs).
Collect,
Craft(CraftingTab),
Mount(Vec<Vec3<f32>>),
}
pub enum FireplaceType {
@ -168,6 +172,21 @@ impl BlocksOfInterest {
Some(SpriteKind::RepairBench) => {
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
},
Some(
sprite_kind @ SpriteKind::ChairSingle
| sprite_kind @ SpriteKind::ChairDouble,
) => interactables.push((
pos,
Interaction::Mount(
sprite_mount_points(
sprite_kind,
Vec3::zero(),
block.get_ori().unwrap_or(0),
)
.map(|(pos, _)| pos)
.collect(),
),
)),
_ => {},
},
}

View File

@ -9,13 +9,13 @@ use super::{
use client::Client;
use common::{
comp,
comp::tool::ToolKind,
comp::{tool::ToolKind, CharacterState},
consts::MAX_PICKUP_RANGE,
link::Is,
mounting::Mount,
terrain::{Block, TerrainGrid, UnlockKind},
util::find_dist::{Cube, Cylinder, FindDist},
vol::ReadVol,
vol::ReadVol, states::utils::SPRITE_MOUNT_DIST_SQR,
};
use common_base::span;
@ -32,6 +32,7 @@ pub enum BlockInteraction {
// TODO: mining blocks don't use the interaction key, so it might not be the best abstraction
// to have them here, will see how things turn out
Mine(ToolKind),
Mount(Vec<Vec3<f32>>),
}
#[derive(Clone, Debug)]
@ -82,6 +83,7 @@ impl Interactable {
}
},
Interaction::Craft(tab) => BlockInteraction::Craft(tab),
Interaction::Mount(points) => BlockInteraction::Mount(points),
};
Some(Self::Block(block, pos, block_interaction))
}
@ -176,11 +178,12 @@ pub(super) fn select_interactable(
let items = ecs.read_storage::<comp::Item>();
let stats = ecs.read_storage::<comp::Stats>();
let player_char_state = char_states.get(player_entity);
let player_cylinder = Cylinder::from_components(
player_pos,
scales.get(player_entity).copied(),
colliders.get(player_entity),
char_states.get(player_entity),
player_char_state,
);
let closest_interactable_entity = (
@ -248,11 +251,24 @@ pub(super) fn select_interactable(
.interactables
.iter()
.map(move |(block_offset, interaction)| (chunk_pos + block_offset, interaction))
.filter_map(|(pos, interaction)| {
if matches!(player_char_state, Some(CharacterState::MountSprite(_))) && matches!(interaction, Interaction::Mount(_) | Interaction::Collect) {
None
} else if let Interaction::Mount(mount_points) = interaction {
mount_points.iter()
.map(|p| (p + pos.as_()).distance_squared(player_pos))
.filter(|dist| *dist < SPRITE_MOUNT_DIST_SQR)
.min_by_key(|dist| OrderedFloat(*dist))
.map(|dist| (pos, dist))
.zip(Some(interaction))
} else {
Some(((pos, (pos.as_() + 0.5).distance_squared(player_pos)), interaction))
}
})
})
.map(|(block_pos, interaction)| (
.map(|((block_pos, dis), interaction)| (
block_pos,
block_pos.map(|e| e as f32 + 0.5)
.distance_squared(player_pos),
dis,
interaction,
))
.min_by_key(|(_, dist_sqr, _)| OrderedFloat(*dist_sqr))
@ -267,7 +283,7 @@ pub(super) fn select_interactable(
}) < search_dist
})
.and_then(|(block_pos, interaction)| {
Interactable::from_block_pos(&terrain, block_pos, *interaction)
Interactable::from_block_pos(&terrain, block_pos, interaction.clone())
})
.or_else(|| closest_interactable_entity.map(|(e, _)| Interactable::Entity(e)))
}

View File

@ -921,8 +921,8 @@ impl PlayState for SessionState {
},
GameInput::Interact => {
if state {
let mut client = self.client.borrow_mut();
if let Some(interactable) = &self.interactable {
let mut client = self.client.borrow_mut();
match interactable {
Interactable::Block(block, pos, interaction) => {
match interaction {
@ -938,6 +938,11 @@ impl PlayState for SessionState {
block.get_sprite().map(|s| (*pos, s)),
)
},
BlockInteraction::Mount(_) => {
if block.is_mountable() {
client.mount_sprite(*pos);
}
},
BlockInteraction::Mine(_) => {},
}
},
@ -964,6 +969,8 @@ impl PlayState for SessionState {
}
},
}
} else {
client.stand_if_mounted()
}
}
},