mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
intercat with sprites on ships
This commit is contained in:
parent
7fbe2cd5ec
commit
6674cce2cc
@ -19,6 +19,9 @@
|
|||||||
|
|
||||||
custom_indices: {
|
custom_indices: {
|
||||||
1: Air(ChairSingle, 4),
|
1: Air(ChairSingle, 4),
|
||||||
|
2: Air(Helm, 0),
|
||||||
|
3: Air(Door, 4),
|
||||||
|
8: Air(Door, 0),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
AirBalloon: (
|
AirBalloon: (
|
||||||
|
BIN
assets/common/voxel/airship_human/structure.vox
(Stored with Git LFS)
BIN
assets/common/voxel/airship_human/structure.vox
(Stored with Git LFS)
Binary file not shown.
BIN
assets/voxygen/voxel/sprite/furniture/helm.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/sprite/furniture/helm.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -1924,6 +1924,17 @@ ChairDouble: Some((
|
|||||||
],
|
],
|
||||||
wind_sway: 0.0,
|
wind_sway: 0.0,
|
||||||
)),
|
)),
|
||||||
|
// Helm
|
||||||
|
Helm: Some((
|
||||||
|
variations: [
|
||||||
|
(
|
||||||
|
model: "voxygen.voxel.sprite.furniture.helm",
|
||||||
|
offset: (-5.5, -5.5, 0.0),
|
||||||
|
lod_axes: (1.0, 1.0, 1.0),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
wind_sway: 0.0,
|
||||||
|
)),
|
||||||
// CoatRack
|
// CoatRack
|
||||||
CoatRack: Some((
|
CoatRack: Some((
|
||||||
variations: [
|
variations: [
|
||||||
|
@ -37,7 +37,7 @@ use common::{
|
|||||||
grid::Grid,
|
grid::Grid,
|
||||||
link::Is,
|
link::Is,
|
||||||
lod,
|
lod,
|
||||||
mounting::Rider,
|
mounting::{Rider, VolumePos, VolumeRider},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
recipe::{ComponentRecipeBook, RecipeBook, RepairRecipeBook},
|
recipe::{ComponentRecipeBook, RecipeBook, RepairRecipeBook},
|
||||||
resources::{GameMode, PlayerEntity, Time, TimeOfDay},
|
resources::{GameMode, PlayerEntity, Time, TimeOfDay},
|
||||||
@ -1185,7 +1185,7 @@ impl Client {
|
|||||||
&mut self,
|
&mut self,
|
||||||
recipe: &str,
|
recipe: &str,
|
||||||
slots: Vec<(u32, InvSlotId)>,
|
slots: Vec<(u32, InvSlotId)>,
|
||||||
craft_sprite: Option<(Vec3<i32>, SpriteKind)>,
|
craft_sprite: Option<(VolumePos, SpriteKind)>,
|
||||||
amount: u32,
|
amount: u32,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let (can_craft, required_sprite) = self.can_craft_recipe(recipe, amount);
|
let (can_craft, required_sprite) = self.can_craft_recipe(recipe, amount);
|
||||||
@ -1217,7 +1217,7 @@ impl Client {
|
|||||||
|
|
||||||
/// Salvage the item in the given inventory slot. `salvage_pos` should be
|
/// Salvage the item in the given inventory slot. `salvage_pos` should be
|
||||||
/// the location of a relevant crafting station within range of the player.
|
/// the location of a relevant crafting station within range of the player.
|
||||||
pub fn salvage_item(&mut self, slot: InvSlotId, salvage_pos: Vec3<i32>) -> bool {
|
pub fn salvage_item(&mut self, slot: InvSlotId, salvage_pos: VolumePos) -> bool {
|
||||||
let is_salvageable = self.can_salvage_item(slot);
|
let is_salvageable = self.can_salvage_item(slot);
|
||||||
if is_salvageable {
|
if is_salvageable {
|
||||||
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::InventoryEvent(
|
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::InventoryEvent(
|
||||||
@ -1239,7 +1239,7 @@ impl Client {
|
|||||||
&mut self,
|
&mut self,
|
||||||
primary_component: InvSlotId,
|
primary_component: InvSlotId,
|
||||||
secondary_component: InvSlotId,
|
secondary_component: InvSlotId,
|
||||||
sprite_pos: Option<Vec3<i32>>,
|
sprite_pos: Option<VolumePos>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let inventories = self.inventories();
|
let inventories = self.inventories();
|
||||||
let inventory = inventories.get(self.entity());
|
let inventory = inventories.get(self.entity());
|
||||||
@ -1288,7 +1288,7 @@ impl Client {
|
|||||||
material: InvSlotId,
|
material: InvSlotId,
|
||||||
modifier: Option<InvSlotId>,
|
modifier: Option<InvSlotId>,
|
||||||
slots: Vec<(u32, InvSlotId)>,
|
slots: Vec<(u32, InvSlotId)>,
|
||||||
sprite_pos: Option<Vec3<i32>>,
|
sprite_pos: Option<VolumePos>,
|
||||||
) {
|
) {
|
||||||
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::InventoryEvent(
|
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::InventoryEvent(
|
||||||
InventoryEvent::CraftRecipe {
|
InventoryEvent::CraftRecipe {
|
||||||
@ -1309,7 +1309,7 @@ impl Client {
|
|||||||
&mut self,
|
&mut self,
|
||||||
item: Slot,
|
item: Slot,
|
||||||
slots: Vec<(u32, InvSlotId)>,
|
slots: Vec<(u32, InvSlotId)>,
|
||||||
sprite_pos: Vec3<i32>,
|
sprite_pos: VolumePos,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let is_repairable = {
|
let is_repairable = {
|
||||||
let inventories = self.inventories();
|
let inventories = self.inventories();
|
||||||
@ -1448,6 +1448,12 @@ impl Client {
|
|||||||
.read_storage::<Is<Rider>>()
|
.read_storage::<Is<Rider>>()
|
||||||
.get(self.entity())
|
.get(self.entity())
|
||||||
.is_some()
|
.is_some()
|
||||||
|
|| self
|
||||||
|
.state
|
||||||
|
.ecs()
|
||||||
|
.read_storage::<Is<VolumeRider>>()
|
||||||
|
.get(self.entity())
|
||||||
|
.is_some()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_lantern_enabled(&self) -> bool {
|
pub fn is_lantern_enabled(&self) -> bool {
|
||||||
@ -1464,6 +1470,12 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mount_volume(&mut self, volume_pos: VolumePos) {
|
||||||
|
self.send_msg(ClientGeneral::ControlEvent(ControlEvent::MountVolume(
|
||||||
|
volume_pos,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn unmount(&mut self) { self.send_msg(ClientGeneral::ControlEvent(ControlEvent::Unmount)); }
|
pub fn unmount(&mut self) { self.send_msg(ClientGeneral::ControlEvent(ControlEvent::Unmount)); }
|
||||||
|
|
||||||
pub fn respawn(&mut self) {
|
pub fn respawn(&mut self) {
|
||||||
@ -1530,7 +1542,7 @@ impl Client {
|
|||||||
.ecs()
|
.ecs()
|
||||||
.read_storage::<CharacterState>()
|
.read_storage::<CharacterState>()
|
||||||
.get(self.entity())
|
.get(self.entity())
|
||||||
.map(|cs| matches!(cs, CharacterState::Sit | CharacterState::MountSprite(_)));
|
.map(|cs| matches!(cs, CharacterState::Sit));
|
||||||
|
|
||||||
match is_sitting {
|
match is_sitting {
|
||||||
Some(true) => self.control_action(ControlAction::Stand),
|
Some(true) => self.control_action(ControlAction::Stand),
|
||||||
@ -1539,22 +1551,6 @@ impl Client {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stand_if_mounted(&mut self) -> bool {
|
|
||||||
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`"),
|
|
||||||
}
|
|
||||||
is_sitting.unwrap_or(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn toggle_dance(&mut self) {
|
pub fn toggle_dance(&mut self) {
|
||||||
let is_dancing = self
|
let is_dancing = self
|
||||||
.state
|
.state
|
||||||
@ -1738,10 +1734,6 @@ 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) {
|
pub fn change_ability(&mut self, slot: usize, new_ability: comp::ability::AuxiliaryAbility) {
|
||||||
let auxiliary_key = self
|
let auxiliary_key = self
|
||||||
.inventories()
|
.inventories()
|
||||||
|
@ -34,6 +34,7 @@ macro_rules! synced_components {
|
|||||||
group: Group,
|
group: Group,
|
||||||
is_mount: IsMount,
|
is_mount: IsMount,
|
||||||
is_rider: IsRider,
|
is_rider: IsRider,
|
||||||
|
is_volume_rider: IsVolumeRider,
|
||||||
mass: Mass,
|
mass: Mass,
|
||||||
density: Density,
|
density: Density,
|
||||||
collider: Collider,
|
collider: Collider,
|
||||||
@ -72,7 +73,7 @@ macro_rules! reexport_comps {
|
|||||||
mod inner {
|
mod inner {
|
||||||
pub use common::comp::*;
|
pub use common::comp::*;
|
||||||
use common::link::Is;
|
use common::link::Is;
|
||||||
use common::mounting::{Mount, Rider};
|
use common::mounting::{Mount, Rider, VolumeRider};
|
||||||
|
|
||||||
// We alias these because the identifier used for the
|
// We alias these because the identifier used for the
|
||||||
// component's type is reused as an enum variant name
|
// component's type is reused as an enum variant name
|
||||||
@ -82,6 +83,7 @@ macro_rules! reexport_comps {
|
|||||||
// we can't just re-export all the types directly from `common::comp`.
|
// we can't just re-export all the types directly from `common::comp`.
|
||||||
pub type IsMount = Is<Mount>;
|
pub type IsMount = Is<Mount>;
|
||||||
pub type IsRider = Is<Rider>;
|
pub type IsRider = Is<Rider>;
|
||||||
|
pub type IsVolumeRider = Is<VolumeRider>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Re-export all the component types. So that uses of `synced_components!` outside this
|
// Re-export all the component types. So that uses of `synced_components!` outside this
|
||||||
@ -178,6 +180,10 @@ impl NetSync for IsRider {
|
|||||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl NetSync for IsVolumeRider {
|
||||||
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
|
}
|
||||||
|
|
||||||
impl NetSync for Mass {
|
impl NetSync for Mass {
|
||||||
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
const SYNC_FROM: SyncFrom = SyncFrom::AnyEntity;
|
||||||
}
|
}
|
||||||
|
@ -470,7 +470,6 @@ impl From<&CharacterState> for CharacterAbilityType {
|
|||||||
| CharacterState::SpriteSummon(_)
|
| CharacterState::SpriteSummon(_)
|
||||||
| CharacterState::UseItem(_)
|
| CharacterState::UseItem(_)
|
||||||
| CharacterState::SpriteInteract(_)
|
| CharacterState::SpriteInteract(_)
|
||||||
| CharacterState::MountSprite(_)
|
|
||||||
| CharacterState::Skate(_)
|
| CharacterState::Skate(_)
|
||||||
| CharacterState::Wallrun(_) => Self::Other,
|
| CharacterState::Wallrun(_) => Self::Other,
|
||||||
}
|
}
|
||||||
|
@ -130,8 +130,6 @@ pub enum CharacterState {
|
|||||||
/// Handles logic for interacting with a sprite, e.g. using a chest or
|
/// Handles logic for interacting with a sprite, e.g. using a chest or
|
||||||
/// picking a plant
|
/// picking a plant
|
||||||
SpriteInteract(sprite_interact::Data),
|
SpriteInteract(sprite_interact::Data),
|
||||||
// Mounted to a sprite
|
|
||||||
MountSprite(mount_sprite::Data),
|
|
||||||
/// Runs on the wall
|
/// Runs on the wall
|
||||||
Wallrun(wallrun::Data),
|
Wallrun(wallrun::Data),
|
||||||
/// Ice skating or skiing
|
/// Ice skating or skiing
|
||||||
@ -472,7 +470,6 @@ impl CharacterState {
|
|||||||
CharacterState::SpriteSummon(data) => data.behavior(j, output_events),
|
CharacterState::SpriteSummon(data) => data.behavior(j, output_events),
|
||||||
CharacterState::UseItem(data) => data.behavior(j, output_events),
|
CharacterState::UseItem(data) => data.behavior(j, output_events),
|
||||||
CharacterState::SpriteInteract(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::Skate(data) => data.behavior(j, output_events),
|
||||||
CharacterState::Music(data) => data.behavior(j, output_events),
|
CharacterState::Music(data) => data.behavior(j, output_events),
|
||||||
CharacterState::FinisherMelee(data) => data.behavior(j, output_events),
|
CharacterState::FinisherMelee(data) => data.behavior(j, output_events),
|
||||||
@ -527,7 +524,6 @@ impl CharacterState {
|
|||||||
CharacterState::SpriteSummon(data) => data.handle_event(j, output_events, action),
|
CharacterState::SpriteSummon(data) => data.handle_event(j, output_events, action),
|
||||||
CharacterState::UseItem(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::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::Skate(data) => data.handle_event(j, output_events, action),
|
||||||
CharacterState::Music(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),
|
CharacterState::FinisherMelee(data) => data.handle_event(j, output_events, action),
|
||||||
@ -582,7 +578,6 @@ impl CharacterState {
|
|||||||
CharacterState::SpriteSummon(data) => Some(data.static_data.ability_info),
|
CharacterState::SpriteSummon(data) => Some(data.static_data.ability_info),
|
||||||
CharacterState::UseItem(_) => None,
|
CharacterState::UseItem(_) => None,
|
||||||
CharacterState::SpriteInteract(_) => None,
|
CharacterState::SpriteInteract(_) => None,
|
||||||
CharacterState::MountSprite(_) => None,
|
|
||||||
CharacterState::FinisherMelee(data) => Some(data.static_data.ability_info),
|
CharacterState::FinisherMelee(data) => Some(data.static_data.ability_info),
|
||||||
CharacterState::Music(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),
|
CharacterState::DiveMelee(data) => Some(data.static_data.ability_info),
|
||||||
@ -628,7 +623,6 @@ impl CharacterState {
|
|||||||
CharacterState::SpriteSummon(data) => Some(data.stage_section),
|
CharacterState::SpriteSummon(data) => Some(data.stage_section),
|
||||||
CharacterState::UseItem(data) => Some(data.stage_section),
|
CharacterState::UseItem(data) => Some(data.stage_section),
|
||||||
CharacterState::SpriteInteract(data) => Some(data.stage_section),
|
CharacterState::SpriteInteract(data) => Some(data.stage_section),
|
||||||
CharacterState::MountSprite(_) => None,
|
|
||||||
CharacterState::FinisherMelee(data) => Some(data.stage_section),
|
CharacterState::FinisherMelee(data) => Some(data.stage_section),
|
||||||
CharacterState::Music(data) => Some(data.stage_section),
|
CharacterState::Music(data) => Some(data.stage_section),
|
||||||
CharacterState::DiveMelee(data) => Some(data.stage_section),
|
CharacterState::DiveMelee(data) => Some(data.stage_section),
|
||||||
@ -800,7 +794,6 @@ impl CharacterState {
|
|||||||
recover: Some(data.static_data.recover_duration),
|
recover: Some(data.static_data.recover_duration),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
CharacterState::MountSprite(_) => None,
|
|
||||||
CharacterState::FinisherMelee(data) => Some(DurationsInfo {
|
CharacterState::FinisherMelee(data) => Some(DurationsInfo {
|
||||||
buildup: Some(data.static_data.buildup_duration),
|
buildup: Some(data.static_data.buildup_duration),
|
||||||
action: Some(data.static_data.swing_duration),
|
action: Some(data.static_data.swing_duration),
|
||||||
@ -869,7 +862,6 @@ impl CharacterState {
|
|||||||
CharacterState::SpriteSummon(data) => Some(data.timer),
|
CharacterState::SpriteSummon(data) => Some(data.timer),
|
||||||
CharacterState::UseItem(data) => Some(data.timer),
|
CharacterState::UseItem(data) => Some(data.timer),
|
||||||
CharacterState::SpriteInteract(data) => Some(data.timer),
|
CharacterState::SpriteInteract(data) => Some(data.timer),
|
||||||
CharacterState::MountSprite(_) => None,
|
|
||||||
CharacterState::FinisherMelee(data) => Some(data.timer),
|
CharacterState::FinisherMelee(data) => Some(data.timer),
|
||||||
CharacterState::Music(data) => Some(data.timer),
|
CharacterState::Music(data) => Some(data.timer),
|
||||||
CharacterState::DiveMelee(data) => Some(data.timer),
|
CharacterState::DiveMelee(data) => Some(data.timer),
|
||||||
@ -889,7 +881,6 @@ impl CharacterState {
|
|||||||
CharacterState::GlideWield(_) => None,
|
CharacterState::GlideWield(_) => None,
|
||||||
CharacterState::Stunned(_) => None,
|
CharacterState::Stunned(_) => None,
|
||||||
CharacterState::Sit => None,
|
CharacterState::Sit => None,
|
||||||
CharacterState::MountSprite(_) => None,
|
|
||||||
CharacterState::Dance => None,
|
CharacterState::Dance => None,
|
||||||
CharacterState::BasicBlock(_) => None,
|
CharacterState::BasicBlock(_) => None,
|
||||||
CharacterState::Roll(_) => None,
|
CharacterState::Roll(_) => None,
|
||||||
|
@ -9,6 +9,7 @@ use crate::{
|
|||||||
invite::{InviteKind, InviteResponse},
|
invite::{InviteKind, InviteResponse},
|
||||||
BuffKind,
|
BuffKind,
|
||||||
},
|
},
|
||||||
|
mounting::VolumePos,
|
||||||
trade::{TradeAction, TradeId},
|
trade::{TradeAction, TradeId},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
util::Dir,
|
util::Dir,
|
||||||
@ -28,7 +29,7 @@ pub enum InventoryEvent {
|
|||||||
Sort,
|
Sort,
|
||||||
CraftRecipe {
|
CraftRecipe {
|
||||||
craft_event: CraftEvent,
|
craft_event: CraftEvent,
|
||||||
craft_sprite: Option<Vec3<i32>>,
|
craft_sprite: Option<VolumePos>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +58,7 @@ pub enum InventoryManip {
|
|||||||
Sort,
|
Sort,
|
||||||
CraftRecipe {
|
CraftRecipe {
|
||||||
craft_event: CraftEvent,
|
craft_event: CraftEvent,
|
||||||
craft_sprite: Option<Vec3<i32>>,
|
craft_sprite: Option<VolumePos>,
|
||||||
},
|
},
|
||||||
SwapEquippedWeapons,
|
SwapEquippedWeapons,
|
||||||
}
|
}
|
||||||
@ -143,6 +144,7 @@ pub enum ControlEvent {
|
|||||||
InviteResponse(InviteResponse),
|
InviteResponse(InviteResponse),
|
||||||
PerformTradeAction(TradeId, TradeAction),
|
PerformTradeAction(TradeId, TradeAction),
|
||||||
Mount(Uid),
|
Mount(Uid),
|
||||||
|
MountVolume(VolumePos),
|
||||||
Unmount,
|
Unmount,
|
||||||
InventoryEvent(InventoryEvent),
|
InventoryEvent(InventoryEvent),
|
||||||
GroupManip(GroupManip),
|
GroupManip(GroupManip),
|
||||||
@ -165,7 +167,6 @@ pub enum ControlAction {
|
|||||||
GlideWield,
|
GlideWield,
|
||||||
Unwield,
|
Unwield,
|
||||||
Sit,
|
Sit,
|
||||||
MountSprite(Vec3<i32>),
|
|
||||||
Dance,
|
Dance,
|
||||||
Sneak,
|
Sneak,
|
||||||
Stand,
|
Stand,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::{Fluid, Ori};
|
use super::{ship::figuredata::ShipSpec, Fluid, Ori};
|
||||||
use crate::{
|
use crate::{
|
||||||
comp::{body::ship::figuredata::VoxelCollider, inventory::item::armor::Friction},
|
comp::{body::ship::figuredata::VoxelCollider, inventory::item::armor::Friction},
|
||||||
consts::WATER_DENSITY,
|
consts::WATER_DENSITY,
|
||||||
@ -127,6 +127,14 @@ pub enum Collider {
|
|||||||
impl Collider {
|
impl Collider {
|
||||||
pub fn is_voxel(&self) -> bool { matches!(self, Collider::Voxel { .. } | Collider::Volume(_)) }
|
pub fn is_voxel(&self) -> bool { matches!(self, Collider::Voxel { .. } | Collider::Volume(_)) }
|
||||||
|
|
||||||
|
pub fn get_vol<'a>(&'a self, ship_spec: &'a ShipSpec) -> Option<&'a VoxelCollider> {
|
||||||
|
match self {
|
||||||
|
Collider::Voxel { id } => ship_spec.colliders.get(id),
|
||||||
|
Collider::Volume(vol) => Some(&**vol),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn bounding_radius(&self) -> f32 {
|
pub fn bounding_radius(&self) -> f32 {
|
||||||
match self {
|
match self {
|
||||||
Collider::Voxel { .. } | Collider::Volume(_) => 1.0,
|
Collider::Voxel { .. } | Collider::Volume(_) => 1.0,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// The limit on distance between the entity and a collectible (squared)
|
// The limit on distance between the entity and a collectible (squared)
|
||||||
pub const MAX_PICKUP_RANGE: f32 = 5.0;
|
pub const MAX_PICKUP_RANGE: f32 = 5.0;
|
||||||
pub const MAX_MOUNT_RANGE: f32 = 5.0;
|
pub const MAX_MOUNT_RANGE: f32 = 5.0;
|
||||||
|
pub const MAX_SPRITE_MOUNT_RANGE: f32 = 1.5;
|
||||||
pub const MAX_TRADE_RANGE: f32 = 20.0;
|
pub const MAX_TRADE_RANGE: f32 = 20.0;
|
||||||
|
|
||||||
pub const GRAVITY: f32 = 25.0;
|
pub const GRAVITY: f32 = 25.0;
|
||||||
|
@ -8,6 +8,7 @@ use crate::{
|
|||||||
DisconnectReason, Ori, Pos,
|
DisconnectReason, Ori, Pos,
|
||||||
},
|
},
|
||||||
lottery::LootSpec,
|
lottery::LootSpec,
|
||||||
|
mounting::VolumePos,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
rtsim::{RtSimEntity, RtSimVehicle},
|
rtsim::{RtSimEntity, RtSimVehicle},
|
||||||
terrain::SpriteKind,
|
terrain::SpriteKind,
|
||||||
@ -195,6 +196,7 @@ pub enum ServerEvent {
|
|||||||
InitiateInvite(EcsEntity, Uid, InviteKind),
|
InitiateInvite(EcsEntity, Uid, InviteKind),
|
||||||
ProcessTradeAction(EcsEntity, TradeId, TradeAction),
|
ProcessTradeAction(EcsEntity, TradeId, TradeAction),
|
||||||
Mount(EcsEntity, EcsEntity),
|
Mount(EcsEntity, EcsEntity),
|
||||||
|
MountVolume(EcsEntity, VolumePos),
|
||||||
Unmount(EcsEntity),
|
Unmount(EcsEntity),
|
||||||
Possess(Uid, Uid),
|
Possess(Uid, Uid),
|
||||||
/// Inserts default components for a character when loading into the game
|
/// Inserts default components for a character when loading into the game
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp,
|
comp::{self, ship::figuredata::VOXEL_COLLIDER_MANIFEST},
|
||||||
link::{Is, Link, LinkHandle, Role},
|
link::{Is, Link, LinkHandle, Role},
|
||||||
terrain::TerrainGrid,
|
terrain::{Block, TerrainGrid},
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
|
vol::ReadVol,
|
||||||
};
|
};
|
||||||
|
use hashbrown::HashSet;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::{saveload::MarkerAllocator, Entities, Read, ReadExpect, ReadStorage, WriteStorage};
|
use specs::{
|
||||||
|
saveload::MarkerAllocator,
|
||||||
|
storage::{GenericWriteStorage, MaskedStorage},
|
||||||
|
Component, DenseVecStorage, Entities, Read, ReadExpect, ReadStorage, Storage, Write,
|
||||||
|
WriteStorage,
|
||||||
|
};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
@ -39,6 +46,7 @@ impl Link for Mounting {
|
|||||||
Read<'a, UidAllocator>,
|
Read<'a, UidAllocator>,
|
||||||
WriteStorage<'a, Is<Mount>>,
|
WriteStorage<'a, Is<Mount>>,
|
||||||
WriteStorage<'a, Is<Rider>>,
|
WriteStorage<'a, Is<Rider>>,
|
||||||
|
ReadStorage<'a, Is<VolumeRider>>,
|
||||||
);
|
);
|
||||||
type DeleteData<'a> = (
|
type DeleteData<'a> = (
|
||||||
Read<'a, UidAllocator>,
|
Read<'a, UidAllocator>,
|
||||||
@ -59,7 +67,7 @@ impl Link for Mounting {
|
|||||||
|
|
||||||
fn create(
|
fn create(
|
||||||
this: &LinkHandle<Self>,
|
this: &LinkHandle<Self>,
|
||||||
(uid_allocator, mut is_mounts, mut is_riders): Self::CreateData<'_>,
|
(uid_allocator, mut is_mounts, mut is_riders, is_volume_rider): Self::CreateData<'_>,
|
||||||
) -> Result<(), Self::Error> {
|
) -> Result<(), Self::Error> {
|
||||||
let entity = |uid: Uid| uid_allocator.retrieve_entity_internal(uid.into());
|
let entity = |uid: Uid| uid_allocator.retrieve_entity_internal(uid.into());
|
||||||
|
|
||||||
@ -67,8 +75,11 @@ impl Link for Mounting {
|
|||||||
// Forbid self-mounting
|
// Forbid self-mounting
|
||||||
Err(MountingError::NotMountable)
|
Err(MountingError::NotMountable)
|
||||||
} else if let Some((mount, rider)) = entity(this.mount).zip(entity(this.rider)) {
|
} else if let Some((mount, rider)) = entity(this.mount).zip(entity(this.rider)) {
|
||||||
let can_mount_with =
|
let can_mount_with = |entity| {
|
||||||
|entity| is_mounts.get(entity).is_none() && is_riders.get(entity).is_none();
|
!is_mounts.contains(entity)
|
||||||
|
&& !is_riders.contains(entity)
|
||||||
|
&& !is_volume_rider.contains(entity)
|
||||||
|
};
|
||||||
|
|
||||||
// Ensure that neither mount or rider are already part of a mounting
|
// Ensure that neither mount or rider are already part of a mounting
|
||||||
// relationship
|
// relationship
|
||||||
@ -145,3 +156,257 @@ impl Link for Mounting {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct VolumeRider;
|
||||||
|
|
||||||
|
impl Role for VolumeRider {
|
||||||
|
type Link = VolumeMounting;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||||
|
pub enum Volume {
|
||||||
|
Terrain,
|
||||||
|
Entity(Uid),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||||
|
pub struct VolumePos {
|
||||||
|
pub kind: Volume,
|
||||||
|
pub pos: Vec3<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VolumePos {
|
||||||
|
pub fn terrain(block_pos: Vec3<i32>) -> Self {
|
||||||
|
Self {
|
||||||
|
kind: Volume::Terrain,
|
||||||
|
pos: block_pos,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn entity(block_pos: Vec3<i32>, uid: Uid) -> Self {
|
||||||
|
Self {
|
||||||
|
kind: Volume::Entity(uid),
|
||||||
|
pos: block_pos,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VolumePos {
|
||||||
|
pub fn get_block_and_transform(
|
||||||
|
&self,
|
||||||
|
terrain: &TerrainGrid,
|
||||||
|
uid_allocator: &UidAllocator,
|
||||||
|
positions: &Storage<comp::Pos, impl std::ops::Deref<Target = MaskedStorage<comp::Pos>>>,
|
||||||
|
orientations: &Storage<comp::Ori, impl std::ops::Deref<Target = MaskedStorage<comp::Ori>>>,
|
||||||
|
colliders: &ReadStorage<comp::Collider>,
|
||||||
|
) -> Option<(Mat4<f32>, Block)> {
|
||||||
|
match self.kind {
|
||||||
|
Volume::Terrain => Some((
|
||||||
|
Mat4::translation_3d(self.pos.as_()),
|
||||||
|
*terrain.get(self.pos).ok()?,
|
||||||
|
)),
|
||||||
|
Volume::Entity(uid) => {
|
||||||
|
uid_allocator
|
||||||
|
.retrieve_entity_internal(uid.0)
|
||||||
|
.and_then(|entity| {
|
||||||
|
let collider = colliders.get(entity)?;
|
||||||
|
let pos = positions.get(entity)?;
|
||||||
|
let ori = orientations.get(entity)?;
|
||||||
|
|
||||||
|
let voxel_colliders_manifest = VOXEL_COLLIDER_MANIFEST.read();
|
||||||
|
let voxel_collider = collider.get_vol(&voxel_colliders_manifest)?;
|
||||||
|
|
||||||
|
let block = *voxel_collider.volume().get(self.pos).ok()?;
|
||||||
|
|
||||||
|
let local_translation = voxel_collider.translation + self.pos.as_();
|
||||||
|
|
||||||
|
let trans = Mat4::from(ori.to_quat()).translated_3d(pos.0)
|
||||||
|
* Mat4::<f32>::translation_3d(local_translation);
|
||||||
|
|
||||||
|
Some((trans, block))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_block(
|
||||||
|
&self,
|
||||||
|
terrain: &TerrainGrid,
|
||||||
|
uid_allocator: &UidAllocator,
|
||||||
|
colliders: &ReadStorage<comp::Collider>,
|
||||||
|
) -> Option<Block> {
|
||||||
|
match self.kind {
|
||||||
|
Volume::Terrain => Some(*terrain.get(self.pos).ok()?),
|
||||||
|
Volume::Entity(uid) => {
|
||||||
|
uid_allocator
|
||||||
|
.retrieve_entity_internal(uid.0)
|
||||||
|
.and_then(|entity| {
|
||||||
|
let collider = colliders.get(entity)?;
|
||||||
|
|
||||||
|
let voxel_colliders_manifest = VOXEL_COLLIDER_MANIFEST.read();
|
||||||
|
let voxel_collider = collider.get_vol(&voxel_colliders_manifest)?;
|
||||||
|
|
||||||
|
let block = *voxel_collider.volume().get(self.pos).ok()?;
|
||||||
|
|
||||||
|
Some(block)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct VolumeRiders {
|
||||||
|
riders: HashSet<Vec3<i32>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for VolumeRiders {
|
||||||
|
type Storage = DenseVecStorage<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct VolumeMounting {
|
||||||
|
pub pos: VolumePos,
|
||||||
|
pub block: Block,
|
||||||
|
pub rider: Uid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Link for VolumeMounting {
|
||||||
|
type CreateData<'a> = (
|
||||||
|
Write<'a, VolumeRiders>,
|
||||||
|
WriteStorage<'a, VolumeRiders>,
|
||||||
|
WriteStorage<'a, Is<VolumeRider>>,
|
||||||
|
ReadStorage<'a, Is<Rider>>,
|
||||||
|
ReadStorage<'a, Is<Mount>>,
|
||||||
|
ReadExpect<'a, TerrainGrid>,
|
||||||
|
Read<'a, UidAllocator>,
|
||||||
|
ReadStorage<'a, comp::Collider>,
|
||||||
|
);
|
||||||
|
type DeleteData<'a> = (
|
||||||
|
Write<'a, VolumeRiders>,
|
||||||
|
WriteStorage<'a, VolumeRiders>,
|
||||||
|
WriteStorage<'a, Is<VolumeRider>>,
|
||||||
|
Read<'a, UidAllocator>,
|
||||||
|
);
|
||||||
|
type Error = MountingError;
|
||||||
|
type PersistData<'a> = (
|
||||||
|
Entities<'a>,
|
||||||
|
ReadStorage<'a, comp::Health>,
|
||||||
|
Read<'a, VolumeRiders>,
|
||||||
|
ReadStorage<'a, VolumeRiders>,
|
||||||
|
ReadStorage<'a, Is<VolumeRider>>,
|
||||||
|
ReadExpect<'a, TerrainGrid>,
|
||||||
|
Read<'a, UidAllocator>,
|
||||||
|
ReadStorage<'a, comp::Collider>,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn create(
|
||||||
|
this: &LinkHandle<Self>,
|
||||||
|
(
|
||||||
|
mut terrain_riders,
|
||||||
|
mut volume_riders,
|
||||||
|
mut is_volume_riders,
|
||||||
|
is_riders,
|
||||||
|
is_mounts,
|
||||||
|
terrain_grid,
|
||||||
|
uid_allocator,
|
||||||
|
colliders,
|
||||||
|
): Self::CreateData<'_>,
|
||||||
|
) -> Result<(), Self::Error> {
|
||||||
|
let entity = |uid: Uid| uid_allocator.retrieve_entity_internal(uid.into());
|
||||||
|
|
||||||
|
let riders = match this.pos.kind {
|
||||||
|
Volume::Terrain => &mut *terrain_riders,
|
||||||
|
Volume::Entity(uid) => entity(uid)
|
||||||
|
.and_then(|entity| volume_riders.get_mut_or_default(entity))
|
||||||
|
.ok_or(MountingError::NoSuchEntity)?,
|
||||||
|
};
|
||||||
|
let rider = entity(this.rider).ok_or(MountingError::NoSuchEntity)?;
|
||||||
|
|
||||||
|
if !riders.riders.contains(&this.pos.pos)
|
||||||
|
&& !is_volume_riders.contains(rider)
|
||||||
|
&& !is_volume_riders.contains(rider)
|
||||||
|
&& !is_riders.contains(rider)
|
||||||
|
&& !is_mounts.contains(rider)
|
||||||
|
{
|
||||||
|
let block = this
|
||||||
|
.pos
|
||||||
|
.get_block(&terrain_grid, &uid_allocator, &colliders)
|
||||||
|
.ok_or(MountingError::NoSuchEntity)?;
|
||||||
|
|
||||||
|
if block == this.block {
|
||||||
|
let _ = is_volume_riders.insert(rider, this.make_role());
|
||||||
|
riders.riders.insert(this.pos.pos);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(MountingError::NotMountable)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(MountingError::NotMountable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn persist(
|
||||||
|
this: &LinkHandle<Self>,
|
||||||
|
(
|
||||||
|
entities,
|
||||||
|
healths,
|
||||||
|
terrain_riders,
|
||||||
|
volume_riders,
|
||||||
|
is_volume_riders,
|
||||||
|
terrain_grid,
|
||||||
|
uid_allocator,
|
||||||
|
colliders,
|
||||||
|
): Self::PersistData<'_>,
|
||||||
|
) -> bool {
|
||||||
|
let entity = |uid: Uid| uid_allocator.retrieve_entity_internal(uid.into());
|
||||||
|
let is_alive =
|
||||||
|
|entity| entities.is_alive(entity) && healths.get(entity).map_or(true, |h| !h.is_dead);
|
||||||
|
let riders = match this.pos.kind {
|
||||||
|
Volume::Terrain => &*terrain_riders,
|
||||||
|
Volume::Entity(uid) => {
|
||||||
|
let Some(riders) = entity(uid)
|
||||||
|
.filter(|entity| is_alive(*entity))
|
||||||
|
.and_then(|entity| volume_riders.get(entity)) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
riders
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let rider_exists = entity(this.rider).map_or(false, |rider| {
|
||||||
|
is_volume_riders.contains(rider) && is_alive(rider)
|
||||||
|
});
|
||||||
|
let mount_spot_exists = riders.riders.contains(&this.pos.pos);
|
||||||
|
|
||||||
|
let block_exists = this
|
||||||
|
.pos
|
||||||
|
.get_block(&terrain_grid, &uid_allocator, &colliders)
|
||||||
|
.map_or(false, |block| block == this.block);
|
||||||
|
|
||||||
|
rider_exists && mount_spot_exists && block_exists
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete(
|
||||||
|
this: &LinkHandle<Self>,
|
||||||
|
(mut terrain_riders, mut volume_riders, mut is_rider, uid_allocator): Self::DeleteData<'_>,
|
||||||
|
) {
|
||||||
|
let entity = |uid: Uid| uid_allocator.retrieve_entity_internal(uid.into());
|
||||||
|
|
||||||
|
let riders = match this.pos.kind {
|
||||||
|
Volume::Terrain => Some(&mut *terrain_riders),
|
||||||
|
Volume::Entity(uid) => {
|
||||||
|
entity(uid).and_then(|entity| volume_riders.get_mut_or_default(entity))
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(riders) = riders {
|
||||||
|
riders.riders.remove(&this.pos.pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(entity) = entity(this.rider) {
|
||||||
|
is_rider.remove(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -9,7 +9,7 @@ use crate::{
|
|||||||
Stats, Vel,
|
Stats, Vel,
|
||||||
},
|
},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::Rider,
|
mounting::{Rider, VolumeRider},
|
||||||
resources::{DeltaTime, Time},
|
resources::{DeltaTime, Time},
|
||||||
terrain::TerrainGrid,
|
terrain::TerrainGrid,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
@ -59,14 +59,6 @@ pub trait CharacterBehavior {
|
|||||||
fn talk(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate {
|
fn talk(&self, data: &JoinData, _output_events: &mut OutputEvents) -> StateUpdate {
|
||||||
StateUpdate::from(data)
|
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
|
// start_input has custom implementation in the following character states that
|
||||||
// may also need to be modified when changes are made here: ComboMelee2
|
// may also need to be modified when changes are made here: ComboMelee2
|
||||||
fn start_input(
|
fn start_input(
|
||||||
@ -105,7 +97,7 @@ pub trait CharacterBehavior {
|
|||||||
ControlAction::Sit => self.sit(data, output_events),
|
ControlAction::Sit => self.sit(data, output_events),
|
||||||
ControlAction::Dance => self.dance(data, output_events),
|
ControlAction::Dance => self.dance(data, output_events),
|
||||||
ControlAction::Sneak => {
|
ControlAction::Sneak => {
|
||||||
if data.mount_data.is_none() {
|
if data.mount_data.is_none() && data.volume_mount_data.is_none() {
|
||||||
self.sneak(data, output_events)
|
self.sneak(data, output_events)
|
||||||
} else {
|
} else {
|
||||||
self.stand(data, output_events)
|
self.stand(data, output_events)
|
||||||
@ -119,7 +111,6 @@ pub trait CharacterBehavior {
|
|||||||
select_pos,
|
select_pos,
|
||||||
} => self.start_input(data, input, target_entity, select_pos),
|
} => self.start_input(data, input, target_entity, select_pos),
|
||||||
ControlAction::CancelInput(input) => self.cancel_input(data, input),
|
ControlAction::CancelInput(input) => self.cancel_input(data, input),
|
||||||
ControlAction::MountSprite(pos) => self.mount_sprite(data, output_events, pos),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -156,6 +147,7 @@ pub struct JoinData<'a> {
|
|||||||
pub alignment: Option<&'a comp::Alignment>,
|
pub alignment: Option<&'a comp::Alignment>,
|
||||||
pub terrain: &'a TerrainGrid,
|
pub terrain: &'a TerrainGrid,
|
||||||
pub mount_data: Option<&'a Is<Rider>>,
|
pub mount_data: Option<&'a Is<Rider>>,
|
||||||
|
pub volume_mount_data: Option<&'a Is<VolumeRider>>,
|
||||||
pub stance: Option<&'a Stance>,
|
pub stance: Option<&'a Stance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,6 +177,7 @@ pub struct JoinStruct<'a> {
|
|||||||
pub alignment: Option<&'a comp::Alignment>,
|
pub alignment: Option<&'a comp::Alignment>,
|
||||||
pub terrain: &'a TerrainGrid,
|
pub terrain: &'a TerrainGrid,
|
||||||
pub mount_data: Option<&'a Is<Rider>>,
|
pub mount_data: Option<&'a Is<Rider>>,
|
||||||
|
pub volume_mount_data: Option<&'a Is<VolumeRider>>,
|
||||||
pub stance: Option<&'a Stance>,
|
pub stance: Option<&'a Stance>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,6 +221,7 @@ impl<'a> JoinData<'a> {
|
|||||||
terrain: j.terrain,
|
terrain: j.terrain,
|
||||||
active_abilities: j.active_abilities,
|
active_abilities: j.active_abilities,
|
||||||
mount_data: j.mount_data,
|
mount_data: j.mount_data,
|
||||||
|
volume_mount_data: j.volume_mount_data,
|
||||||
stance: j.stance,
|
stance: j.stance,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::Vec3;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
||||||
pub struct Data;
|
pub struct Data;
|
||||||
@ -51,12 +50,6 @@ impl CharacterBehavior for Data {
|
|||||||
update
|
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 {
|
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
// Try to Fall/Stand up/Move
|
// Try to Fall/Stand up/Move
|
||||||
|
@ -12,7 +12,6 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::Vec3;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
@ -111,12 +110,6 @@ impl CharacterBehavior for Data {
|
|||||||
update
|
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 {
|
fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
attempt_dance(data, &mut update);
|
attempt_dance(data, &mut update);
|
||||||
|
@ -8,7 +8,6 @@ use crate::{
|
|||||||
states::behavior::{CharacterBehavior, JoinData},
|
states::behavior::{CharacterBehavior, JoinData},
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::Vec3;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Default)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Default)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
@ -87,12 +86,6 @@ impl CharacterBehavior for Data {
|
|||||||
update
|
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 {
|
fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
attempt_dance(data, &mut update);
|
attempt_dance(data, &mut update);
|
||||||
|
@ -22,7 +22,6 @@ pub mod glide_wield;
|
|||||||
pub mod idle;
|
pub mod idle;
|
||||||
pub mod leap_melee;
|
pub mod leap_melee;
|
||||||
pub mod leap_shockwave;
|
pub mod leap_shockwave;
|
||||||
pub mod mount_sprite;
|
|
||||||
pub mod music;
|
pub mod music;
|
||||||
pub mod rapid_melee;
|
pub mod rapid_melee;
|
||||||
pub mod repeater_ranged;
|
pub mod repeater_ranged;
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use vek::Vec3;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
comp::{character_state::OutputEvents, CharacterState, StateUpdate},
|
|
||||||
util::Dir,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
behavior::{CharacterBehavior, JoinData},
|
|
||||||
idle,
|
|
||||||
utils::{end_ability, handle_orientation, handle_wield},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[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;
|
|
||||||
update.vel.0 = Vec3::zero();
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,7 +10,6 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::Vec3;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
@ -109,12 +108,6 @@ impl CharacterBehavior for Data {
|
|||||||
update
|
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 {
|
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
// Try to Fall/Stand up/Move
|
// Try to Fall/Stand up/Move
|
||||||
|
@ -7,7 +7,6 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::Vec3;
|
|
||||||
|
|
||||||
const TURN_RATE: f32 = 40.0;
|
const TURN_RATE: f32 = 40.0;
|
||||||
|
|
||||||
@ -48,13 +47,6 @@ impl CharacterBehavior for Data {
|
|||||||
update
|
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 {
|
fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
update.character = CharacterState::Idle(idle::Data::default());
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
|
@ -21,13 +21,12 @@ use crate::{
|
|||||||
event::{LocalEvent, ServerEvent},
|
event::{LocalEvent, ServerEvent},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
states::{behavior::JoinData, utils::CharacterState::Idle, *},
|
states::{behavior::JoinData, utils::CharacterState::Idle, *},
|
||||||
terrain::{SpriteKind, TerrainGrid, UnlockKind},
|
terrain::{TerrainGrid, UnlockKind},
|
||||||
util::Dir,
|
util::Dir,
|
||||||
vol::ReadVol,
|
vol::ReadVol,
|
||||||
};
|
};
|
||||||
use core::hash::BuildHasherDefault;
|
use core::hash::BuildHasherDefault;
|
||||||
use fxhash::FxHasher64;
|
use fxhash::FxHasher64;
|
||||||
use ordered_float::OrderedFloat;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
f32::consts::PI,
|
f32::consts::PI,
|
||||||
@ -785,52 +784,6 @@ 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()))
|
|
||||||
}
|
|
||||||
|
|
||||||
const SPRITE_MOUNT_RANGE: f32 = 2.0;
|
|
||||||
pub const SPRITE_MOUNT_RANGE_SQR: f32 = SPRITE_MOUNT_RANGE * SPRITE_MOUNT_RANGE;
|
|
||||||
|
|
||||||
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 reach_block(
|
|
||||||
data.pos.0,
|
|
||||||
pos,
|
|
||||||
SPRITE_MOUNT_RANGE_SQR,
|
|
||||||
data.body,
|
|
||||||
data.terrain,
|
|
||||||
) {
|
|
||||||
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) {
|
pub fn attempt_dance(data: &JoinData<'_>, update: &mut StateUpdate) {
|
||||||
if data.physics.on_ground.is_some() && data.body.is_humanoid() {
|
if data.physics.on_ground.is_some() && data.body.is_humanoid() {
|
||||||
update.character = CharacterState::Dance;
|
update.character = CharacterState::Dance;
|
||||||
|
@ -11,7 +11,6 @@ use crate::{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::Vec3;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
@ -94,12 +93,6 @@ impl CharacterBehavior for Data {
|
|||||||
update
|
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 {
|
fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
attempt_dance(data, &mut update);
|
attempt_dance(data, &mut update);
|
||||||
|
@ -428,7 +428,16 @@ impl Block {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_mountable(&self) -> bool { self.get_sprite().map_or(false, |s| s.is_mountable()) }
|
pub fn is_mountable(&self) -> bool { self.mount_offset().is_some() }
|
||||||
|
|
||||||
|
/// Get the position and direction to mount this block if any.
|
||||||
|
pub fn mount_offset(&self) -> Option<(Vec3<f32>, Vec3<f32>)> {
|
||||||
|
self.get_sprite().and_then(|sprite| sprite.mount_offset())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_controller(&self) -> bool {
|
||||||
|
self.get_sprite().map_or(false, |sprite| sprite.is_controller())
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_bonkable(&self) -> bool {
|
pub fn is_bonkable(&self) -> bool {
|
||||||
|
@ -242,6 +242,7 @@ make_case_elim!(
|
|||||||
KeyDoor = 0xD8,
|
KeyDoor = 0xD8,
|
||||||
CommonLockedChest = 0xD9,
|
CommonLockedChest = 0xD9,
|
||||||
RepairBench = 0xDA,
|
RepairBench = 0xDA,
|
||||||
|
Helm = 0xDB,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -475,47 +476,40 @@ impl SpriteKind {
|
|||||||
matches!(self.collectible_id(), Some(Some(LootSpec::LootTable(_))))
|
matches!(self.collectible_id(), Some(Some(LootSpec::LootTable(_))))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the sprite a container that will emit a mystery item?
|
/// Get the position and direction to mount this sprite if any.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn mount_offsets(&self) -> &'static [(Vec3<f32>, Vec3<f32>)] {
|
pub fn mount_offset(&self) -> Option<(Vec3<f32>, Vec3<f32>)> {
|
||||||
const UNIT_Y: Vec3<f32> = Vec3 {
|
|
||||||
x: 0.0,
|
|
||||||
y: -1.0,
|
|
||||||
z: 0.0,
|
|
||||||
};
|
|
||||||
match self {
|
match self {
|
||||||
SpriteKind::ChairSingle => &[(
|
SpriteKind::ChairSingle | SpriteKind::ChairDouble => Some((
|
||||||
Vec3 {
|
Vec3 {
|
||||||
x: 0.0,
|
x: 0.0,
|
||||||
y: 0.0,
|
y: 0.0,
|
||||||
z: 0.5,
|
z: 0.5,
|
||||||
},
|
},
|
||||||
UNIT_Y,
|
-Vec3::unit_y(),
|
||||||
)],
|
)),
|
||||||
SpriteKind::ChairDouble => &[
|
SpriteKind::Helm => Some((
|
||||||
(
|
Vec3 {
|
||||||
Vec3 {
|
x: 0.0,
|
||||||
x: -0.5,
|
y: -0.6,
|
||||||
y: 0.0,
|
z: 0.2,
|
||||||
z: 0.6,
|
},
|
||||||
},
|
Vec3::unit_y(),
|
||||||
UNIT_Y,
|
)),
|
||||||
),
|
_ => None,
|
||||||
(
|
|
||||||
Vec3 {
|
|
||||||
x: 0.5,
|
|
||||||
y: -0.1,
|
|
||||||
z: 0.6,
|
|
||||||
},
|
|
||||||
UNIT_Y,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
_ => &[],
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_mountable(&self) -> bool { !self.mount_offsets().is_empty() }
|
pub fn is_mountable(&self) -> bool { self.mount_offset().is_some() }
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn is_controller(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
SpriteKind::Helm => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Which tool (if any) is needed to collect this sprite?
|
/// Which tool (if any) is needed to collect this sprite?
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -646,6 +640,7 @@ impl SpriteKind {
|
|||||||
| SpriteKind::Grave
|
| SpriteKind::Grave
|
||||||
| SpriteKind::Gravestone
|
| SpriteKind::Gravestone
|
||||||
| SpriteKind::MagicalBarrier
|
| SpriteKind::MagicalBarrier
|
||||||
|
| SpriteKind::Helm,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,6 +149,18 @@ impl FindDist<Vec3<f32>> for Cylinder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FindDist<Cylinder> for Vec3<f32> {
|
||||||
|
#[inline]
|
||||||
|
fn approx_in_range(self, other: Cylinder, range: f32) -> bool {
|
||||||
|
other.approx_in_range(self, range)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn min_distance(self, other: Cylinder) -> f32 {
|
||||||
|
other.min_distance(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -9,7 +9,7 @@ use common::{
|
|||||||
comp,
|
comp,
|
||||||
event::{EventBus, LocalEvent, ServerEvent},
|
event::{EventBus, LocalEvent, ServerEvent},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::{Mount, Rider},
|
mounting::{Mount, Rider, VolumeRider, VolumeRiders},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
region::RegionMap,
|
region::RegionMap,
|
||||||
resources::{
|
resources::{
|
||||||
@ -201,6 +201,7 @@ impl State {
|
|||||||
ecs.register::<comp::Scale>();
|
ecs.register::<comp::Scale>();
|
||||||
ecs.register::<Is<Mount>>();
|
ecs.register::<Is<Mount>>();
|
||||||
ecs.register::<Is<Rider>>();
|
ecs.register::<Is<Rider>>();
|
||||||
|
ecs.register::<Is<VolumeRider>>();
|
||||||
ecs.register::<comp::Mass>();
|
ecs.register::<comp::Mass>();
|
||||||
ecs.register::<comp::Density>();
|
ecs.register::<comp::Density>();
|
||||||
ecs.register::<comp::Collider>();
|
ecs.register::<comp::Collider>();
|
||||||
@ -260,6 +261,7 @@ impl State {
|
|||||||
ecs.register::<comp::invite::Invite>();
|
ecs.register::<comp::invite::Invite>();
|
||||||
ecs.register::<comp::invite::PendingInvites>();
|
ecs.register::<comp::invite::PendingInvites>();
|
||||||
ecs.register::<comp::Beam>();
|
ecs.register::<comp::Beam>();
|
||||||
|
ecs.register::<VolumeRiders>();
|
||||||
|
|
||||||
// Register synced resources used by the ECS.
|
// Register synced resources used by the ECS.
|
||||||
ecs.insert(TimeOfDay(0.0));
|
ecs.insert(TimeOfDay(0.0));
|
||||||
@ -294,6 +296,7 @@ impl State {
|
|||||||
ecs.insert(PhysicsMetrics::default());
|
ecs.insert(PhysicsMetrics::default());
|
||||||
ecs.insert(Trades::default());
|
ecs.insert(Trades::default());
|
||||||
ecs.insert(PlayerPhysicsSettings::default());
|
ecs.insert(PlayerPhysicsSettings::default());
|
||||||
|
ecs.insert(VolumeRiders::default());
|
||||||
|
|
||||||
// Load plugins from asset directory
|
// Load plugins from asset directory
|
||||||
#[cfg(feature = "plugins")]
|
#[cfg(feature = "plugins")]
|
||||||
|
@ -14,7 +14,7 @@ use common::{
|
|||||||
},
|
},
|
||||||
event::{EventBus, LocalEvent, ServerEvent},
|
event::{EventBus, LocalEvent, ServerEvent},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::Rider,
|
mounting::{Rider, VolumeRider},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::{DeltaTime, Time},
|
resources::{DeltaTime, Time},
|
||||||
states::{
|
states::{
|
||||||
@ -43,6 +43,7 @@ pub struct ReadData<'a> {
|
|||||||
beams: ReadStorage<'a, Beam>,
|
beams: ReadStorage<'a, Beam>,
|
||||||
uids: ReadStorage<'a, Uid>,
|
uids: ReadStorage<'a, Uid>,
|
||||||
is_riders: ReadStorage<'a, Is<Rider>>,
|
is_riders: ReadStorage<'a, Is<Rider>>,
|
||||||
|
is_volume_riders: ReadStorage<'a, Is<VolumeRider>>,
|
||||||
stats: ReadStorage<'a, Stats>,
|
stats: ReadStorage<'a, Stats>,
|
||||||
skill_sets: ReadStorage<'a, SkillSet>,
|
skill_sets: ReadStorage<'a, SkillSet>,
|
||||||
active_abilities: ReadStorage<'a, ActiveAbilities>,
|
active_abilities: ReadStorage<'a, ActiveAbilities>,
|
||||||
@ -207,6 +208,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
alignment: read_data.alignments.get(entity),
|
alignment: read_data.alignments.get(entity),
|
||||||
terrain: &read_data.terrain,
|
terrain: &read_data.terrain,
|
||||||
mount_data: read_data.is_riders.get(entity),
|
mount_data: read_data.is_riders.get(entity),
|
||||||
|
volume_mount_data: read_data.is_volume_riders.get(entity),
|
||||||
stance: read_data.stances.get(entity),
|
stance: read_data.stances.get(entity),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2,16 +2,17 @@ use common::{
|
|||||||
comp::{
|
comp::{
|
||||||
ability::Stance,
|
ability::Stance,
|
||||||
agent::{Sound, SoundKind},
|
agent::{Sound, SoundKind},
|
||||||
Body, BuffChange, ControlEvent, Controller, Pos, Scale,
|
Body, BuffChange, Collider, ControlEvent, Controller, Pos, Scale,
|
||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
|
terrain::TerrainGrid,
|
||||||
uid::UidAllocator,
|
uid::UidAllocator,
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
use specs::{
|
use specs::{
|
||||||
saveload::{Marker, MarkerAllocator},
|
saveload::{Marker, MarkerAllocator},
|
||||||
shred::ResourceId,
|
shred::ResourceId,
|
||||||
Entities, Join, Read, ReadStorage, SystemData, World, WriteStorage,
|
Entities, Join, Read, ReadExpect, ReadStorage, SystemData, World, WriteStorage,
|
||||||
};
|
};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -20,9 +21,11 @@ pub struct ReadData<'a> {
|
|||||||
entities: Entities<'a>,
|
entities: Entities<'a>,
|
||||||
uid_allocator: Read<'a, UidAllocator>,
|
uid_allocator: Read<'a, UidAllocator>,
|
||||||
server_bus: Read<'a, EventBus<ServerEvent>>,
|
server_bus: Read<'a, EventBus<ServerEvent>>,
|
||||||
|
terrain_grid: ReadExpect<'a, TerrainGrid>,
|
||||||
positions: ReadStorage<'a, Pos>,
|
positions: ReadStorage<'a, Pos>,
|
||||||
bodies: ReadStorage<'a, Body>,
|
bodies: ReadStorage<'a, Body>,
|
||||||
scales: ReadStorage<'a, Scale>,
|
scales: ReadStorage<'a, Scale>,
|
||||||
|
colliders: ReadStorage<'a, Collider>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -53,6 +56,17 @@ impl<'a> System<'a> for Sys {
|
|||||||
server_emitter.emit(ServerEvent::Mount(entity, mountee_entity));
|
server_emitter.emit(ServerEvent::Mount(entity, mountee_entity));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
ControlEvent::MountVolume(volume) => {
|
||||||
|
if let Some(block) = volume.get_block(
|
||||||
|
&read_data.terrain_grid,
|
||||||
|
&read_data.uid_allocator,
|
||||||
|
&read_data.colliders,
|
||||||
|
) {
|
||||||
|
if block.is_mountable() {
|
||||||
|
server_emitter.emit(ServerEvent::MountVolume(entity, volume));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
ControlEvent::RemoveBuff(buff_id) => {
|
ControlEvent::RemoveBuff(buff_id) => {
|
||||||
server_emitter.emit(ServerEvent::Buff {
|
server_emitter.emit(ServerEvent::Buff {
|
||||||
entity,
|
entity,
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{Body, ControlAction, Controller, InputKind, Ori, Pos, Scale, Vel},
|
comp::{Body, Collider, ControlAction, Controller, InputKind, Ori, Pos, Scale, Vel},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::Mount,
|
mounting::{Mount, VolumeRider},
|
||||||
|
terrain::TerrainGrid,
|
||||||
uid::UidAllocator,
|
uid::UidAllocator,
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
use specs::{
|
use specs::{
|
||||||
saveload::{Marker, MarkerAllocator},
|
saveload::{Marker, MarkerAllocator},
|
||||||
Entities, Join, Read, ReadStorage, WriteStorage,
|
Entities, Join, Read, ReadExpect, ReadStorage, WriteStorage,
|
||||||
};
|
};
|
||||||
|
use tracing::error;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
/// This system is responsible for controlling mounts
|
/// This system is responsible for controlling mounts
|
||||||
@ -17,14 +19,17 @@ pub struct Sys;
|
|||||||
impl<'a> System<'a> for Sys {
|
impl<'a> System<'a> for Sys {
|
||||||
type SystemData = (
|
type SystemData = (
|
||||||
Read<'a, UidAllocator>,
|
Read<'a, UidAllocator>,
|
||||||
|
ReadExpect<'a, TerrainGrid>,
|
||||||
Entities<'a>,
|
Entities<'a>,
|
||||||
WriteStorage<'a, Controller>,
|
WriteStorage<'a, Controller>,
|
||||||
ReadStorage<'a, Is<Mount>>,
|
ReadStorage<'a, Is<Mount>>,
|
||||||
|
ReadStorage<'a, Is<VolumeRider>>,
|
||||||
WriteStorage<'a, Pos>,
|
WriteStorage<'a, Pos>,
|
||||||
WriteStorage<'a, Vel>,
|
WriteStorage<'a, Vel>,
|
||||||
WriteStorage<'a, Ori>,
|
WriteStorage<'a, Ori>,
|
||||||
ReadStorage<'a, Body>,
|
ReadStorage<'a, Body>,
|
||||||
ReadStorage<'a, Scale>,
|
ReadStorage<'a, Scale>,
|
||||||
|
ReadStorage<'a, Collider>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const NAME: &'static str = "mount";
|
const NAME: &'static str = "mount";
|
||||||
@ -35,14 +40,17 @@ impl<'a> System<'a> for Sys {
|
|||||||
_job: &mut Job<Self>,
|
_job: &mut Job<Self>,
|
||||||
(
|
(
|
||||||
uid_allocator,
|
uid_allocator,
|
||||||
|
terrain,
|
||||||
entities,
|
entities,
|
||||||
mut controllers,
|
mut controllers,
|
||||||
is_mounts,
|
is_mounts,
|
||||||
|
is_volume_riders,
|
||||||
mut positions,
|
mut positions,
|
||||||
mut velocities,
|
mut velocities,
|
||||||
mut orientations,
|
mut orientations,
|
||||||
bodies,
|
bodies,
|
||||||
scales,
|
scales,
|
||||||
|
colliders,
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
// For each mount...
|
// For each mount...
|
||||||
@ -84,5 +92,87 @@ impl<'a> System<'a> for Sys {
|
|||||||
controller.actions = actions;
|
controller.actions = actions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For each volume rider.
|
||||||
|
for (entity, is_volume_rider) in (&entities, &is_volume_riders).join() {
|
||||||
|
if let Some((mut mat, _)) = is_volume_rider.pos.get_block_and_transform(
|
||||||
|
&terrain,
|
||||||
|
&uid_allocator,
|
||||||
|
&positions,
|
||||||
|
&orientations,
|
||||||
|
&colliders,
|
||||||
|
) {
|
||||||
|
let Some((mount_offset, mount_dir)) = is_volume_rider.block.mount_offset() else {
|
||||||
|
error!("Mounted on unmountable block");
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(ori) = is_volume_rider.block.get_ori() {
|
||||||
|
mat *= Mat4::identity()
|
||||||
|
.translated_3d(mount_offset)
|
||||||
|
.rotated_z(std::f32::consts::PI * 0.25 * ori as f32)
|
||||||
|
.translated_3d(Vec3::new(0.5, 0.5, 0.0));
|
||||||
|
} else {
|
||||||
|
mat *= Mat4::identity().translated_3d(mount_offset + Vec3::new(0.5, 0.5, 0.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(pos) = positions.get_mut(entity) {
|
||||||
|
pos.0 = mat.mul_point(Vec3::zero());
|
||||||
|
}
|
||||||
|
if let Some(ori) = orientations.get_mut(entity) {
|
||||||
|
*ori = Ori::from_unnormalized_vec(mat.mul_direction(mount_dir))
|
||||||
|
.unwrap_or_default();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let v = match is_volume_rider.pos.kind {
|
||||||
|
common::mounting::Volume::Terrain => Vec3::zero(),
|
||||||
|
common::mounting::Volume::Entity(uid) => {
|
||||||
|
if let Some(v) = uid_allocator
|
||||||
|
.retrieve_entity_internal(uid.into())
|
||||||
|
.and_then(|e| velocities.get(e))
|
||||||
|
{
|
||||||
|
v.0
|
||||||
|
} else {
|
||||||
|
Vec3::zero()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if let Some(vel) = velocities.get_mut(entity) {
|
||||||
|
vel.0 = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
let inputs = controllers.get_mut(entity).map(|c| {
|
||||||
|
let actions: Vec<_> = c
|
||||||
|
.actions
|
||||||
|
.drain_filter(|action| match action {
|
||||||
|
ControlAction::StartInput { input: i, .. }
|
||||||
|
| ControlAction::CancelInput(i) => {
|
||||||
|
matches!(i, InputKind::Jump | InputKind::Fly | InputKind::Roll)
|
||||||
|
},
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let inputs = c.inputs.clone();
|
||||||
|
|
||||||
|
(actions, inputs)
|
||||||
|
});
|
||||||
|
|
||||||
|
if is_volume_rider.block.is_controller() {
|
||||||
|
if let Some((actions, inputs)) = inputs {
|
||||||
|
match is_volume_rider.pos.kind {
|
||||||
|
common::mounting::Volume::Entity(uid) => {
|
||||||
|
if let Some(controller) = uid_allocator
|
||||||
|
.retrieve_entity_internal(uid.into())
|
||||||
|
.and_then(|e| controllers.get_mut(e))
|
||||||
|
{
|
||||||
|
controller.inputs = inputs;
|
||||||
|
controller.actions = actions;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
common::mounting::Volume::Terrain => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ use common::{
|
|||||||
consts::{AIR_DENSITY, FRIC_GROUND, GRAVITY},
|
consts::{AIR_DENSITY, FRIC_GROUND, GRAVITY},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::Rider,
|
mounting::{Rider, VolumeRider},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::DeltaTime,
|
resources::DeltaTime,
|
||||||
states,
|
states,
|
||||||
@ -122,6 +122,7 @@ pub struct PhysicsRead<'a> {
|
|||||||
masses: ReadStorage<'a, Mass>,
|
masses: ReadStorage<'a, Mass>,
|
||||||
colliders: ReadStorage<'a, Collider>,
|
colliders: ReadStorage<'a, Collider>,
|
||||||
is_ridings: ReadStorage<'a, Is<Rider>>,
|
is_ridings: ReadStorage<'a, Is<Rider>>,
|
||||||
|
is_volume_ridings: ReadStorage<'a, Is<VolumeRider>>,
|
||||||
projectiles: ReadStorage<'a, Projectile>,
|
projectiles: ReadStorage<'a, Projectile>,
|
||||||
char_states: ReadStorage<'a, CharacterState>,
|
char_states: ReadStorage<'a, CharacterState>,
|
||||||
bodies: ReadStorage<'a, Body>,
|
bodies: ReadStorage<'a, Body>,
|
||||||
@ -335,6 +336,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
&read.masses,
|
&read.masses,
|
||||||
&read.colliders,
|
&read.colliders,
|
||||||
read.is_ridings.maybe(),
|
read.is_ridings.maybe(),
|
||||||
|
read.is_volume_ridings.maybe(),
|
||||||
read.stickies.maybe(),
|
read.stickies.maybe(),
|
||||||
read.immovables.maybe(),
|
read.immovables.maybe(),
|
||||||
&mut write.physics_states,
|
&mut write.physics_states,
|
||||||
@ -359,6 +361,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
mass,
|
mass,
|
||||||
collider,
|
collider,
|
||||||
is_riding,
|
is_riding,
|
||||||
|
is_volume_riding,
|
||||||
sticky,
|
sticky,
|
||||||
immovable,
|
immovable,
|
||||||
physics,
|
physics,
|
||||||
@ -366,8 +369,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
char_state_maybe,
|
char_state_maybe,
|
||||||
)| {
|
)| {
|
||||||
let is_sticky = sticky.is_some();
|
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 is_mid_air = physics.on_surface().is_none();
|
||||||
let mut entity_entity_collision_checks = 0;
|
let mut entity_entity_collision_checks = 0;
|
||||||
let mut entity_entity_collisions = 0;
|
let mut entity_entity_collisions = 0;
|
||||||
@ -492,7 +494,9 @@ impl<'a> PhysicsData<'a> {
|
|||||||
mass: *mass_other,
|
mass: *mass_other,
|
||||||
},
|
},
|
||||||
vel,
|
vel,
|
||||||
is_riding.is_some() || other_is_riding_maybe.is_some(),
|
is_riding.is_some()
|
||||||
|
|| is_volume_riding.is_some()
|
||||||
|
|| other_is_riding_maybe.is_some(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -547,11 +551,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
{
|
{
|
||||||
let vol = match collider {
|
let vol = collider.get_vol(&voxel_colliders_manifest);
|
||||||
Collider::Voxel { id } => voxel_colliders_manifest.colliders.get(id),
|
|
||||||
Collider::Volume(vol) => Some(&**vol),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(vol) = vol {
|
if let Some(vol) = vol {
|
||||||
let sphere = voxel_collider_bounding_sphere(vol, pos, ori);
|
let sphere = voxel_collider_bounding_sphere(vol, pos, ori);
|
||||||
@ -588,6 +588,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
!&write.pos_vel_ori_defers, // This is the one we are adding
|
!&write.pos_vel_ori_defers, // This is the one we are adding
|
||||||
write.previous_phys_cache.mask(),
|
write.previous_phys_cache.mask(),
|
||||||
!&read.is_ridings,
|
!&read.is_ridings,
|
||||||
|
!&read.is_volume_ridings,
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
.map(|t| (t.0, *t.2, *t.3, *t.4))
|
.map(|t| (t.0, *t.2, *t.3, *t.4))
|
||||||
@ -620,9 +621,9 @@ impl<'a> PhysicsData<'a> {
|
|||||||
&read.densities,
|
&read.densities,
|
||||||
read.scales.maybe(),
|
read.scales.maybe(),
|
||||||
!&read.is_ridings,
|
!&read.is_ridings,
|
||||||
|
!&read.is_volume_ridings,
|
||||||
)
|
)
|
||||||
.par_join()
|
.par_join()
|
||||||
.filter(|tuple| !matches!(tuple.4, Some(CharacterState::MountSprite(_))))
|
|
||||||
.for_each_init(
|
.for_each_init(
|
||||||
|| {
|
|| {
|
||||||
prof_span!(guard, "velocity update rayon job");
|
prof_span!(guard, "velocity update rayon job");
|
||||||
@ -640,6 +641,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
density,
|
density,
|
||||||
scale,
|
scale,
|
||||||
_,
|
_,
|
||||||
|
_,
|
||||||
)| {
|
)| {
|
||||||
let in_loaded_chunk = read
|
let in_loaded_chunk = read
|
||||||
.terrain
|
.terrain
|
||||||
@ -751,12 +753,10 @@ impl<'a> PhysicsData<'a> {
|
|||||||
&mut write.pos_vel_ori_defers,
|
&mut write.pos_vel_ori_defers,
|
||||||
previous_phys_cache,
|
previous_phys_cache,
|
||||||
!&read.is_ridings,
|
!&read.is_ridings,
|
||||||
|
!&read.is_volume_ridings,
|
||||||
)
|
)
|
||||||
.par_join()
|
.par_join()
|
||||||
.filter(|tuple| {
|
.filter(|tuple| tuple.3.is_voxel() == terrain_like_entities)
|
||||||
tuple.3.is_voxel() == terrain_like_entities
|
|
||||||
&& !matches!(tuple.8, Some(CharacterState::MountSprite(_)))
|
|
||||||
})
|
|
||||||
.map_init(
|
.map_init(
|
||||||
|| {
|
|| {
|
||||||
prof_span!(guard, "physics e<>t rayon job");
|
prof_span!(guard, "physics e<>t rayon job");
|
||||||
@ -777,6 +777,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
pos_vel_ori_defer,
|
pos_vel_ori_defer,
|
||||||
previous_cache,
|
previous_cache,
|
||||||
_,
|
_,
|
||||||
|
_,
|
||||||
)| {
|
)| {
|
||||||
let mut land_on_ground = None;
|
let mut land_on_ground = None;
|
||||||
let mut outcomes = Vec::new();
|
let mut outcomes = Vec::new();
|
||||||
@ -1066,13 +1067,8 @@ impl<'a> PhysicsData<'a> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let voxel_collider = match collider_other {
|
let voxel_collider =
|
||||||
Collider::Voxel { id } => {
|
collider_other.get_vol(&voxel_colliders_manifest);
|
||||||
voxel_colliders_manifest.colliders.get(id)
|
|
||||||
},
|
|
||||||
Collider::Volume(vol) => Some(&**vol),
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// use bounding cylinder regardless of our collider
|
// use bounding cylinder regardless of our collider
|
||||||
// TODO: extract point-terrain collision above to its own
|
// TODO: extract point-terrain collision above to its own
|
||||||
|
@ -145,7 +145,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
{
|
{
|
||||||
match character_state {
|
match character_state {
|
||||||
// Sitting accelerates recharging energy the most
|
// Sitting accelerates recharging energy the most
|
||||||
CharacterState::Sit | CharacterState::MountSprite(_) => {
|
CharacterState::Sit => {
|
||||||
if energy.needs_regen() {
|
if energy.needs_regen() {
|
||||||
energy.regen(SIT_ENERGY_REGEN_ACCEL, dt);
|
energy.regen(SIT_ENERGY_REGEN_ACCEL, dt);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ use common::{
|
|||||||
SkillSet, Stance, Stats, Vel,
|
SkillSet, Stance, Stats, Vel,
|
||||||
},
|
},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::{Mount, Rider},
|
mounting::{Mount, Rider, VolumeRider},
|
||||||
path::TraversalConfig,
|
path::TraversalConfig,
|
||||||
resources::{DeltaTime, Time, TimeOfDay},
|
resources::{DeltaTime, Time, TimeOfDay},
|
||||||
rtsim::{Actor, RtSimEntity},
|
rtsim::{Actor, RtSimEntity},
|
||||||
@ -236,6 +236,7 @@ pub struct ReadData<'a> {
|
|||||||
pub bodies: ReadStorage<'a, Body>,
|
pub bodies: ReadStorage<'a, Body>,
|
||||||
pub is_mounts: ReadStorage<'a, Is<Mount>>,
|
pub is_mounts: ReadStorage<'a, Is<Mount>>,
|
||||||
pub is_riders: ReadStorage<'a, Is<Rider>>,
|
pub is_riders: ReadStorage<'a, Is<Rider>>,
|
||||||
|
pub is_volume_riders: ReadStorage<'a, Is<VolumeRider>>,
|
||||||
pub time_of_day: Read<'a, TimeOfDay>,
|
pub time_of_day: Read<'a, TimeOfDay>,
|
||||||
pub light_emitter: ReadStorage<'a, LightEmitter>,
|
pub light_emitter: ReadStorage<'a, LightEmitter>,
|
||||||
#[cfg(feature = "worldgen")]
|
#[cfg(feature = "worldgen")]
|
||||||
|
@ -229,6 +229,7 @@ fn position_mut<T>(
|
|||||||
descriptor: &str,
|
descriptor: &str,
|
||||||
f: impl for<'a> FnOnce(&'a mut comp::Pos) -> T,
|
f: impl for<'a> FnOnce(&'a mut comp::Pos) -> T,
|
||||||
) -> CmdResult<T> {
|
) -> CmdResult<T> {
|
||||||
|
// TODO: Handle volume mount
|
||||||
let entity = server
|
let entity = server
|
||||||
.state
|
.state
|
||||||
.ecs()
|
.ecs()
|
||||||
|
@ -14,10 +14,10 @@ use common::{
|
|||||||
tool::{AbilityMap, ToolKind},
|
tool::{AbilityMap, ToolKind},
|
||||||
Inventory, LootOwner, Pos, SkillGroupKind,
|
Inventory, LootOwner, Pos, SkillGroupKind,
|
||||||
},
|
},
|
||||||
consts::{MAX_MOUNT_RANGE, SOUND_TRAVEL_DIST_PER_VOLUME},
|
consts::{MAX_MOUNT_RANGE, MAX_SPRITE_MOUNT_RANGE, SOUND_TRAVEL_DIST_PER_VOLUME},
|
||||||
event::EventBus,
|
event::EventBus,
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::{Mount, Mounting, Rider},
|
mounting::{Mounting, Rider, VolumeMounting, VolumePos, VolumeRider},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
terrain::{Block, SpriteKind},
|
terrain::{Block, SpriteKind},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
@ -103,53 +103,78 @@ pub fn handle_npc_interaction(
|
|||||||
pub fn handle_mount(server: &mut Server, rider: EcsEntity, mount: EcsEntity) {
|
pub fn handle_mount(server: &mut Server, rider: EcsEntity, mount: EcsEntity) {
|
||||||
let state = server.state_mut();
|
let state = server.state_mut();
|
||||||
|
|
||||||
if state.ecs().read_storage::<Is<Rider>>().get(rider).is_none() {
|
let within_range = {
|
||||||
let not_mounting_yet = state.ecs().read_storage::<Is<Mount>>().get(mount).is_none();
|
let positions = state.ecs().read_storage::<Pos>();
|
||||||
|
within_mounting_range(positions.get(rider), positions.get(mount))
|
||||||
|
};
|
||||||
|
|
||||||
let within_range = || {
|
if within_range {
|
||||||
let positions = state.ecs().read_storage::<Pos>();
|
let uids = state.ecs().read_storage::<Uid>();
|
||||||
within_mounting_range(positions.get(rider), positions.get(mount))
|
if let (Some(rider_uid), Some(mount_uid)) =
|
||||||
};
|
(uids.get(rider).copied(), uids.get(mount).copied())
|
||||||
let healths = state.ecs().read_storage::<comp::Health>();
|
{
|
||||||
let alive = |e| healths.get(e).map_or(true, |h| !h.is_dead);
|
let is_pet = matches!(
|
||||||
|
state
|
||||||
if not_mounting_yet && within_range() && alive(rider) && alive(mount) {
|
|
||||||
let uids = state.ecs().read_storage::<Uid>();
|
|
||||||
if let (Some(rider_uid), Some(mount_uid)) =
|
|
||||||
(uids.get(rider).copied(), uids.get(mount).copied())
|
|
||||||
{
|
|
||||||
let is_pet = matches!(
|
|
||||||
state
|
|
||||||
.ecs()
|
|
||||||
.read_storage::<comp::Alignment>()
|
|
||||||
.get(mount),
|
|
||||||
Some(comp::Alignment::Owned(owner)) if *owner == rider_uid,
|
|
||||||
);
|
|
||||||
|
|
||||||
let can_ride = state
|
|
||||||
.ecs()
|
.ecs()
|
||||||
.read_storage()
|
.read_storage::<comp::Alignment>()
|
||||||
.get(mount)
|
.get(mount),
|
||||||
.map_or(false, |mount_body| {
|
Some(comp::Alignment::Owned(owner)) if *owner == rider_uid,
|
||||||
is_mountable(mount_body, state.ecs().read_storage().get(rider))
|
);
|
||||||
});
|
|
||||||
|
|
||||||
if is_pet && can_ride {
|
let can_ride = state
|
||||||
drop(uids);
|
.ecs()
|
||||||
drop(healths);
|
.read_storage()
|
||||||
let _ = state.link(Mounting {
|
.get(mount)
|
||||||
mount: mount_uid,
|
.map_or(false, |mount_body| {
|
||||||
rider: rider_uid,
|
is_mountable(mount_body, state.ecs().read_storage().get(rider))
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
if is_pet && can_ride {
|
||||||
|
drop(uids);
|
||||||
|
let _ = state.link(Mounting {
|
||||||
|
mount: mount_uid,
|
||||||
|
rider: rider_uid,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn handle_mount_volume(server: &mut Server, rider: EcsEntity, volume_pos: VolumePos) {
|
||||||
|
let state = server.state_mut();
|
||||||
|
|
||||||
|
let block_transform = volume_pos.get_block_and_transform(
|
||||||
|
&state.ecs().read_resource(),
|
||||||
|
&state.ecs().read_resource(),
|
||||||
|
&state.ecs().read_storage(),
|
||||||
|
&state.ecs().read_storage(),
|
||||||
|
&state.ecs().read_storage(),
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some((transform, block)) = block_transform
|
||||||
|
&& let Some(mount_offset) = block.mount_offset() {
|
||||||
|
let mount_pos = (Mat4::from(transform) * mount_offset.0.with_w(1.0)).xyz();
|
||||||
|
let within_range = {
|
||||||
|
let positions = state.ecs().read_storage::<Pos>();
|
||||||
|
positions.get(rider).map_or(false, |pos| pos.0.distance_squared(mount_pos) < MAX_SPRITE_MOUNT_RANGE * MAX_SPRITE_MOUNT_RANGE)
|
||||||
|
};
|
||||||
|
|
||||||
|
let maybe_uid = state.ecs().read_storage::<Uid>().get(rider).copied();
|
||||||
|
|
||||||
|
if let Some(rider) = maybe_uid && within_range {
|
||||||
|
let _ = state.link(VolumeMounting {
|
||||||
|
pos: volume_pos,
|
||||||
|
block,
|
||||||
|
rider,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn handle_unmount(server: &mut Server, rider: EcsEntity) {
|
pub fn handle_unmount(server: &mut Server, rider: EcsEntity) {
|
||||||
let state = server.state_mut();
|
let state = server.state_mut();
|
||||||
state.ecs().write_storage::<Is<Rider>>().remove(rider);
|
state.ecs().write_storage::<Is<Rider>>().remove(rider);
|
||||||
|
state.ecs().write_storage::<Is<VolumeRider>>().remove(rider);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn within_mounting_range(player_position: Option<&Pos>, mount_position: Option<&Pos>) -> bool {
|
fn within_mounting_range(player_position: Option<&Pos>, mount_position: Option<&Pos>) -> bool {
|
||||||
|
@ -21,7 +21,7 @@ use common::{
|
|||||||
trade::Trades,
|
trade::Trades,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
util::find_dist::{self, FindDist},
|
util::find_dist::{self, FindDist},
|
||||||
vol::ReadVol,
|
vol::ReadVol, mounting::VolumePos,
|
||||||
};
|
};
|
||||||
use common_net::sync::WorldSyncExt;
|
use common_net::sync::WorldSyncExt;
|
||||||
use common_state::State;
|
use common_state::State;
|
||||||
@ -745,27 +745,24 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
let ability_map = &state.ecs().read_resource::<AbilityMap>();
|
let ability_map = &state.ecs().read_resource::<AbilityMap>();
|
||||||
let msm = state.ecs().read_resource::<MaterialStatManifest>();
|
let msm = state.ecs().read_resource::<MaterialStatManifest>();
|
||||||
|
|
||||||
let get_craft_sprite = |state, sprite_pos: Option<Vec3<i32>>| {
|
let get_craft_sprite = |state, sprite_pos: Option<VolumePos>| {
|
||||||
sprite_pos
|
sprite_pos
|
||||||
.filter(|pos| {
|
.filter(|pos| {
|
||||||
let entity_cylinder = get_cylinder(state, entity);
|
let entity_cylinder = get_cylinder(state, entity);
|
||||||
let in_range = within_pickup_range(entity_cylinder, || {
|
let in_range = within_pickup_range(entity_cylinder, || {
|
||||||
Some(find_dist::Cube {
|
pos.get_block_and_transform(&state.terrain(), &state.ecs().read_resource(), &state.read_storage(), &state.read_storage(), &state.read_storage()).map(|(transform, _)| transform.mul_point(Vec3::broadcast(0.5)))
|
||||||
min: pos.as_(),
|
|
||||||
side_length: 1.0,
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
if !in_range {
|
if !in_range {
|
||||||
debug!(
|
debug!(
|
||||||
?entity_cylinder,
|
?entity_cylinder,
|
||||||
"Failed to craft recipe as not within range of required sprite, \
|
"Failed to craft recipe as not within range of required sprite, \
|
||||||
sprite pos: {}",
|
sprite pos: {:?}",
|
||||||
pos
|
pos
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
in_range
|
in_range
|
||||||
})
|
})
|
||||||
.and_then(|pos| state.terrain().get(pos).ok().copied())
|
.and_then(|pos| pos.get_block(&state.terrain(), &state.ecs().read_resource(), &state.read_storage()))
|
||||||
.and_then(|block| block.get_sprite())
|
.and_then(|block| block.get_sprite())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
events::interaction::handle_tame_pet, persistence::PersistedComponents, state_ext::StateExt,
|
events::interaction::{handle_mount_volume, handle_tame_pet},
|
||||||
|
persistence::PersistedComponents,
|
||||||
|
state_ext::StateExt,
|
||||||
Server,
|
Server,
|
||||||
};
|
};
|
||||||
use common::event::{EventBus, ServerEvent, ServerEventDiscriminants};
|
use common::event::{EventBus, ServerEvent, ServerEventDiscriminants};
|
||||||
@ -136,6 +138,9 @@ impl Server {
|
|||||||
handle_process_trade_action(self, entity, trade_id, action);
|
handle_process_trade_action(self, entity, trade_id, action);
|
||||||
},
|
},
|
||||||
ServerEvent::Mount(mounter, mountee) => handle_mount(self, mounter, mountee),
|
ServerEvent::Mount(mounter, mountee) => handle_mount(self, mounter, mountee),
|
||||||
|
ServerEvent::MountVolume(mounter, volume) => {
|
||||||
|
handle_mount_volume(self, mounter, volume)
|
||||||
|
},
|
||||||
ServerEvent::Unmount(mounter) => handle_unmount(self, mounter),
|
ServerEvent::Unmount(mounter) => handle_unmount(self, mounter),
|
||||||
ServerEvent::Possess(possessor_uid, possesse_uid) => {
|
ServerEvent::Possess(possessor_uid, possesse_uid) => {
|
||||||
handle_possess(self, possessor_uid, possesse_uid)
|
handle_possess(self, possessor_uid, possesse_uid)
|
||||||
|
@ -23,7 +23,7 @@ use common::{
|
|||||||
},
|
},
|
||||||
effect::Effect,
|
effect::Effect,
|
||||||
link::{Link, LinkHandle},
|
link::{Link, LinkHandle},
|
||||||
mounting::Mounting,
|
mounting::{Mounting, VolumeMounting},
|
||||||
resources::{Secs, Time, TimeOfDay},
|
resources::{Secs, Time, TimeOfDay},
|
||||||
rtsim::{Actor, RtSimEntity},
|
rtsim::{Actor, RtSimEntity},
|
||||||
slowjob::SlowJobPool,
|
slowjob::SlowJobPool,
|
||||||
@ -1099,6 +1099,7 @@ impl StateExt for State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
maintain_link::<Mounting>(self);
|
maintain_link::<Mounting>(self);
|
||||||
|
maintain_link::<VolumeMounting>(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn delete_entity_recorded(
|
fn delete_entity_recorded(
|
||||||
|
@ -12,6 +12,7 @@ use common::{
|
|||||||
Controller, Health, InputKind, Scale,
|
Controller, Health, InputKind, Scale,
|
||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
|
mounting::Volume,
|
||||||
path::TraversalConfig,
|
path::TraversalConfig,
|
||||||
};
|
};
|
||||||
use common_base::prof_span;
|
use common_base::prof_span;
|
||||||
@ -69,6 +70,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
read_data.rtsim_entities.maybe(),
|
read_data.rtsim_entities.maybe(),
|
||||||
!&read_data.is_mounts,
|
!&read_data.is_mounts,
|
||||||
read_data.is_riders.maybe(),
|
read_data.is_riders.maybe(),
|
||||||
|
read_data.is_volume_riders.maybe(),
|
||||||
)
|
)
|
||||||
.par_join()
|
.par_join()
|
||||||
.for_each_init(
|
.for_each_init(
|
||||||
@ -93,6 +95,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
rtsim_entity,
|
rtsim_entity,
|
||||||
_,
|
_,
|
||||||
is_rider,
|
is_rider,
|
||||||
|
is_volume_rider,
|
||||||
)| {
|
)| {
|
||||||
let mut event_emitter = event_bus.emitter();
|
let mut event_emitter = event_bus.emitter();
|
||||||
let mut rng = thread_rng();
|
let mut rng = thread_rng();
|
||||||
@ -104,6 +107,16 @@ impl<'a> System<'a> for Sys {
|
|||||||
.uid_allocator
|
.uid_allocator
|
||||||
.retrieve_entity_internal(is_rider.mount.into())
|
.retrieve_entity_internal(is_rider.mount.into())
|
||||||
})
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
is_volume_rider.and_then(|is_volume_rider| {
|
||||||
|
match is_volume_rider.pos.kind {
|
||||||
|
Volume::Terrain => None,
|
||||||
|
Volume::Entity(uid) => {
|
||||||
|
read_data.uid_allocator.retrieve_entity_internal(uid.into())
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
.unwrap_or(entity);
|
.unwrap_or(entity);
|
||||||
|
|
||||||
let moving_body = read_data.bodies.get(moving_entity);
|
let moving_body = read_data.bodies.get(moving_entity);
|
||||||
|
@ -8,7 +8,7 @@ use common::{
|
|||||||
},
|
},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::Rider,
|
mounting::{Rider, VolumeRider},
|
||||||
resources::{PlayerPhysicsSetting, PlayerPhysicsSettings},
|
resources::{PlayerPhysicsSetting, PlayerPhysicsSettings},
|
||||||
slowjob::SlowJobPool,
|
slowjob::SlowJobPool,
|
||||||
terrain::TerrainGrid,
|
terrain::TerrainGrid,
|
||||||
@ -52,6 +52,7 @@ impl Sys {
|
|||||||
terrain: &ReadExpect<'_, TerrainGrid>,
|
terrain: &ReadExpect<'_, TerrainGrid>,
|
||||||
can_build: &ReadStorage<'_, CanBuild>,
|
can_build: &ReadStorage<'_, CanBuild>,
|
||||||
is_rider: &ReadStorage<'_, Is<Rider>>,
|
is_rider: &ReadStorage<'_, Is<Rider>>,
|
||||||
|
is_volume_rider: &ReadStorage<'_, Is<VolumeRider>>,
|
||||||
force_updates: &ReadStorage<'_, ForceUpdate>,
|
force_updates: &ReadStorage<'_, ForceUpdate>,
|
||||||
skill_set: &mut Option<Cow<'_, SkillSet>>,
|
skill_set: &mut Option<Cow<'_, SkillSet>>,
|
||||||
healths: &ReadStorage<'_, Health>,
|
healths: &ReadStorage<'_, Health>,
|
||||||
@ -126,6 +127,7 @@ impl Sys {
|
|||||||
&& force_updates.get(entity).map_or(true, |force_update| force_update.counter() == force_counter)
|
&& force_updates.get(entity).map_or(true, |force_update| force_update.counter() == force_counter)
|
||||||
&& healths.get(entity).map_or(true, |h| !h.is_dead)
|
&& healths.get(entity).map_or(true, |h| !h.is_dead)
|
||||||
&& is_rider.get(entity).is_none()
|
&& is_rider.get(entity).is_none()
|
||||||
|
&& is_volume_rider.get(entity).is_none()
|
||||||
&& player_physics_setting
|
&& player_physics_setting
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(true, |s| s.client_authoritative())
|
.map_or(true, |s| s.client_authoritative())
|
||||||
@ -322,6 +324,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
ReadStorage<'a, CanBuild>,
|
ReadStorage<'a, CanBuild>,
|
||||||
ReadStorage<'a, ForceUpdate>,
|
ReadStorage<'a, ForceUpdate>,
|
||||||
ReadStorage<'a, Is<Rider>>,
|
ReadStorage<'a, Is<Rider>>,
|
||||||
|
ReadStorage<'a, Is<VolumeRider>>,
|
||||||
WriteStorage<'a, SkillSet>,
|
WriteStorage<'a, SkillSet>,
|
||||||
ReadStorage<'a, Health>,
|
ReadStorage<'a, Health>,
|
||||||
Write<'a, BlockChange>,
|
Write<'a, BlockChange>,
|
||||||
@ -353,6 +356,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
can_build,
|
can_build,
|
||||||
force_updates,
|
force_updates,
|
||||||
is_rider,
|
is_rider,
|
||||||
|
is_volume_rider,
|
||||||
mut skill_sets,
|
mut skill_sets,
|
||||||
healths,
|
healths,
|
||||||
mut block_changes,
|
mut block_changes,
|
||||||
@ -430,6 +434,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
&terrain,
|
&terrain,
|
||||||
&can_build,
|
&can_build,
|
||||||
&is_rider,
|
&is_rider,
|
||||||
|
&is_volume_rider,
|
||||||
&force_updates,
|
&force_updates,
|
||||||
&mut skill_set,
|
&mut skill_set,
|
||||||
&healths,
|
&healths,
|
||||||
|
@ -25,6 +25,7 @@ use common::{
|
|||||||
slot::{InvSlotId, Slot},
|
slot::{InvSlotId, Slot},
|
||||||
Inventory,
|
Inventory,
|
||||||
},
|
},
|
||||||
|
mounting::VolumePos,
|
||||||
recipe::{ComponentKey, Recipe, RecipeInput},
|
recipe::{ComponentKey, Recipe, RecipeInput},
|
||||||
terrain::SpriteKind,
|
terrain::SpriteKind,
|
||||||
};
|
};
|
||||||
@ -123,7 +124,7 @@ pub enum Event {
|
|||||||
pub struct CraftingShow {
|
pub struct CraftingShow {
|
||||||
pub crafting_tab: CraftingTab,
|
pub crafting_tab: CraftingTab,
|
||||||
pub crafting_search_key: Option<String>,
|
pub crafting_search_key: Option<String>,
|
||||||
pub craft_sprite: Option<(Vec3<i32>, SpriteKind)>,
|
pub craft_sprite: Option<(VolumePos, SpriteKind)>,
|
||||||
pub salvage: bool,
|
pub salvage: bool,
|
||||||
pub initialize_repair: bool,
|
pub initialize_repair: bool,
|
||||||
// TODO: Maybe try to do something that doesn't need to allocate?
|
// TODO: Maybe try to do something that doesn't need to allocate?
|
||||||
|
@ -107,7 +107,7 @@ use common::{
|
|||||||
},
|
},
|
||||||
consts::MAX_PICKUP_RANGE,
|
consts::MAX_PICKUP_RANGE,
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::Mount,
|
mounting::{Mount, VolumePos},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::{Secs, Time},
|
resources::{Secs, Time},
|
||||||
slowjob::SlowJobPool,
|
slowjob::SlowJobPool,
|
||||||
@ -710,27 +710,27 @@ pub enum Event {
|
|||||||
|
|
||||||
CraftRecipe {
|
CraftRecipe {
|
||||||
recipe_name: String,
|
recipe_name: String,
|
||||||
craft_sprite: Option<(Vec3<i32>, SpriteKind)>,
|
craft_sprite: Option<(VolumePos, SpriteKind)>,
|
||||||
amount: u32,
|
amount: u32,
|
||||||
},
|
},
|
||||||
SalvageItem {
|
SalvageItem {
|
||||||
slot: InvSlotId,
|
slot: InvSlotId,
|
||||||
salvage_pos: Vec3<i32>,
|
salvage_pos: VolumePos,
|
||||||
},
|
},
|
||||||
CraftModularWeapon {
|
CraftModularWeapon {
|
||||||
primary_slot: InvSlotId,
|
primary_slot: InvSlotId,
|
||||||
secondary_slot: InvSlotId,
|
secondary_slot: InvSlotId,
|
||||||
craft_sprite: Option<Vec3<i32>>,
|
craft_sprite: Option<VolumePos>,
|
||||||
},
|
},
|
||||||
CraftModularWeaponComponent {
|
CraftModularWeaponComponent {
|
||||||
toolkind: ToolKind,
|
toolkind: ToolKind,
|
||||||
material: InvSlotId,
|
material: InvSlotId,
|
||||||
modifier: Option<InvSlotId>,
|
modifier: Option<InvSlotId>,
|
||||||
craft_sprite: Option<Vec3<i32>>,
|
craft_sprite: Option<VolumePos>,
|
||||||
},
|
},
|
||||||
RepairItem {
|
RepairItem {
|
||||||
item: Slot,
|
item: Slot,
|
||||||
sprite_pos: Vec3<i32>,
|
sprite_pos: VolumePos,
|
||||||
},
|
},
|
||||||
InviteMember(Uid),
|
InviteMember(Uid),
|
||||||
AcceptInvite,
|
AcceptInvite,
|
||||||
@ -995,7 +995,7 @@ impl Show {
|
|||||||
pub fn open_crafting_tab(
|
pub fn open_crafting_tab(
|
||||||
&mut self,
|
&mut self,
|
||||||
tab: CraftingTab,
|
tab: CraftingTab,
|
||||||
craft_sprite: Option<(Vec3<i32>, SpriteKind)>,
|
craft_sprite: Option<(VolumePos, SpriteKind)>,
|
||||||
) {
|
) {
|
||||||
self.selected_crafting_tab(tab);
|
self.selected_crafting_tab(tab);
|
||||||
self.crafting(true);
|
self.crafting(true);
|
||||||
@ -1289,7 +1289,7 @@ pub struct Hud {
|
|||||||
item_imgs: ItemImgs,
|
item_imgs: ItemImgs,
|
||||||
fonts: Fonts,
|
fonts: Fonts,
|
||||||
rot_imgs: ImgsRot,
|
rot_imgs: ImgsRot,
|
||||||
failed_block_pickups: HashMap<Vec3<i32>, CollectFailedData>,
|
failed_block_pickups: HashMap<VolumePos, CollectFailedData>,
|
||||||
failed_entity_pickups: HashMap<EcsEntity, CollectFailedData>,
|
failed_entity_pickups: HashMap<EcsEntity, CollectFailedData>,
|
||||||
new_loot_messages: VecDeque<LootMessage>,
|
new_loot_messages: VecDeque<LootMessage>,
|
||||||
new_messages: VecDeque<comp::ChatMsg>,
|
new_messages: VecDeque<comp::ChatMsg>,
|
||||||
@ -2040,7 +2040,8 @@ impl Hud {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render overtime for an interactable block
|
// Render overtime for an interactable block
|
||||||
if let Some(Interactable::Block(block, pos, interaction)) = interactable {
|
if let Some(Interactable::Block(block, pos, interaction)) = interactable
|
||||||
|
&& let Some((mat, _)) = pos.get_block_and_transform(&ecs.read_resource(), &ecs.read_resource(), &ecs.read_storage(), &ecs.read_storage(), &ecs.read_storage()) {
|
||||||
let overitem_id = overitem_walker.next(
|
let overitem_id = overitem_walker.next(
|
||||||
&mut self.ids.overitems,
|
&mut self.ids.overitems,
|
||||||
&mut ui_widgets.widget_id_generator(),
|
&mut ui_widgets.widget_id_generator(),
|
||||||
@ -2050,7 +2051,8 @@ impl Hud {
|
|||||||
active: true,
|
active: true,
|
||||||
pickup_failed_pulse: self.failed_block_pickups.get(pos).cloned(),
|
pickup_failed_pulse: self.failed_block_pickups.get(pos).cloned(),
|
||||||
};
|
};
|
||||||
let pos = pos.map(|e| e as f32 + 0.5);
|
|
||||||
|
let pos = mat.mul_point(Vec3::broadcast(0.5));
|
||||||
let over_pos = pos + Vec3::unit_z() * 0.7;
|
let over_pos = pos + Vec3::unit_z() * 0.7;
|
||||||
|
|
||||||
let interaction_text = || match interaction {
|
let interaction_text = || match interaction {
|
||||||
@ -2107,10 +2109,9 @@ impl Hud {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
BlockInteraction::Mount(_) => vec![(
|
BlockInteraction::Mount => {
|
||||||
Some(GameInput::Interact),
|
vec![(Some(GameInput::Mount), i18n.get_msg("hud-sit").to_string())]
|
||||||
i18n.get_msg("hud-sit").to_string(),
|
},
|
||||||
)],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// This is only done once per frame, so it's not a performance issue
|
// This is only done once per frame, so it's not a performance issue
|
||||||
@ -4349,7 +4350,7 @@ impl Hud {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_failed_block_pickup(&mut self, pos: Vec3<i32>, reason: HudCollectFailedReason) {
|
pub fn add_failed_block_pickup(&mut self, pos: VolumePos, reason: HudCollectFailedReason) {
|
||||||
self.failed_block_pickups
|
self.failed_block_pickups
|
||||||
.insert(pos, CollectFailedData::new(self.pulse, reason));
|
.insert(pos, CollectFailedData::new(self.pulse, reason));
|
||||||
}
|
}
|
||||||
@ -4730,16 +4731,31 @@ impl Hud {
|
|||||||
.handle_event(conrod_core::event::Input::Text("\t".to_string()));
|
.handle_event(conrod_core::event::Input::Text("\t".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop selecting a sprite to perform crafting with when out of range
|
// Stop selecting a sprite to perform crafting with when out of range or sprite
|
||||||
|
// has been removed
|
||||||
self.show.crafting_fields.craft_sprite =
|
self.show.crafting_fields.craft_sprite =
|
||||||
self.show.crafting_fields.craft_sprite.filter(|(pos, _)| {
|
self.show
|
||||||
self.show.crafting
|
.crafting_fields
|
||||||
&& if let Some(player_pos) = client.position() {
|
.craft_sprite
|
||||||
pos.map(|e| e as f32 + 0.5).distance(player_pos) < MAX_PICKUP_RANGE
|
.filter(|(pos, sprite)| {
|
||||||
} else {
|
self.show.crafting
|
||||||
false
|
&& if let Some(player_pos) = client.position() {
|
||||||
}
|
pos.get_block_and_transform(
|
||||||
});
|
&client.state().terrain(),
|
||||||
|
&client.state().ecs().read_resource(),
|
||||||
|
&client.state().read_storage(),
|
||||||
|
&client.state().read_storage(),
|
||||||
|
&client.state().read_storage(),
|
||||||
|
)
|
||||||
|
.map_or(false, |(mat, block)| {
|
||||||
|
block.get_sprite() == Some(*sprite)
|
||||||
|
&& mat.mul_point(Vec3::broadcast(0.5)).distance(player_pos)
|
||||||
|
< MAX_PICKUP_RANGE
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Optimization: skip maintaining UI when it's off.
|
// Optimization: skip maintaining UI when it's off.
|
||||||
if !self.show.ui {
|
if !self.show.ui {
|
||||||
|
@ -43,7 +43,7 @@ use common::{
|
|||||||
Scale, SkillSet, Stance, Vel,
|
Scale, SkillSet, Stance, Vel,
|
||||||
},
|
},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::Rider,
|
mounting::{Rider, VolumeRider},
|
||||||
resources::{DeltaTime, Time},
|
resources::{DeltaTime, Time},
|
||||||
states::{equipping, idle, utils::StageSection, wielding},
|
states::{equipping, idle, utils::StageSection, wielding},
|
||||||
terrain::{Block, TerrainChunk, TerrainGrid},
|
terrain::{Block, TerrainChunk, TerrainGrid},
|
||||||
@ -830,7 +830,7 @@ impl FigureMgr {
|
|||||||
inventory,
|
inventory,
|
||||||
item,
|
item,
|
||||||
light_emitter,
|
light_emitter,
|
||||||
(is_rider, collider, stance, skillset),
|
(is_rider, is_volume_rider, collider, stance, skillset),
|
||||||
),
|
),
|
||||||
) in (
|
) in (
|
||||||
&ecs.entities(),
|
&ecs.entities(),
|
||||||
@ -850,6 +850,7 @@ impl FigureMgr {
|
|||||||
ecs.read_storage::<LightEmitter>().maybe(),
|
ecs.read_storage::<LightEmitter>().maybe(),
|
||||||
(
|
(
|
||||||
ecs.read_storage::<Is<Rider>>().maybe(),
|
ecs.read_storage::<Is<Rider>>().maybe(),
|
||||||
|
ecs.read_storage::<Is<VolumeRider>>().maybe(),
|
||||||
ecs.read_storage::<Collider>().maybe(),
|
ecs.read_storage::<Collider>().maybe(),
|
||||||
ecs.read_storage::<Stance>().maybe(),
|
ecs.read_storage::<Stance>().maybe(),
|
||||||
ecs.read_storage::<SkillSet>().maybe(),
|
ecs.read_storage::<SkillSet>().maybe(),
|
||||||
@ -1029,11 +1030,15 @@ impl FigureMgr {
|
|||||||
|
|
||||||
// If a mount exists, get its animated mounting transform and its position
|
// If a mount exists, get its animated mounting transform and its position
|
||||||
let mount_transform_pos = (|| -> Option<_> {
|
let mount_transform_pos = (|| -> Option<_> {
|
||||||
let mount = is_rider?.mount;
|
if let Some(is_rider) = is_rider {
|
||||||
let mount = uid_allocator.retrieve_entity_internal(mount.into())?;
|
let mount = is_rider.mount;
|
||||||
let body = *bodies.get(mount)?;
|
let mount = uid_allocator.retrieve_entity_internal(mount.into())?;
|
||||||
let meta = self.states.get_mut(&body, &mount)?;
|
let body = *bodies.get(mount)?;
|
||||||
Some((meta.mount_transform, meta.mount_world_pos))
|
let meta = self.states.get_mut(&body, &mount)?;
|
||||||
|
Some((meta.mount_transform, meta.mount_world_pos))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
let body = *body;
|
let body = *body;
|
||||||
@ -1111,7 +1116,7 @@ impl FigureMgr {
|
|||||||
physics.on_ground.is_some(),
|
physics.on_ground.is_some(),
|
||||||
rel_vel.magnitude_squared() > 0.01, // Moving
|
rel_vel.magnitude_squared() > 0.01, // Moving
|
||||||
physics.in_liquid().is_some(), // In water
|
physics.in_liquid().is_some(), // In water
|
||||||
is_rider.is_some(),
|
is_rider.is_some() || is_volume_rider.is_some(),
|
||||||
physics.skating_active,
|
physics.skating_active,
|
||||||
) {
|
) {
|
||||||
// Standing or Skating
|
// Standing or Skating
|
||||||
@ -2059,7 +2064,7 @@ impl FigureMgr {
|
|||||||
skeleton_attr,
|
skeleton_attr,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
CharacterState::Sit { .. } | CharacterState::MountSprite(_) => {
|
CharacterState::Sit { .. } => {
|
||||||
anim::character::SitAnimation::update_skeleton(
|
anim::character::SitAnimation::update_skeleton(
|
||||||
&target_base,
|
&target_base,
|
||||||
(active_tool_kind, second_tool_kind, time),
|
(active_tool_kind, second_tool_kind, time),
|
||||||
@ -6099,7 +6104,7 @@ impl FigureMgr {
|
|||||||
pos.0.into(),
|
pos.0.into(),
|
||||||
ori.into_vec4().into(),
|
ori.into_vec4().into(),
|
||||||
vk,
|
vk,
|
||||||
Arc::clone(vol),
|
Arc::clone(&vol),
|
||||||
tick,
|
tick,
|
||||||
&slow_jobs,
|
&slow_jobs,
|
||||||
terrain,
|
terrain,
|
||||||
|
@ -1,20 +1,17 @@
|
|||||||
use crate::hud::CraftingTab;
|
use crate::hud::CraftingTab;
|
||||||
use common::{
|
use common::terrain::{Block, BlockKind, SpriteKind};
|
||||||
states::utils::sprite_mount_points,
|
|
||||||
terrain::{Block, BlockKind, SpriteKind},
|
|
||||||
};
|
|
||||||
use common_base::span;
|
use common_base::span;
|
||||||
use rand::prelude::*;
|
use rand::prelude::*;
|
||||||
use rand_chacha::ChaCha8Rng;
|
use rand_chacha::ChaCha8Rng;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
pub enum Interaction {
|
pub enum Interaction {
|
||||||
/// This covers mining, unlocking, and regular collectable things (e.g.
|
/// This covers mining, unlocking, and regular collectable things (e.g.
|
||||||
/// twigs).
|
/// twigs).
|
||||||
Collect,
|
Collect,
|
||||||
Craft(CraftingTab),
|
Craft(CraftingTab),
|
||||||
Mount(Vec<Vec3<f32>>),
|
Mount,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum FireplaceType {
|
pub enum FireplaceType {
|
||||||
@ -175,21 +172,7 @@ impl BlocksOfInterest {
|
|||||||
Some(SpriteKind::RepairBench) => {
|
Some(SpriteKind::RepairBench) => {
|
||||||
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
|
interactables.push((pos, Interaction::Craft(CraftingTab::All)))
|
||||||
},
|
},
|
||||||
Some(
|
_ if block.is_mountable() => interactables.push((pos, Interaction::Mount)),
|
||||||
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(),
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
_ => {},
|
_ => {},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use ordered_float::OrderedFloat;
|
use ordered_float::OrderedFloat;
|
||||||
use specs::{Join, WorldExt};
|
use specs::{Join, ReadStorage, WorldExt};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
@ -9,12 +9,12 @@ use super::{
|
|||||||
use client::Client;
|
use client::Client;
|
||||||
use common::{
|
use common::{
|
||||||
comp,
|
comp,
|
||||||
comp::{tool::ToolKind, CharacterState},
|
comp::{ship::figuredata::VOXEL_COLLIDER_MANIFEST, tool::ToolKind, Collider},
|
||||||
consts::MAX_PICKUP_RANGE,
|
consts::{MAX_PICKUP_RANGE, MAX_SPRITE_MOUNT_RANGE},
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::Mount,
|
mounting::{Mount, VolumePos, VolumeRider},
|
||||||
states::utils::SPRITE_MOUNT_RANGE_SQR,
|
|
||||||
terrain::{Block, TerrainGrid, UnlockKind},
|
terrain::{Block, TerrainGrid, UnlockKind},
|
||||||
|
uid::{Uid, UidAllocator},
|
||||||
util::find_dist::{Cube, Cylinder, FindDist},
|
util::find_dist::{Cube, Cylinder, FindDist},
|
||||||
vol::ReadVol,
|
vol::ReadVol,
|
||||||
};
|
};
|
||||||
@ -33,12 +33,12 @@ pub enum BlockInteraction {
|
|||||||
// TODO: mining blocks don't use the interaction key, so it might not be the best abstraction
|
// 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
|
// to have them here, will see how things turn out
|
||||||
Mine(ToolKind),
|
Mine(ToolKind),
|
||||||
Mount(Vec<Vec3<f32>>),
|
Mount,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Interactable {
|
pub enum Interactable {
|
||||||
Block(Block, Vec3<i32>, BlockInteraction),
|
Block(Block, VolumePos, BlockInteraction),
|
||||||
Entity(specs::Entity),
|
Entity(specs::Entity),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,28 +52,34 @@ impl Interactable {
|
|||||||
|
|
||||||
fn from_block_pos(
|
fn from_block_pos(
|
||||||
terrain: &TerrainGrid,
|
terrain: &TerrainGrid,
|
||||||
pos: Vec3<i32>,
|
uid_allocator: &UidAllocator,
|
||||||
|
colliders: &ReadStorage<Collider>,
|
||||||
|
volume_pos: VolumePos,
|
||||||
interaction: Interaction,
|
interaction: Interaction,
|
||||||
) -> Option<Self> {
|
) -> Option<Self> {
|
||||||
let Ok(&block) = terrain.get(pos) else { return None };
|
let Some(block) = volume_pos.get_block(terrain, uid_allocator, colliders) else { return None };
|
||||||
let block_interaction = match interaction {
|
let block_interaction = match interaction {
|
||||||
Interaction::Collect => {
|
Interaction::Collect => {
|
||||||
// Check if this is an unlockable sprite
|
// Check if this is an unlockable sprite
|
||||||
let unlock = block.get_sprite().and_then(|sprite| {
|
let unlock = match volume_pos.kind {
|
||||||
let Some(chunk) = terrain.pos_chunk(pos) else { return None };
|
common::mounting::Volume::Terrain => block.get_sprite().and_then(|sprite| {
|
||||||
let sprite_chunk_pos = TerrainGrid::chunk_offs(pos);
|
let Some(chunk) = terrain.pos_chunk(volume_pos.pos) else { return None };
|
||||||
let sprite_cfg = chunk.meta().sprite_cfg_at(sprite_chunk_pos);
|
let sprite_chunk_pos = TerrainGrid::chunk_offs(volume_pos.pos);
|
||||||
let unlock_condition = sprite.unlock_condition(sprite_cfg.cloned());
|
let sprite_cfg = chunk.meta().sprite_cfg_at(sprite_chunk_pos);
|
||||||
// HACK: No other way to distinguish between things that should be unlockable
|
let unlock_condition = sprite.unlock_condition(sprite_cfg.cloned());
|
||||||
// and regular sprites with the current unlock_condition method so we hack
|
// HACK: No other way to distinguish between things that should be
|
||||||
// around that by saying that it is a regular collectible sprite if
|
// unlockable and regular sprites with the current
|
||||||
// `unlock_condition` returns UnlockKind::Free and the cfg was `None`.
|
// unlock_condition method so we hack around that by
|
||||||
if sprite_cfg.is_some() || !matches!(&unlock_condition, UnlockKind::Free) {
|
// saying that it is a regular collectible sprite if
|
||||||
Some(unlock_condition)
|
// `unlock_condition` returns UnlockKind::Free and the cfg was `None`.
|
||||||
} else {
|
if sprite_cfg.is_some() || !matches!(&unlock_condition, UnlockKind::Free) {
|
||||||
None
|
Some(unlock_condition)
|
||||||
}
|
} else {
|
||||||
});
|
None
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
common::mounting::Volume::Entity(_) => None,
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(unlock) = unlock {
|
if let Some(unlock) = unlock {
|
||||||
BlockInteraction::Unlock(unlock)
|
BlockInteraction::Unlock(unlock)
|
||||||
@ -84,9 +90,9 @@ impl Interactable {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Interaction::Craft(tab) => BlockInteraction::Craft(tab),
|
Interaction::Craft(tab) => BlockInteraction::Craft(tab),
|
||||||
Interaction::Mount(points) => BlockInteraction::Mount(points),
|
Interaction::Mount => BlockInteraction::Mount,
|
||||||
};
|
};
|
||||||
Some(Self::Block(block, pos, block_interaction))
|
Some(Self::Block(block, volume_pos, block_interaction))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +136,11 @@ pub(super) fn select_interactable(
|
|||||||
collect_target.and_then(|t| {
|
collect_target.and_then(|t| {
|
||||||
if Some(t.distance) == nearest_dist {
|
if Some(t.distance) == nearest_dist {
|
||||||
terrain.get(t.position_int()).ok().map(|&b| {
|
terrain.get(t.position_int()).ok().map(|&b| {
|
||||||
Interactable::Block(b, t.position_int(), BlockInteraction::Collect)
|
Interactable::Block(
|
||||||
|
b,
|
||||||
|
VolumePos::terrain(t.position_int()),
|
||||||
|
BlockInteraction::Collect,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
@ -149,7 +159,7 @@ pub(super) fn select_interactable(
|
|||||||
if let Some(mine_tool) = b.mine_tool() && b.is_air() {
|
if let Some(mine_tool) = b.mine_tool() && b.is_air() {
|
||||||
Some(Interactable::Block(
|
Some(Interactable::Block(
|
||||||
b,
|
b,
|
||||||
t.position_int(),
|
VolumePos::terrain(t.position_int()),
|
||||||
BlockInteraction::Mine(mine_tool),
|
BlockInteraction::Mine(mine_tool),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
@ -232,6 +242,49 @@ pub(super) fn select_interactable(
|
|||||||
});
|
});
|
||||||
let scene_terrain = scene.terrain();
|
let scene_terrain = scene.terrain();
|
||||||
|
|
||||||
|
let voxel_colliders_manifest = VOXEL_COLLIDER_MANIFEST.read();
|
||||||
|
|
||||||
|
let volumes_data = (
|
||||||
|
&ecs.entities(),
|
||||||
|
&ecs.read_storage::<Uid>(),
|
||||||
|
&ecs.read_storage::<comp::Body>(),
|
||||||
|
&ecs.read_storage::<comp::Pos>(),
|
||||||
|
&ecs.read_storage::<comp::Ori>(),
|
||||||
|
&ecs.read_storage::<comp::Collider>(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let volumes = volumes_data
|
||||||
|
.join()
|
||||||
|
.filter_map(|(entity, uid, body, pos, ori, collider)| {
|
||||||
|
let vol = collider.get_vol(&voxel_colliders_manifest)?;
|
||||||
|
|
||||||
|
let mat = Mat4::from(ori.to_quat()).translated_3d(pos.0)
|
||||||
|
* Mat4::translation_3d(vol.translation);
|
||||||
|
|
||||||
|
let p = mat.inverted().mul_point(player_pos);
|
||||||
|
let aabb = Aabb {
|
||||||
|
min: Vec3::zero(),
|
||||||
|
max: vol.volume().sz.as_(),
|
||||||
|
};
|
||||||
|
if aabb.contains_point(p) || aabb.distance_to_point(p) < search_dist {
|
||||||
|
scene
|
||||||
|
.figure_mgr()
|
||||||
|
.get_blocks_of_interest(entity, body, Some(collider))
|
||||||
|
.map(move |(blocks_of_interest, _)| {
|
||||||
|
blocks_of_interest.interactables.iter().map(
|
||||||
|
move |(block_offset, interaction)| {
|
||||||
|
let wpos = mat.mul_point(block_offset.as_() + 0.5);
|
||||||
|
(wpos, VolumePos::entity(*block_offset, *uid), interaction)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flatten();
|
||||||
|
|
||||||
|
let is_volume_rider = ecs.read_storage::<Is<VolumeRider>>();
|
||||||
// Find closest interactable block
|
// Find closest interactable block
|
||||||
// TODO: consider doing this one first?
|
// TODO: consider doing this one first?
|
||||||
let closest_interactable_block_pos = Spiral2d::new()
|
let closest_interactable_block_pos = Spiral2d::new()
|
||||||
@ -252,39 +305,36 @@ pub(super) fn select_interactable(
|
|||||||
.interactables
|
.interactables
|
||||||
.iter()
|
.iter()
|
||||||
.map(move |(block_offset, interaction)| (chunk_pos + block_offset, interaction))
|
.map(move |(block_offset, interaction)| (chunk_pos + block_offset, interaction))
|
||||||
.filter_map(|(pos, interaction)| {
|
.map(|(pos, interaction)| {
|
||||||
if matches!(player_char_state, Some(CharacterState::MountSprite(_))) && matches!(interaction, Interaction::Mount(_) | Interaction::Collect) {
|
(pos.as_::<f32>() + 0.5, VolumePos::terrain(pos), interaction)
|
||||||
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_RANGE_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, dis), interaction)| (
|
.chain(volumes)
|
||||||
block_pos,
|
.filter(|(wpos, volume_pos, interaction)| {
|
||||||
dis,
|
match interaction {
|
||||||
interaction,
|
Interaction::Mount => !is_volume_rider.contains(player_entity) && wpos.distance_squared(player_pos) < MAX_SPRITE_MOUNT_RANGE * MAX_SPRITE_MOUNT_RANGE &&
|
||||||
))
|
is_volume_rider.join().find(|is_volume_rider| is_volume_rider.pos == *volume_pos).is_none(),
|
||||||
.min_by_key(|(_, dist_sqr, _)| OrderedFloat(*dist_sqr))
|
_ => true,
|
||||||
.map(|(block_pos, _, interaction)| (block_pos, interaction));
|
}
|
||||||
|
})
|
||||||
|
.min_by_key(|(wpos, _, _)| OrderedFloat(wpos.distance_squared(player_pos)));
|
||||||
|
|
||||||
// Return the closest of the 2 closest
|
// Return the closest of the 2 closest
|
||||||
closest_interactable_block_pos
|
closest_interactable_block_pos
|
||||||
.filter(|(block_pos, _)| {
|
.filter(|(wpos, _, _)| {
|
||||||
player_cylinder.min_distance(Cube {
|
player_cylinder.min_distance(Cube {
|
||||||
min: block_pos.as_(),
|
min: *wpos,
|
||||||
side_length: 1.0,
|
side_length: 1.0,
|
||||||
}) < search_dist
|
}) < search_dist
|
||||||
})
|
})
|
||||||
.and_then(|(block_pos, interaction)| {
|
.and_then(|(_, block_pos, interaction)| {
|
||||||
Interactable::from_block_pos(&terrain, block_pos, interaction.clone())
|
Interactable::from_block_pos(
|
||||||
|
&terrain,
|
||||||
|
&ecs.read_resource::<UidAllocator>(),
|
||||||
|
&ecs.read_storage(),
|
||||||
|
block_pos,
|
||||||
|
interaction.clone(),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.or_else(|| closest_interactable_entity.map(|(e, _)| Interactable::Entity(e)))
|
.or_else(|| closest_interactable_entity.map(|(e, _)| Interactable::Entity(e)))
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ use common::{
|
|||||||
consts::MAX_MOUNT_RANGE,
|
consts::MAX_MOUNT_RANGE,
|
||||||
event::UpdateCharacterMetadata,
|
event::UpdateCharacterMetadata,
|
||||||
link::Is,
|
link::Is,
|
||||||
mounting::Mount,
|
mounting::{Mount, VolumePos},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
recipe,
|
recipe,
|
||||||
terrain::{Block, BlockKind},
|
terrain::{Block, BlockKind},
|
||||||
@ -350,7 +350,8 @@ impl SessionState {
|
|||||||
match inv_event {
|
match inv_event {
|
||||||
InventoryUpdateEvent::BlockCollectFailed { pos, reason } => {
|
InventoryUpdateEvent::BlockCollectFailed { pos, reason } => {
|
||||||
self.hud.add_failed_block_pickup(
|
self.hud.add_failed_block_pickup(
|
||||||
pos,
|
// TODO: Support volumes.
|
||||||
|
VolumePos::terrain(pos),
|
||||||
HudCollectFailedReason::from_server_reason(
|
HudCollectFailedReason::from_server_reason(
|
||||||
&reason,
|
&reason,
|
||||||
client.state().ecs(),
|
client.state().ecs(),
|
||||||
@ -887,14 +888,12 @@ impl PlayState for SessionState {
|
|||||||
let mut client = self.client.borrow_mut();
|
let mut client = self.client.borrow_mut();
|
||||||
if client.is_riding() {
|
if client.is_riding() {
|
||||||
client.unmount();
|
client.unmount();
|
||||||
} else if client.stand_if_mounted() {
|
|
||||||
} else {
|
} else {
|
||||||
if let Some(interactable) = &self.interactable {
|
if let Some(interactable) = &self.interactable {
|
||||||
match interactable {
|
match interactable {
|
||||||
Interactable::Block(_, pos, interaction) => {
|
Interactable::Block(_, pos, interaction) => {
|
||||||
if matches!(interaction, BlockInteraction::Mount(_))
|
if matches!(interaction, BlockInteraction::Mount) {
|
||||||
{
|
client.mount_volume(*pos)
|
||||||
client.mount_sprite(*pos)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Interactable::Entity(entity) => client.mount(*entity),
|
Interactable::Entity(entity) => client.mount(*entity),
|
||||||
@ -941,18 +940,25 @@ impl PlayState for SessionState {
|
|||||||
BlockInteraction::Collect
|
BlockInteraction::Collect
|
||||||
| BlockInteraction::Unlock(_) => {
|
| BlockInteraction::Unlock(_) => {
|
||||||
if block.is_collectible() {
|
if block.is_collectible() {
|
||||||
client.collect_block(*pos);
|
match pos.kind {
|
||||||
|
common::mounting::Volume::Terrain => {
|
||||||
|
client.collect_block(pos.pos);
|
||||||
|
}
|
||||||
|
common::mounting::Volume::Entity(_) => todo!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
BlockInteraction::Craft(tab) => {
|
BlockInteraction::Craft(tab) => {
|
||||||
self.hud.show.open_crafting_tab(
|
self.hud.show.open_crafting_tab(
|
||||||
*tab,
|
*tab,
|
||||||
block.get_sprite().map(|s| (*pos, s)),
|
block
|
||||||
|
.get_sprite()
|
||||||
|
.map(|s| (*pos, s)),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
BlockInteraction::Mount(_) => {
|
BlockInteraction::Mount => {
|
||||||
if block.is_mountable() {
|
if block.is_mountable() {
|
||||||
client.mount_sprite(*pos);
|
client.mount_volume(*pos)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
BlockInteraction::Mine(_) => {},
|
BlockInteraction::Mine(_) => {},
|
||||||
|
Loading…
Reference in New Issue
Block a user