mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Merge branch 'aweinstock/airship-mvp-rebased' into 'master'
Airships See merge request veloren/veloren!1888
This commit is contained in:
commit
5b21ee7200
@ -48,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Gave weapons critical strike {chance, multiplier} stats
|
- Gave weapons critical strike {chance, multiplier} stats
|
||||||
- A system to add glow and reflection effects to figures (i.e: characters, armour, weapons, etc.)
|
- A system to add glow and reflection effects to figures (i.e: characters, armour, weapons, etc.)
|
||||||
- Merchants will trade wares with players
|
- Merchants will trade wares with players
|
||||||
|
- Airships that can be mounted and flown, and also walked on (`/airship` admin command)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -5619,6 +5619,7 @@ dependencies = [
|
|||||||
"hashbrown",
|
"hashbrown",
|
||||||
"serde",
|
"serde",
|
||||||
"specs",
|
"specs",
|
||||||
|
"specs-idvs",
|
||||||
"sum_type",
|
"sum_type",
|
||||||
"tracing",
|
"tracing",
|
||||||
"vek 0.14.1",
|
"vek 0.14.1",
|
||||||
|
@ -969,6 +969,15 @@
|
|||||||
),
|
),
|
||||||
species: ()
|
species: ()
|
||||||
),
|
),
|
||||||
|
ship: (
|
||||||
|
body: (
|
||||||
|
keyword: "ship",
|
||||||
|
names_0: [
|
||||||
|
"Boaty McBoatface",
|
||||||
|
],
|
||||||
|
),
|
||||||
|
species: (),
|
||||||
|
),
|
||||||
biped_small: (
|
biped_small: (
|
||||||
body: (
|
body: (
|
||||||
keyword: "biped_small",
|
keyword: "biped_small",
|
||||||
|
23
assets/server/manifests/ship_manifest.ron
Normal file
23
assets/server/manifests/ship_manifest.ron
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
({
|
||||||
|
DefaultAirship: (
|
||||||
|
bone0: (
|
||||||
|
//offset: (3.0, 7.0, 1.0),
|
||||||
|
//offset: (-20.75, -34.75, 1.25),
|
||||||
|
//offset: (0.0, 0.0, 0.0),
|
||||||
|
offset: (-17.5, -35.0, 1.0),
|
||||||
|
//phys_offset: (0.25, 0.25, 0.25),
|
||||||
|
phys_offset: (0.0, 0.0, 0.0),
|
||||||
|
central: ("Human_Airship"),
|
||||||
|
),
|
||||||
|
bone1: (
|
||||||
|
offset: (0.0, 40.0, -8.0),
|
||||||
|
phys_offset: (0.0, 0.0, 0.0),
|
||||||
|
central: ("propeller-l"),
|
||||||
|
),
|
||||||
|
bone2: (
|
||||||
|
offset: (0.0, 0.0, -4.0),
|
||||||
|
phys_offset: (0.0, 0.0, 0.0),
|
||||||
|
central: ("propeller-r"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
})
|
BIN
assets/server/voxel/Human_Airship.vox
(Stored with Git LFS)
Normal file
BIN
assets/server/voxel/Human_Airship.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/server/voxel/airship.vox
(Stored with Git LFS)
Normal file
BIN
assets/server/voxel/airship.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/server/voxel/propeller-l.vox
(Stored with Git LFS)
Normal file
BIN
assets/server/voxel/propeller-l.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/server/voxel/propeller-r.vox
(Stored with Git LFS)
Normal file
BIN
assets/server/voxel/propeller-r.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -33,6 +33,7 @@ use common::{
|
|||||||
grid::Grid,
|
grid::Grid,
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
recipe::RecipeBook,
|
recipe::RecipeBook,
|
||||||
|
resources::PlayerEntity,
|
||||||
terrain::{block::Block, neighbors, BiomeKind, SitesKind, TerrainChunk, TerrainChunkSize},
|
terrain::{block::Block, neighbors, BiomeKind, SitesKind, TerrainChunk, TerrainChunkSize},
|
||||||
trade::{PendingTrade, TradeAction, TradeId, TradeResult},
|
trade::{PendingTrade, TradeAction, TradeId, TradeResult},
|
||||||
uid::{Uid, UidAllocator},
|
uid::{Uid, UidAllocator},
|
||||||
@ -281,6 +282,7 @@ impl Client {
|
|||||||
|
|
||||||
let entity = state.ecs_mut().apply_entity_package(entity_package);
|
let entity = state.ecs_mut().apply_entity_package(entity_package);
|
||||||
*state.ecs_mut().write_resource() = time_of_day;
|
*state.ecs_mut().write_resource() = time_of_day;
|
||||||
|
*state.ecs_mut().write_resource() = PlayerEntity(Some(entity));
|
||||||
state.ecs_mut().insert(material_stats);
|
state.ecs_mut().insert(material_stats);
|
||||||
state.ecs_mut().insert(ability_map);
|
state.ecs_mut().insert(ability_map);
|
||||||
|
|
||||||
@ -1458,6 +1460,7 @@ impl Client {
|
|||||||
ServerGeneral::SetPlayerEntity(uid) => {
|
ServerGeneral::SetPlayerEntity(uid) => {
|
||||||
if let Some(entity) = self.state.ecs().entity_from_uid(uid.0) {
|
if let Some(entity) = self.state.ecs().entity_from_uid(uid.0) {
|
||||||
self.entity = entity;
|
self.entity = entity;
|
||||||
|
*self.state.ecs_mut().write_resource() = PlayerEntity(Some(entity));
|
||||||
} else {
|
} else {
|
||||||
return Err(Error::Other("Failed to find entity from uid.".to_owned()));
|
return Err(Error::Other("Failed to find entity from uid.".to_owned()));
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ default = ["simd"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
common-base = { package = "veloren-common-base", path = "base" }
|
common-base = { package = "veloren-common-base", path = "base" }
|
||||||
|
#inline_tweak = "1.0.2"
|
||||||
|
|
||||||
# Serde
|
# Serde
|
||||||
serde = { version = "1.0.110", features = ["derive", "rc"] }
|
serde = { version = "1.0.110", features = ["derive", "rc"] }
|
||||||
|
@ -12,6 +12,7 @@ default = ["simd"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
common = {package = "veloren-common", path = "../../common"}
|
common = {package = "veloren-common", path = "../../common"}
|
||||||
|
#inline_tweak = "1.0.2"
|
||||||
|
|
||||||
sum_type = "0.2.0"
|
sum_type = "0.2.0"
|
||||||
vek = { version = "=0.14.1", features = ["serde"] }
|
vek = { version = "=0.14.1", features = ["serde"] }
|
||||||
@ -25,6 +26,7 @@ authc = { git = "https://gitlab.com/veloren/auth.git", rev = "fb3dcbc4962b367253
|
|||||||
|
|
||||||
# ECS
|
# ECS
|
||||||
specs = { git = "https://github.com/amethyst/specs.git", features = ["serde", "storage-event-control"], rev = "5a9b71035007be0e3574f35184acac1cd4530496" }
|
specs = { git = "https://github.com/amethyst/specs.git", features = ["serde", "storage-event-control"], rev = "5a9b71035007be0e3574f35184acac1cd4530496" }
|
||||||
|
specs-idvs = { git = "https://gitlab.com/veloren/specs-idvs.git", rev = "b65fb220e94f5d3c9bc30074a076149763795556" }
|
||||||
|
|
||||||
# Serde
|
# Serde
|
||||||
serde = { version = "1.0.110", features = ["derive"] }
|
serde = { version = "1.0.110", features = ["derive"] }
|
@ -100,9 +100,9 @@ impl sync::CompPacket for EcsCompPacket {
|
|||||||
EcsCompPacket::Gravity(comp) => sync::handle_insert(comp, entity, world),
|
EcsCompPacket::Gravity(comp) => sync::handle_insert(comp, entity, world),
|
||||||
EcsCompPacket::Sticky(comp) => sync::handle_insert(comp, entity, world),
|
EcsCompPacket::Sticky(comp) => sync::handle_insert(comp, entity, world),
|
||||||
EcsCompPacket::CharacterState(comp) => sync::handle_insert(comp, entity, world),
|
EcsCompPacket::CharacterState(comp) => sync::handle_insert(comp, entity, world),
|
||||||
EcsCompPacket::Pos(comp) => sync::handle_insert(comp, entity, world),
|
EcsCompPacket::Pos(comp) => sync::handle_interp_insert(comp, entity, world),
|
||||||
EcsCompPacket::Vel(comp) => sync::handle_insert(comp, entity, world),
|
EcsCompPacket::Vel(comp) => sync::handle_interp_insert(comp, entity, world),
|
||||||
EcsCompPacket::Ori(comp) => sync::handle_insert(comp, entity, world),
|
EcsCompPacket::Ori(comp) => sync::handle_interp_insert(comp, entity, world),
|
||||||
EcsCompPacket::Shockwave(comp) => sync::handle_insert(comp, entity, world),
|
EcsCompPacket::Shockwave(comp) => sync::handle_insert(comp, entity, world),
|
||||||
EcsCompPacket::BeamSegment(comp) => sync::handle_insert(comp, entity, world),
|
EcsCompPacket::BeamSegment(comp) => sync::handle_insert(comp, entity, world),
|
||||||
}
|
}
|
||||||
@ -132,9 +132,9 @@ impl sync::CompPacket for EcsCompPacket {
|
|||||||
EcsCompPacket::Gravity(comp) => sync::handle_modify(comp, entity, world),
|
EcsCompPacket::Gravity(comp) => sync::handle_modify(comp, entity, world),
|
||||||
EcsCompPacket::Sticky(comp) => sync::handle_modify(comp, entity, world),
|
EcsCompPacket::Sticky(comp) => sync::handle_modify(comp, entity, world),
|
||||||
EcsCompPacket::CharacterState(comp) => sync::handle_modify(comp, entity, world),
|
EcsCompPacket::CharacterState(comp) => sync::handle_modify(comp, entity, world),
|
||||||
EcsCompPacket::Pos(comp) => sync::handle_modify(comp, entity, world),
|
EcsCompPacket::Pos(comp) => sync::handle_interp_modify(comp, entity, world),
|
||||||
EcsCompPacket::Vel(comp) => sync::handle_modify(comp, entity, world),
|
EcsCompPacket::Vel(comp) => sync::handle_interp_modify(comp, entity, world),
|
||||||
EcsCompPacket::Ori(comp) => sync::handle_modify(comp, entity, world),
|
EcsCompPacket::Ori(comp) => sync::handle_interp_modify(comp, entity, world),
|
||||||
EcsCompPacket::Shockwave(comp) => sync::handle_modify(comp, entity, world),
|
EcsCompPacket::Shockwave(comp) => sync::handle_modify(comp, entity, world),
|
||||||
EcsCompPacket::BeamSegment(comp) => sync::handle_modify(comp, entity, world),
|
EcsCompPacket::BeamSegment(comp) => sync::handle_modify(comp, entity, world),
|
||||||
}
|
}
|
||||||
@ -168,9 +168,9 @@ impl sync::CompPacket for EcsCompPacket {
|
|||||||
EcsCompPhantom::CharacterState(_) => {
|
EcsCompPhantom::CharacterState(_) => {
|
||||||
sync::handle_remove::<comp::CharacterState>(entity, world)
|
sync::handle_remove::<comp::CharacterState>(entity, world)
|
||||||
},
|
},
|
||||||
EcsCompPhantom::Pos(_) => sync::handle_remove::<comp::Pos>(entity, world),
|
EcsCompPhantom::Pos(_) => sync::handle_interp_remove::<comp::Pos>(entity, world),
|
||||||
EcsCompPhantom::Vel(_) => sync::handle_remove::<comp::Vel>(entity, world),
|
EcsCompPhantom::Vel(_) => sync::handle_interp_remove::<comp::Vel>(entity, world),
|
||||||
EcsCompPhantom::Ori(_) => sync::handle_remove::<comp::Ori>(entity, world),
|
EcsCompPhantom::Ori(_) => sync::handle_interp_remove::<comp::Ori>(entity, world),
|
||||||
EcsCompPhantom::Shockwave(_) => sync::handle_remove::<comp::Shockwave>(entity, world),
|
EcsCompPhantom::Shockwave(_) => sync::handle_remove::<comp::Shockwave>(entity, world),
|
||||||
EcsCompPhantom::BeamSegment(_) => sync::handle_remove::<comp::Ori>(entity, world),
|
EcsCompPhantom::BeamSegment(_) => sync::handle_remove::<comp::Ori>(entity, world),
|
||||||
}
|
}
|
||||||
|
155
common/net/src/sync/interpolation.rs
Normal file
155
common/net/src/sync/interpolation.rs
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
// impls of `InterpolatableComponent` on things defined in `common`, since
|
||||||
|
// `common_net` is downstream of `common`, and an `InterpolationSystem` that
|
||||||
|
// applies them
|
||||||
|
use super::InterpolatableComponent;
|
||||||
|
use common::comp::{Ori, Pos, Vel};
|
||||||
|
use specs::Component;
|
||||||
|
use specs_idvs::IdvStorage;
|
||||||
|
use tracing::warn;
|
||||||
|
use vek::ops::{Lerp, Slerp};
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct InterpBuffer<T> {
|
||||||
|
pub buf: [(f64, T); 4],
|
||||||
|
pub i: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> InterpBuffer<T> {
|
||||||
|
fn push(&mut self, time: f64, x: T) {
|
||||||
|
let InterpBuffer {
|
||||||
|
ref mut buf,
|
||||||
|
ref mut i,
|
||||||
|
} = self;
|
||||||
|
*i += 1;
|
||||||
|
*i %= buf.len();
|
||||||
|
buf[*i] = (time, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: 'static + Send + Sync> Component for InterpBuffer<T> {
|
||||||
|
type Storage = IdvStorage<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 0 is pure physics, 1 is pure extrapolation
|
||||||
|
const PHYSICS_VS_EXTRAPOLATION_FACTOR: f32 = 0.2;
|
||||||
|
const POSITION_INTERP_SANITY: Option<f32> = None;
|
||||||
|
const VELOCITY_INTERP_SANITY: Option<f32> = None;
|
||||||
|
const ENABLE_POSITION_HERMITE: bool = false;
|
||||||
|
|
||||||
|
impl InterpolatableComponent for Pos {
|
||||||
|
type InterpData = InterpBuffer<Pos>;
|
||||||
|
type ReadData = InterpBuffer<Vel>;
|
||||||
|
|
||||||
|
fn update_component(&self, interp_data: &mut Self::InterpData, time: f64) {
|
||||||
|
interp_data.push(time, *self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interpolate(self, interp_data: &Self::InterpData, t2: f64, vel: &InterpBuffer<Vel>) -> Self {
|
||||||
|
// lerp to test interface, do hermite spline later
|
||||||
|
let InterpBuffer { ref buf, ref i } = interp_data;
|
||||||
|
let (t0, p0) = buf[(i + buf.len() - 1) % buf.len()];
|
||||||
|
let (t1, p1) = buf[i % buf.len()];
|
||||||
|
if (t1 - t0).abs() < f64::EPSILON {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
if POSITION_INTERP_SANITY
|
||||||
|
.map_or(false, |limit| p0.0.distance_squared(p1.0) > limit.powf(2.0))
|
||||||
|
{
|
||||||
|
warn!("position delta exceeded sanity check, clamping");
|
||||||
|
return p1;
|
||||||
|
}
|
||||||
|
let (t0prime, m0) = vel.buf[(i + vel.buf.len() - 1) % vel.buf.len()];
|
||||||
|
let (t1prime, m1) = vel.buf[i % vel.buf.len()];
|
||||||
|
let mut out;
|
||||||
|
let t = (t2 - t0) / (t1 - t0);
|
||||||
|
if ENABLE_POSITION_HERMITE
|
||||||
|
&& ((t0 - t0prime).abs() < f64::EPSILON && (t1 - t1prime).abs() < f64::EPSILON)
|
||||||
|
{
|
||||||
|
let h00 = |t: f64| (2.0 * t.powf(3.0) - 3.0 * t.powf(2.0) + 1.0) as f32;
|
||||||
|
let h10 = |t: f64| (t.powf(3.0) - 2.0 * t.powf(2.0) + t) as f32;
|
||||||
|
let h01 = |t: f64| (-2.0 * t.powf(3.0) + 3.0 * t.powf(2.0)) as f32;
|
||||||
|
let h11 = |t: f64| (t.powf(3.0) - t.powf(2.0)) as f32;
|
||||||
|
let dt = (t1 - t0) as f32;
|
||||||
|
out = h00(t) * p0.0 + h10(t) * dt * m0.0 + h01(t) * p1.0 + h11(t) * dt * m1.0;
|
||||||
|
} else {
|
||||||
|
if ENABLE_POSITION_HERMITE {
|
||||||
|
warn!(
|
||||||
|
"timestamps for pos and vel don't match ({:?}, {:?}), falling back to lerp",
|
||||||
|
interp_data, vel
|
||||||
|
);
|
||||||
|
}
|
||||||
|
out = Lerp::lerp_unclamped(p0.0, p1.0, t as f32);
|
||||||
|
}
|
||||||
|
|
||||||
|
if out.map(|x| x.is_nan()).reduce_or() {
|
||||||
|
warn!("interpolation output is nan: {}, {}, {:?}", t2, t, buf);
|
||||||
|
out = p1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pos(Lerp::lerp(self.0, out, PHYSICS_VS_EXTRAPOLATION_FACTOR))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterpolatableComponent for Vel {
|
||||||
|
type InterpData = InterpBuffer<Vel>;
|
||||||
|
type ReadData = ();
|
||||||
|
|
||||||
|
fn update_component(&self, interp_data: &mut Self::InterpData, time: f64) {
|
||||||
|
interp_data.push(time, *self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interpolate(self, interp_data: &Self::InterpData, t2: f64, _: &()) -> Self {
|
||||||
|
let InterpBuffer { ref buf, ref i } = interp_data;
|
||||||
|
let (t0, p0) = buf[(i + buf.len() - 1) % buf.len()];
|
||||||
|
let (t1, p1) = buf[i % buf.len()];
|
||||||
|
if (t1 - t0).abs() < f64::EPSILON {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
if VELOCITY_INTERP_SANITY
|
||||||
|
.map_or(false, |limit| p0.0.distance_squared(p1.0) > limit.powf(2.0))
|
||||||
|
{
|
||||||
|
warn!("velocity delta exceeded sanity check, clamping");
|
||||||
|
return p1;
|
||||||
|
}
|
||||||
|
let lerp_factor = 1.0 + ((t2 - t1) / (t1 - t0)) as f32;
|
||||||
|
let mut out = Lerp::lerp_unclamped(p0.0, p1.0, lerp_factor);
|
||||||
|
if out.map(|x| x.is_nan()).reduce_or() {
|
||||||
|
warn!(
|
||||||
|
"interpolation output is nan: {}, {}, {:?}",
|
||||||
|
t2, lerp_factor, buf
|
||||||
|
);
|
||||||
|
out = p1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vel(Lerp::lerp(self.0, out, PHYSICS_VS_EXTRAPOLATION_FACTOR))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InterpolatableComponent for Ori {
|
||||||
|
type InterpData = InterpBuffer<Ori>;
|
||||||
|
type ReadData = ();
|
||||||
|
|
||||||
|
fn update_component(&self, interp_data: &mut Self::InterpData, time: f64) {
|
||||||
|
interp_data.push(time, *self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn interpolate(self, interp_data: &Self::InterpData, t2: f64, _: &()) -> Self {
|
||||||
|
let InterpBuffer { ref buf, ref i } = interp_data;
|
||||||
|
let (t0, p0) = buf[(i + buf.len() - 1) % buf.len()];
|
||||||
|
let (t1, p1) = buf[i % buf.len()];
|
||||||
|
if (t1 - t0).abs() < f64::EPSILON {
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
let lerp_factor = 1.0 + ((t2 - t1) / (t1 - t0)) as f32;
|
||||||
|
let mut out = Slerp::slerp_unclamped(p0.to_quat(), p1.to_quat(), lerp_factor);
|
||||||
|
if out.into_vec4().map(|x| x.is_nan()).reduce_or() {
|
||||||
|
warn!(
|
||||||
|
"interpolation output is nan: {}, {}, {:?}",
|
||||||
|
t2, lerp_factor, buf
|
||||||
|
);
|
||||||
|
out = p1.to_quat();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ori::new(Slerp::slerp(self.to_quat(), out, PHYSICS_VS_EXTRAPOLATION_FACTOR).normalized())
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
// Note: Currently only one-way sync is supported until a usecase for two-way
|
// Note: Currently only one-way sync is supported until a usecase for two-way
|
||||||
// sync arises
|
// sync arises
|
||||||
|
pub mod interpolation;
|
||||||
mod packet;
|
mod packet;
|
||||||
mod sync_ext;
|
mod sync_ext;
|
||||||
mod track;
|
mod track;
|
||||||
@ -7,8 +8,9 @@ mod track;
|
|||||||
// Reexports
|
// Reexports
|
||||||
pub use common::uid::{Uid, UidAllocator};
|
pub use common::uid::{Uid, UidAllocator};
|
||||||
pub use packet::{
|
pub use packet::{
|
||||||
handle_insert, handle_modify, handle_remove, CompPacket, CompSyncPackage, EntityPackage,
|
handle_insert, handle_interp_insert, handle_interp_modify, handle_interp_remove, handle_modify,
|
||||||
EntitySyncPackage, StatePackage,
|
handle_remove, CompPacket, CompSyncPackage, EntityPackage, EntitySyncPackage,
|
||||||
|
InterpolatableComponent, StatePackage,
|
||||||
};
|
};
|
||||||
pub use sync_ext::WorldSyncExt;
|
pub use sync_ext::WorldSyncExt;
|
||||||
pub use track::UpdateTracker;
|
pub use track::UpdateTracker;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::track::UpdateTracker;
|
use super::track::UpdateTracker;
|
||||||
use common::uid::Uid;
|
use common::{resources::Time, uid::Uid};
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
use specs::{Component, Entity, Join, ReadStorage, World, WorldExt};
|
use specs::{Component, Entity, Join, ReadStorage, World, WorldExt};
|
||||||
use std::{
|
use std::{
|
||||||
@ -9,6 +9,10 @@ use std::{
|
|||||||
};
|
};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
|
// TODO: apply_{insert,modify,remove} all take the entity and call
|
||||||
|
// `write_storage` once per entity per component, instead of once per update
|
||||||
|
// batch(e.g. in a system-like memory access pattern); if sync ends up being a
|
||||||
|
// bottleneck, try optimizing this
|
||||||
/// Implemented by type that carries component data for insertion and
|
/// Implemented by type that carries component data for insertion and
|
||||||
/// modification The assocatied `Phantom` type only carries information about
|
/// modification The assocatied `Phantom` type only carries information about
|
||||||
/// which component type is of interest and is used to transmit deletion events
|
/// which component type is of interest and is used to transmit deletion events
|
||||||
@ -42,6 +46,44 @@ pub fn handle_remove<C: Component>(entity: Entity, world: &World) {
|
|||||||
world.write_storage::<C>().remove(entity);
|
world.write_storage::<C>().remove(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait InterpolatableComponent: Component {
|
||||||
|
type InterpData: Component + Default;
|
||||||
|
type ReadData;
|
||||||
|
|
||||||
|
fn update_component(&self, data: &mut Self::InterpData, time: f64);
|
||||||
|
fn interpolate(self, data: &Self::InterpData, time: f64, read_data: &Self::ReadData) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_interp_insert<C: InterpolatableComponent>(comp: C, entity: Entity, world: &World) {
|
||||||
|
let mut interp_data = C::InterpData::default();
|
||||||
|
let time = world.read_resource::<Time>().0;
|
||||||
|
comp.update_component(&mut interp_data, time);
|
||||||
|
handle_insert(comp, entity, world);
|
||||||
|
handle_insert(interp_data, entity, world);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_interp_modify<C: InterpolatableComponent + Debug>(
|
||||||
|
comp: C,
|
||||||
|
entity: Entity,
|
||||||
|
world: &World,
|
||||||
|
) {
|
||||||
|
if let Some(mut interp_data) = world.write_storage::<C::InterpData>().get_mut(entity) {
|
||||||
|
let time = world.read_resource::<Time>().0;
|
||||||
|
comp.update_component(&mut interp_data, time);
|
||||||
|
handle_modify(comp, entity, world);
|
||||||
|
} else {
|
||||||
|
error!(
|
||||||
|
?comp,
|
||||||
|
"Error modifying interpolation data for synced component, it doesn't seem to exist"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_interp_remove<C: InterpolatableComponent>(entity: Entity, world: &World) {
|
||||||
|
handle_remove::<C>(entity, world);
|
||||||
|
handle_remove::<C::InterpData>(entity, world);
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
|
||||||
pub enum CompUpdateKind<P: CompPacket> {
|
pub enum CompUpdateKind<P: CompPacket> {
|
||||||
Inserted(P),
|
Inserted(P),
|
||||||
|
@ -36,6 +36,7 @@ impl ChatCommandData {
|
|||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
pub enum ChatCommand {
|
pub enum ChatCommand {
|
||||||
Adminify,
|
Adminify,
|
||||||
|
Airship,
|
||||||
Alias,
|
Alias,
|
||||||
Ban,
|
Ban,
|
||||||
Build,
|
Build,
|
||||||
@ -89,6 +90,7 @@ pub enum ChatCommand {
|
|||||||
// Thank you for keeping this sorted alphabetically :-)
|
// Thank you for keeping this sorted alphabetically :-)
|
||||||
pub static CHAT_COMMANDS: &[ChatCommand] = &[
|
pub static CHAT_COMMANDS: &[ChatCommand] = &[
|
||||||
ChatCommand::Adminify,
|
ChatCommand::Adminify,
|
||||||
|
ChatCommand::Airship,
|
||||||
ChatCommand::Alias,
|
ChatCommand::Alias,
|
||||||
ChatCommand::Ban,
|
ChatCommand::Ban,
|
||||||
ChatCommand::Build,
|
ChatCommand::Build,
|
||||||
@ -222,6 +224,11 @@ impl ChatCommand {
|
|||||||
"Temporarily gives a player admin permissions or removes them",
|
"Temporarily gives a player admin permissions or removes them",
|
||||||
Admin,
|
Admin,
|
||||||
),
|
),
|
||||||
|
ChatCommand::Airship => cmd(
|
||||||
|
vec![Float("destination_degrees_ccw_of_east", 90.0, Optional)],
|
||||||
|
"Spawns an airship",
|
||||||
|
Admin,
|
||||||
|
),
|
||||||
ChatCommand::Alias => cmd(vec![Any("name", Required)], "Change your alias", NoAdmin),
|
ChatCommand::Alias => cmd(vec![Any("name", Required)], "Change your alias", NoAdmin),
|
||||||
ChatCommand::Ban => cmd(
|
ChatCommand::Ban => cmd(
|
||||||
vec![Any("username", Required), Message(Optional)],
|
vec![Any("username", Required), Message(Optional)],
|
||||||
@ -449,6 +456,7 @@ impl ChatCommand {
|
|||||||
pub fn keyword(&self) -> &'static str {
|
pub fn keyword(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
ChatCommand::Adminify => "adminify",
|
ChatCommand::Adminify => "adminify",
|
||||||
|
ChatCommand::Airship => "airship",
|
||||||
ChatCommand::Alias => "alias",
|
ChatCommand::Alias => "alias",
|
||||||
ChatCommand::Ban => "ban",
|
ChatCommand::Ban => "ban",
|
||||||
ChatCommand::Build => "build",
|
ChatCommand::Build => "build",
|
||||||
|
@ -168,6 +168,7 @@ impl<'a> From<&'a Body> for Psyche {
|
|||||||
Body::Golem(_) => 1.0,
|
Body::Golem(_) => 1.0,
|
||||||
Body::Theropod(_) => 1.0,
|
Body::Theropod(_) => 1.0,
|
||||||
Body::Dragon(_) => 1.0,
|
Body::Dragon(_) => 1.0,
|
||||||
|
Body::Ship(_) => 1.0,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -221,6 +222,15 @@ impl Agent {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_destination(pos: Vec3<f32>) -> Self {
|
||||||
|
Self {
|
||||||
|
can_speak: true,
|
||||||
|
psyche: Psyche { aggro: 1.0 },
|
||||||
|
rtsim_controller: RtSimController::with_destination(pos),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
patrol_origin: Option<Vec3<f32>>,
|
patrol_origin: Option<Vec3<f32>>,
|
||||||
can_speak: bool,
|
can_speak: bool,
|
||||||
|
@ -11,6 +11,7 @@ pub mod object;
|
|||||||
pub mod quadruped_low;
|
pub mod quadruped_low;
|
||||||
pub mod quadruped_medium;
|
pub mod quadruped_medium;
|
||||||
pub mod quadruped_small;
|
pub mod quadruped_small;
|
||||||
|
pub mod ship;
|
||||||
pub mod theropod;
|
pub mod theropod;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -44,6 +45,7 @@ make_case_elim!(
|
|||||||
Golem(body: golem::Body) = 11,
|
Golem(body: golem::Body) = 11,
|
||||||
Theropod(body: theropod::Body) = 12,
|
Theropod(body: theropod::Body) = 12,
|
||||||
QuadrupedLow(body: quadruped_low::Body) = 13,
|
QuadrupedLow(body: quadruped_low::Body) = 13,
|
||||||
|
Ship(body: ship::Body) = 14,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -78,6 +80,7 @@ pub struct AllBodies<BodyMeta, SpeciesMeta> {
|
|||||||
pub golem: BodyData<BodyMeta, golem::AllSpecies<SpeciesMeta>>,
|
pub golem: BodyData<BodyMeta, golem::AllSpecies<SpeciesMeta>>,
|
||||||
pub theropod: BodyData<BodyMeta, theropod::AllSpecies<SpeciesMeta>>,
|
pub theropod: BodyData<BodyMeta, theropod::AllSpecies<SpeciesMeta>>,
|
||||||
pub quadruped_low: BodyData<BodyMeta, quadruped_low::AllSpecies<SpeciesMeta>>,
|
pub quadruped_low: BodyData<BodyMeta, quadruped_low::AllSpecies<SpeciesMeta>>,
|
||||||
|
pub ship: BodyData<BodyMeta, ()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Can only retrieve body metadata by direct index.
|
/// Can only retrieve body metadata by direct index.
|
||||||
@ -124,6 +127,7 @@ impl<'a, BodyMeta, SpeciesMeta> core::ops::Index<&'a Body> for AllBodies<BodyMet
|
|||||||
Body::Golem(_) => &self.golem.body,
|
Body::Golem(_) => &self.golem.body,
|
||||||
Body::Theropod(_) => &self.theropod.body,
|
Body::Theropod(_) => &self.theropod.body,
|
||||||
Body::QuadrupedLow(_) => &self.quadruped_low.body,
|
Body::QuadrupedLow(_) => &self.quadruped_low.body,
|
||||||
|
Body::Ship(_) => &self.ship.body,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,6 +222,7 @@ impl Body {
|
|||||||
Body::Golem(_) => 2.5,
|
Body::Golem(_) => 2.5,
|
||||||
Body::BipedSmall(_) => 0.75,
|
Body::BipedSmall(_) => 0.75,
|
||||||
Body::Object(_) => 0.4,
|
Body::Object(_) => 0.4,
|
||||||
|
Body::Ship(_) => 1.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -294,6 +299,7 @@ impl Body {
|
|||||||
object::Body::Crossbow => 1.7,
|
object::Body::Crossbow => 1.7,
|
||||||
_ => 1.0,
|
_ => 1.0,
|
||||||
},
|
},
|
||||||
|
Body::Ship(_) => 1.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,6 +422,7 @@ impl Body {
|
|||||||
quadruped_low::Species::Deadwood => 600,
|
quadruped_low::Species::Deadwood => 600,
|
||||||
_ => 200,
|
_ => 200,
|
||||||
},
|
},
|
||||||
|
Body::Ship(_) => 10000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,12 +515,23 @@ impl Body {
|
|||||||
quadruped_low::Species::Deadwood => 30,
|
quadruped_low::Species::Deadwood => 30,
|
||||||
_ => 20,
|
_ => 20,
|
||||||
},
|
},
|
||||||
|
Body::Ship(_) => 500,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flying_height(&self) -> f32 {
|
||||||
|
match self {
|
||||||
|
Body::BirdSmall(_) => 30.0,
|
||||||
|
Body::BirdMedium(_) => 40.0,
|
||||||
|
Body::Dragon(_) => 60.0,
|
||||||
|
Body::Ship(ship::Body::DefaultAirship) => 60.0,
|
||||||
|
_ => 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn immune_to(&self, buff: BuffKind) -> bool {
|
pub fn immune_to(&self, buff: BuffKind) -> bool {
|
||||||
match buff {
|
match buff {
|
||||||
BuffKind::Bleeding => matches!(self, Body::Object(_) | Body::Golem(_)),
|
BuffKind::Bleeding => matches!(self, Body::Object(_) | Body::Golem(_) | Body::Ship(_)),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -521,7 +539,13 @@ impl Body {
|
|||||||
/// Returns a multiplier representing increased difficulty not accounted for
|
/// Returns a multiplier representing increased difficulty not accounted for
|
||||||
/// due to AI or not using an actual weapon
|
/// due to AI or not using an actual weapon
|
||||||
// TODO: Match on species
|
// TODO: Match on species
|
||||||
pub fn combat_multiplier(&self) -> f32 { if let Body::Object(_) = self { 0.0 } else { 1.0 } }
|
pub fn combat_multiplier(&self) -> f32 {
|
||||||
|
if let Body::Object(_) | Body::Ship(_) = self {
|
||||||
|
0.0
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn base_poise(&self) -> u32 {
|
pub fn base_poise(&self) -> u32 {
|
||||||
match self {
|
match self {
|
||||||
@ -549,6 +573,13 @@ impl Body {
|
|||||||
Body::Humanoid(_) | Body::BipedSmall(_) | Body::BipedLarge(_)
|
Body::Humanoid(_) | Body::BipedSmall(_) | Body::BipedLarge(_)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mounting_offset(&self) -> Vec3<f32> {
|
||||||
|
match self {
|
||||||
|
Body::Ship(ship::Body::DefaultAirship) => Vec3::from([0.0, 0.0, 10.0]),
|
||||||
|
_ => Vec3::unit_z(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for Body {
|
impl Component for Body {
|
||||||
|
126
common/src/comp/body/ship.rs
Normal file
126
common/src/comp/body/ship.rs
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
use crate::make_case_elim;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
make_case_elim!(
|
||||||
|
body,
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
#[repr(u32)]
|
||||||
|
pub enum Body {
|
||||||
|
DefaultAirship = 0,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
impl From<Body> for super::Body {
|
||||||
|
fn from(body: Body) -> Self { super::Body::Ship(body) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Body {
|
||||||
|
pub fn manifest_entry(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Body::DefaultAirship => "Human_Airship",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Terrain is 11.0 scale relative to small-scale voxels, and all figures get
|
||||||
|
/// multiplied by 0.8 in rendering. For now, have a constant in `comp::Scale`
|
||||||
|
/// that compensates for both of these, but there might be a more elegant way
|
||||||
|
/// (e.g. using `Scale(0.8)` for everything else and not having a magic number
|
||||||
|
/// in figure rendering, and multiplying terrain models by 11.0 in animation).
|
||||||
|
pub const AIRSHIP_SCALE: f32 = 11.0 / 0.8;
|
||||||
|
|
||||||
|
/// Duplicate of some of the things defined in `voxygen::scene::figure::load` to
|
||||||
|
/// avoid having to refactor all of that to `common` for using voxels as
|
||||||
|
/// collider geometry
|
||||||
|
pub mod figuredata {
|
||||||
|
use crate::{
|
||||||
|
assets::{self, AssetExt, AssetHandle, DotVoxAsset, Ron},
|
||||||
|
figure::cell::Cell,
|
||||||
|
terrain::{
|
||||||
|
block::{Block, BlockKind},
|
||||||
|
sprite::SpriteKind,
|
||||||
|
},
|
||||||
|
volumes::dyna::{ColumnAccess, Dyna},
|
||||||
|
};
|
||||||
|
use hashbrown::HashMap;
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use vek::Vec3;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct VoxSimple(pub String);
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ShipCentralSpec(pub HashMap<super::Body, SidedShipCentralVoxSpec>);
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct SidedShipCentralVoxSpec {
|
||||||
|
pub bone0: ShipCentralSubSpec,
|
||||||
|
pub bone1: ShipCentralSubSpec,
|
||||||
|
pub bone2: ShipCentralSubSpec,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ShipCentralSubSpec {
|
||||||
|
pub offset: [f32; 3],
|
||||||
|
pub phys_offset: [f32; 3],
|
||||||
|
pub central: VoxSimple,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// manual instead of through `make_vox_spec!` so that it can be in `common`
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ShipSpec {
|
||||||
|
pub central: AssetHandle<Ron<ShipCentralSpec>>,
|
||||||
|
pub colliders: HashMap<String, VoxelCollider>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct VoxelCollider {
|
||||||
|
pub dyna: Dyna<Block, (), ColumnAccess>,
|
||||||
|
pub translation: Vec3<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl assets::Compound for ShipSpec {
|
||||||
|
fn load<S: assets::source::Source>(
|
||||||
|
cache: &assets::AssetCache<S>,
|
||||||
|
_: &str,
|
||||||
|
) -> Result<Self, assets::Error> {
|
||||||
|
let manifest: AssetHandle<Ron<ShipCentralSpec>> =
|
||||||
|
AssetExt::load("server.manifests.ship_manifest")?;
|
||||||
|
let mut colliders = HashMap::new();
|
||||||
|
for (_, spec) in (manifest.read().0).0.iter() {
|
||||||
|
for bone in [&spec.bone0, &spec.bone1, &spec.bone2].iter() {
|
||||||
|
// TODO: Currently both client and server load models and manifests from
|
||||||
|
// "server.voxel.". In order to support CSG procedural airships, we probably
|
||||||
|
// need to load them in the server and sync them as an ECS resource.
|
||||||
|
let vox =
|
||||||
|
cache.load::<DotVoxAsset>(&["server.voxel.", &bone.central.0].concat())?;
|
||||||
|
let dyna = Dyna::<Cell, (), ColumnAccess>::from_vox(&vox.read().0, false);
|
||||||
|
let dyna = dyna.map_into(|cell| {
|
||||||
|
if let Some(rgb) = cell.get_color() {
|
||||||
|
Block::new(BlockKind::Misc, rgb)
|
||||||
|
} else {
|
||||||
|
Block::air(SpriteKind::Empty)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let collider = VoxelCollider {
|
||||||
|
dyna,
|
||||||
|
translation: Vec3::from(bone.offset) + Vec3::from(bone.phys_offset),
|
||||||
|
};
|
||||||
|
colliders.insert(bone.central.0.clone(), collider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(ShipSpec {
|
||||||
|
central: manifest,
|
||||||
|
colliders,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
// TODO: Load this from the ECS as a resource, and maybe make it more general than ships
|
||||||
|
// (although figuring out how to keep the figure bones in sync with the terrain offsets seems
|
||||||
|
// like a hard problem if they're not the same manifest)
|
||||||
|
pub static ref VOXEL_COLLIDER_MANIFEST: AssetHandle<ShipSpec> = AssetExt::load_expect("server.manifests.ship_manifest");
|
||||||
|
}
|
||||||
|
}
|
@ -48,8 +48,8 @@ pub use self::{
|
|||||||
beam::{Beam, BeamSegment},
|
beam::{Beam, BeamSegment},
|
||||||
body::{
|
body::{
|
||||||
biped_large, biped_small, bird_medium, bird_small, dragon, fish_medium, fish_small, golem,
|
biped_large, biped_small, bird_medium, bird_small, dragon, fish_medium, fish_small, golem,
|
||||||
humanoid, object, quadruped_low, quadruped_medium, quadruped_small, theropod, AllBodies,
|
humanoid, object, quadruped_low, quadruped_medium, quadruped_small, ship, theropod,
|
||||||
Body, BodyData,
|
AllBodies, Body, BodyData,
|
||||||
},
|
},
|
||||||
buff::{
|
buff::{
|
||||||
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs,
|
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffId, BuffKind, BuffSource, Buffs,
|
||||||
|
@ -33,6 +33,7 @@ pub struct PreviousPhysCache {
|
|||||||
pub collision_boundary: f32,
|
pub collision_boundary: f32,
|
||||||
pub scale: f32,
|
pub scale: f32,
|
||||||
pub scaled_radius: f32,
|
pub scaled_radius: f32,
|
||||||
|
pub ori: Quaternion<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for PreviousPhysCache {
|
impl Component for PreviousPhysCache {
|
||||||
@ -55,9 +56,12 @@ impl Component for Mass {
|
|||||||
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mass
|
// Collider
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum Collider {
|
pub enum Collider {
|
||||||
|
// TODO: pass the map from ids -> voxel data to get_radius and get_z_limits to compute a
|
||||||
|
// bounding cylinder
|
||||||
|
Voxel { id: String },
|
||||||
Box { radius: f32, z_min: f32, z_max: f32 },
|
Box { radius: f32, z_min: f32, z_max: f32 },
|
||||||
Point,
|
Point,
|
||||||
}
|
}
|
||||||
@ -65,6 +69,7 @@ pub enum Collider {
|
|||||||
impl Collider {
|
impl Collider {
|
||||||
pub fn get_radius(&self) -> f32 {
|
pub fn get_radius(&self) -> f32 {
|
||||||
match self {
|
match self {
|
||||||
|
Collider::Voxel { .. } => 1.0,
|
||||||
Collider::Box { radius, .. } => *radius,
|
Collider::Box { radius, .. } => *radius,
|
||||||
Collider::Point => 0.0,
|
Collider::Point => 0.0,
|
||||||
}
|
}
|
||||||
@ -72,6 +77,7 @@ impl Collider {
|
|||||||
|
|
||||||
pub fn get_z_limits(&self, modifier: f32) -> (f32, f32) {
|
pub fn get_z_limits(&self, modifier: f32) -> (f32, f32) {
|
||||||
match self {
|
match self {
|
||||||
|
Collider::Voxel { .. } => (0.0, 1.0),
|
||||||
Collider::Box { z_min, z_max, .. } => (*z_min * modifier, *z_max * modifier),
|
Collider::Box { z_min, z_max, .. } => (*z_min * modifier, *z_max * modifier),
|
||||||
Collider::Point => (0.0, 0.0),
|
Collider::Point => (0.0, 0.0),
|
||||||
}
|
}
|
||||||
@ -104,6 +110,7 @@ pub struct PhysicsState {
|
|||||||
pub on_wall: Option<Vec3<f32>>,
|
pub on_wall: Option<Vec3<f32>>,
|
||||||
pub touch_entities: Vec<Uid>,
|
pub touch_entities: Vec<Uid>,
|
||||||
pub in_liquid: Option<f32>, // Depth
|
pub in_liquid: Option<f32>, // Depth
|
||||||
|
pub ground_vel: Vec3<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhysicsState {
|
impl PhysicsState {
|
||||||
@ -113,6 +120,8 @@ impl PhysicsState {
|
|||||||
touch_entities.clear();
|
touch_entities.clear();
|
||||||
*self = Self {
|
*self = Self {
|
||||||
touch_entities,
|
touch_entities,
|
||||||
|
ground_vel: self.ground_vel, /* Preserved, since it's the velocity of the last
|
||||||
|
* contact point */
|
||||||
..Self::default()
|
..Self::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,17 +20,12 @@ pub type SiteId = u64;
|
|||||||
|
|
||||||
pub enum LocalEvent {
|
pub enum LocalEvent {
|
||||||
/// Applies upward force to entity's `Vel`
|
/// Applies upward force to entity's `Vel`
|
||||||
Jump(EcsEntity),
|
Jump(EcsEntity, f32),
|
||||||
/// Applies the `impulse` to `entity`'s `Vel`
|
/// Applies the `impulse` to `entity`'s `Vel`
|
||||||
ApplyImpulse {
|
ApplyImpulse {
|
||||||
entity: EcsEntity,
|
entity: EcsEntity,
|
||||||
impulse: Vec3<f32>,
|
impulse: Vec3<f32>,
|
||||||
},
|
},
|
||||||
/// Applies leaping force to `entity`'s `Vel` away from `wall_dir` direction
|
|
||||||
WallLeap {
|
|
||||||
entity: EcsEntity,
|
|
||||||
wall_dir: Vec3<f32>,
|
|
||||||
},
|
|
||||||
/// Applies `vel` velocity to `entity`
|
/// Applies `vel` velocity to `entity`
|
||||||
Boost { entity: EcsEntity, vel: Vec3<f32> },
|
Boost { entity: EcsEntity, vel: Vec3<f32> },
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use specs::Entity;
|
||||||
|
|
||||||
/// A resource that stores the time of day.
|
/// A resource that stores the time of day.
|
||||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Default)]
|
#[derive(Copy, Clone, Debug, Serialize, Deserialize, Default)]
|
||||||
@ -26,3 +27,8 @@ pub enum GameMode {
|
|||||||
// To be used later when we no longer start up an entirely new server for singleplayer
|
// To be used later when we no longer start up an entirely new server for singleplayer
|
||||||
Singleplayer,
|
Singleplayer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A resource that stores the player's entity (on the client), and None on the
|
||||||
|
/// server
|
||||||
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
|
pub struct PlayerEntity(pub Option<Entity>);
|
||||||
|
@ -46,4 +46,11 @@ impl Default for RtSimController {
|
|||||||
|
|
||||||
impl RtSimController {
|
impl RtSimController {
|
||||||
pub fn reset(&mut self) { *self = Self::default(); }
|
pub fn reset(&mut self) { *self = Self::default(); }
|
||||||
|
|
||||||
|
pub fn with_destination(pos: Vec3<f32>) -> Self {
|
||||||
|
Self {
|
||||||
|
travel_to: Some((pos, format!("{:0.1?}", pos))),
|
||||||
|
speed_factor: 0.25,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,9 @@ use crate::{
|
|||||||
util::Dir,
|
util::Dir,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::{
|
use vek::*;
|
||||||
vec::{Vec2, Vec3},
|
|
||||||
Lerp,
|
|
||||||
};
|
|
||||||
|
|
||||||
const HUMANOID_CLIMB_ACCEL: f32 = 5.0;
|
const HUMANOID_CLIMB_ACCEL: f32 = 24.0;
|
||||||
const CLIMB_SPEED: f32 = 5.0;
|
const CLIMB_SPEED: f32 = 5.0;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
||||||
@ -36,7 +33,7 @@ impl CharacterBehavior for Data {
|
|||||||
// They've climbed atop something, give them a boost
|
// They've climbed atop something, give them a boost
|
||||||
update
|
update
|
||||||
.local_events
|
.local_events
|
||||||
.push_front(LocalEvent::Jump(data.entity));
|
.push_front(LocalEvent::Jump(data.entity, BASE_JUMP_IMPULSE * 0.5));
|
||||||
}
|
}
|
||||||
update.character = CharacterState::Idle {};
|
update.character = CharacterState::Idle {};
|
||||||
return update;
|
return update;
|
||||||
@ -76,26 +73,10 @@ impl CharacterBehavior for Data {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Apply Vertical Climbing Movement
|
// Apply Vertical Climbing Movement
|
||||||
if update.vel.0.z <= CLIMB_SPEED {
|
match climb {
|
||||||
match climb {
|
Climb::Down => update.vel.0.z += data.dt.0 * (GRAVITY - HUMANOID_CLIMB_ACCEL),
|
||||||
Climb::Down => {
|
Climb::Up => update.vel.0.z += data.dt.0 * (GRAVITY + HUMANOID_CLIMB_ACCEL),
|
||||||
update.vel.0 -=
|
Climb::Hold => update.vel.0.z += data.dt.0 * GRAVITY,
|
||||||
data.dt.0 * update.vel.0.map(|e| e.abs().powf(1.5) * e.signum() * 1.0);
|
|
||||||
},
|
|
||||||
Climb::Up => {
|
|
||||||
update.vel.0.z = (update.vel.0.z + data.dt.0 * GRAVITY * 1.25).min(CLIMB_SPEED);
|
|
||||||
},
|
|
||||||
Climb::Hold => {
|
|
||||||
// Antigrav
|
|
||||||
update.vel.0.z =
|
|
||||||
(update.vel.0.z + data.dt.0 * GRAVITY * 1.075).min(CLIMB_SPEED);
|
|
||||||
update.vel.0 = Lerp::lerp(
|
|
||||||
update.vel.0,
|
|
||||||
Vec3::zero(),
|
|
||||||
30.0 * data.dt.0 / (1.0 - update.vel.0.z.min(0.0) * 5.0),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update
|
update
|
||||||
|
@ -9,8 +9,7 @@ use vek::Vec2;
|
|||||||
|
|
||||||
// Gravity is 9.81 * 4, so this makes gravity equal to .15
|
// Gravity is 9.81 * 4, so this makes gravity equal to .15
|
||||||
const GLIDE_ANTIGRAV: f32 = crate::consts::GRAVITY * 0.90;
|
const GLIDE_ANTIGRAV: f32 = crate::consts::GRAVITY * 0.90;
|
||||||
const GLIDE_ACCEL: f32 = 12.0;
|
const GLIDE_ACCEL: f32 = 5.0;
|
||||||
const GLIDE_SPEED: f32 = 45.0;
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
||||||
pub struct Data;
|
pub struct Data;
|
||||||
@ -40,13 +39,7 @@ impl CharacterBehavior for Data {
|
|||||||
handle_climb(&data, &mut update);
|
handle_climb(&data, &mut update);
|
||||||
|
|
||||||
// Move player according to movement direction vector
|
// Move player according to movement direction vector
|
||||||
update.vel.0 += Vec2::broadcast(data.dt.0)
|
update.vel.0 += Vec2::broadcast(data.dt.0) * data.inputs.move_dir * GLIDE_ACCEL;
|
||||||
* data.inputs.move_dir
|
|
||||||
* if data.vel.0.magnitude_squared() < GLIDE_SPEED.powi(2) {
|
|
||||||
GLIDE_ACCEL
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
};
|
|
||||||
|
|
||||||
// Determine orientation vector from movement direction vector
|
// Determine orientation vector from movement direction vector
|
||||||
let horiz_vel = Vec2::<f32>::from(update.vel.0);
|
let horiz_vel = Vec2::<f32>::from(update.vel.0);
|
||||||
@ -56,7 +49,7 @@ impl CharacterBehavior for Data {
|
|||||||
|
|
||||||
// Apply Glide antigrav lift
|
// Apply Glide antigrav lift
|
||||||
let horiz_speed_sq = horiz_vel.magnitude_squared();
|
let horiz_speed_sq = horiz_vel.magnitude_squared();
|
||||||
if horiz_speed_sq < GLIDE_SPEED.powi(2) && update.vel.0.z < 0.0 {
|
if update.vel.0.z < 0.0 {
|
||||||
let lift = (GLIDE_ANTIGRAV + update.vel.0.z.powi(2) * 0.15)
|
let lift = (GLIDE_ANTIGRAV + update.vel.0.z.powi(2) * 0.15)
|
||||||
* (horiz_speed_sq * f32::powf(0.075, 2.0)).clamp(0.2, 1.0);
|
* (horiz_speed_sq * f32::powf(0.075, 2.0)).clamp(0.2, 1.0);
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ use crate::{
|
|||||||
biped_large, biped_small,
|
biped_large, biped_small,
|
||||||
inventory::slot::EquipSlot,
|
inventory::slot::EquipSlot,
|
||||||
item::{Hands, ItemKind, Tool, ToolKind},
|
item::{Hands, ItemKind, Tool, ToolKind},
|
||||||
quadruped_low, quadruped_medium, quadruped_small,
|
quadruped_low, quadruped_medium, quadruped_small, ship,
|
||||||
skills::Skill,
|
skills::Skill,
|
||||||
theropod, Body, CharacterAbility, CharacterState, InputKind, InventoryAction, StateUpdate,
|
theropod, Body, CharacterAbility, CharacterState, InputKind, InventoryAction, StateUpdate,
|
||||||
},
|
},
|
||||||
@ -17,10 +17,11 @@ use std::time::Duration;
|
|||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0;
|
pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0;
|
||||||
const BASE_HUMANOID_AIR_ACCEL: f32 = 8.0;
|
const BASE_HUMANOID_AIR_ACCEL: f32 = 2.0;
|
||||||
const BASE_FLIGHT_ACCEL: f32 = 16.0;
|
const BASE_FLIGHT_ACCEL: f32 = 2.0;
|
||||||
const BASE_HUMANOID_WATER_ACCEL: f32 = 150.0;
|
const BASE_HUMANOID_WATER_ACCEL: f32 = 150.0;
|
||||||
const BASE_HUMANOID_WATER_SPEED: f32 = 180.0;
|
const BASE_HUMANOID_WATER_SPEED: f32 = 180.0;
|
||||||
|
pub const BASE_JUMP_IMPULSE: f32 = 16.0;
|
||||||
// const BASE_HUMANOID_CLIMB_ACCEL: f32 = 10.0;
|
// const BASE_HUMANOID_CLIMB_ACCEL: f32 = 10.0;
|
||||||
// const ROLL_SPEED: f32 = 17.0;
|
// const ROLL_SPEED: f32 = 17.0;
|
||||||
// const CHARGE_SPEED: f32 = 20.0;
|
// const CHARGE_SPEED: f32 = 20.0;
|
||||||
@ -117,6 +118,7 @@ impl Body {
|
|||||||
quadruped_low::Species::Basilisk => 120.0,
|
quadruped_low::Species::Basilisk => 120.0,
|
||||||
quadruped_low::Species::Deadwood => 140.0,
|
quadruped_low::Species::Deadwood => 140.0,
|
||||||
},
|
},
|
||||||
|
Body::Ship(_) => 30.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,14 +170,24 @@ impl Body {
|
|||||||
quadruped_low::Species::Lavadrake => 4.0,
|
quadruped_low::Species::Lavadrake => 4.0,
|
||||||
_ => 6.0,
|
_ => 6.0,
|
||||||
},
|
},
|
||||||
|
Body::Ship(_) => 0.175,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_fly(&self) -> bool {
|
/// Returns flying speed if the body type can fly, otherwise None
|
||||||
matches!(
|
pub fn can_fly(&self) -> Option<f32> {
|
||||||
self,
|
match self {
|
||||||
Body::BirdMedium(_) | Body::Dragon(_) | Body::BirdSmall(_)
|
Body::BirdMedium(_) | Body::Dragon(_) | Body::BirdSmall(_) => Some(1.0),
|
||||||
)
|
Body::Ship(ship::Body::DefaultAirship) => Some(1.0),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn jump_impulse(&self) -> Option<f32> {
|
||||||
|
match self {
|
||||||
|
Body::Object(_) | Body::Ship(_) => None,
|
||||||
|
_ => Some(BASE_JUMP_IMPULSE),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_climb(&self) -> bool { matches!(self, Body::Humanoid(_)) }
|
pub fn can_climb(&self) -> bool { matches!(self, Body::Humanoid(_)) }
|
||||||
@ -186,10 +198,18 @@ pub fn handle_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
|||||||
if let Some(depth) = data.physics.in_liquid {
|
if let Some(depth) = data.physics.in_liquid {
|
||||||
swim_move(data, update, efficiency, depth);
|
swim_move(data, update, efficiency, depth);
|
||||||
} else if input_is_pressed(data, InputKind::Fly)
|
} else if input_is_pressed(data, InputKind::Fly)
|
||||||
&& !data.physics.on_ground
|
&& (!data.physics.on_ground || data.body.jump_impulse().is_none())
|
||||||
&& data.body.can_fly()
|
&& data.body.can_fly().is_some()
|
||||||
{
|
{
|
||||||
fly_move(data, update, efficiency);
|
fly_move(
|
||||||
|
data,
|
||||||
|
update,
|
||||||
|
efficiency
|
||||||
|
* data
|
||||||
|
.body
|
||||||
|
.can_fly()
|
||||||
|
.expect("can_fly is_some right above this"),
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
basic_move(data, update, efficiency);
|
basic_move(data, update, efficiency);
|
||||||
}
|
}
|
||||||
@ -289,7 +309,11 @@ fn swim_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32, depth:
|
|||||||
}
|
}
|
||||||
* efficiency;
|
* efficiency;
|
||||||
|
|
||||||
handle_orientation(data, update, if data.physics.on_ground { 9.0 } else { 2.0 });
|
handle_orientation(
|
||||||
|
data,
|
||||||
|
update,
|
||||||
|
data.body.base_ori_rate() * if data.physics.on_ground { 0.5 } else { 0.1 },
|
||||||
|
);
|
||||||
|
|
||||||
// Swim
|
// Swim
|
||||||
update.vel.0.z = (update.vel.0.z
|
update.vel.0.z = (update.vel.0.z
|
||||||
@ -306,7 +330,6 @@ fn swim_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32, depth:
|
|||||||
/// Updates components to move entity as if it's flying
|
/// Updates components to move entity as if it's flying
|
||||||
fn fly_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
fn fly_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
||||||
// Update velocity (counteract gravity with lift)
|
// Update velocity (counteract gravity with lift)
|
||||||
// TODO: Do this better
|
|
||||||
update.vel.0 += Vec3::unit_z() * data.dt.0 * GRAVITY
|
update.vel.0 += Vec3::unit_z() * data.dt.0 * GRAVITY
|
||||||
+ Vec3::new(
|
+ Vec3::new(
|
||||||
data.inputs.move_dir.x,
|
data.inputs.move_dir.x,
|
||||||
@ -316,7 +339,7 @@ fn fly_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
|||||||
* BASE_FLIGHT_ACCEL
|
* BASE_FLIGHT_ACCEL
|
||||||
* efficiency;
|
* efficiency;
|
||||||
|
|
||||||
handle_orientation(data, update, 1.0);
|
handle_orientation(data, update, data.body.base_ori_rate());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if an input related to an attack is held. If one is, moves entity
|
/// Checks if an input related to an attack is held. If one is, moves entity
|
||||||
@ -429,10 +452,12 @@ pub fn handle_jump(data: &JoinData, update: &mut StateUpdate) {
|
|||||||
.in_liquid
|
.in_liquid
|
||||||
.map(|depth| depth > 1.0)
|
.map(|depth| depth > 1.0)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
|
&& data.body.jump_impulse().is_some()
|
||||||
{
|
{
|
||||||
update
|
update.local_events.push_front(LocalEvent::Jump(
|
||||||
.local_events
|
data.entity,
|
||||||
.push_front(LocalEvent::Jump(data.entity));
|
data.body.jump_impulse().unwrap(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,11 +35,11 @@ impl Cylinder {
|
|||||||
pub fn from_components(
|
pub fn from_components(
|
||||||
pos: Vec3<f32>,
|
pos: Vec3<f32>,
|
||||||
scale: Option<crate::comp::Scale>,
|
scale: Option<crate::comp::Scale>,
|
||||||
collider: Option<crate::comp::Collider>,
|
collider: Option<&crate::comp::Collider>,
|
||||||
char_state: Option<&crate::comp::CharacterState>,
|
char_state: Option<&crate::comp::CharacterState>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let scale = scale.map_or(1.0, |s| s.0);
|
let scale = scale.map_or(1.0, |s| s.0);
|
||||||
let radius = collider.map_or(0.5, |c| c.get_radius()) * scale;
|
let radius = collider.as_ref().map_or(0.5, |c| c.get_radius()) * scale;
|
||||||
let z_limit_modifier = char_state
|
let z_limit_modifier = char_state
|
||||||
.filter(|char_state| char_state.is_dodge())
|
.filter(|char_state| char_state.is_dodge())
|
||||||
.map_or(1.0, |_| 0.5)
|
.map_or(1.0, |_| 0.5)
|
||||||
|
@ -44,6 +44,21 @@ impl<V, M, A: Access> Dyna<V, M, A> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn map_into<W, F: FnMut(V) -> W>(self, f: F) -> Dyna<W, M, A> {
|
||||||
|
let Dyna {
|
||||||
|
vox,
|
||||||
|
meta,
|
||||||
|
sz,
|
||||||
|
_phantom,
|
||||||
|
} = self;
|
||||||
|
Dyna {
|
||||||
|
vox: vox.into_iter().map(f).collect(),
|
||||||
|
meta,
|
||||||
|
sz,
|
||||||
|
_phantom,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V, M, A: Access> BaseVol for Dyna<V, M, A> {
|
impl<V, M, A: Access> BaseVol for Dyna<V, M, A> {
|
||||||
|
@ -153,15 +153,6 @@ impl<'a> System<'a> for Sys {
|
|||||||
// Do nothing
|
// Do nothing
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// If mounted, character state is controlled by mount
|
|
||||||
// TODO: Make mounting a state
|
|
||||||
if let Some(Mounting(_)) = read_data.mountings.get(entity) {
|
|
||||||
let sit_state = CharacterState::Sit {};
|
|
||||||
if char_state.get_unchecked() != &sit_state {
|
|
||||||
*char_state.get_mut_unchecked() = sit_state;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enter stunned state if poise damage is enough
|
// Enter stunned state if poise damage is enough
|
||||||
if let Some(mut poise) = poises.get_mut(entity) {
|
if let Some(mut poise) = poises.get_mut(entity) {
|
||||||
@ -317,6 +308,17 @@ impl<'a> System<'a> for Sys {
|
|||||||
incorporate_update(&mut join_struct, state_update);
|
incorporate_update(&mut join_struct, state_update);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mounted occurs after control actions have been handled
|
||||||
|
// If mounted, character state is controlled by mount
|
||||||
|
// TODO: Make mounting a state
|
||||||
|
if let Some(Mounting(_)) = read_data.mountings.get(entity) {
|
||||||
|
let sit_state = CharacterState::Sit {};
|
||||||
|
if join_struct.char_state.get_unchecked() != &sit_state {
|
||||||
|
*join_struct.char_state.get_mut_unchecked() = sit_state;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let j = JoinData::new(
|
let j = JoinData::new(
|
||||||
&join_struct,
|
&join_struct,
|
||||||
&read_data.lazy_update,
|
&read_data.lazy_update,
|
||||||
|
63
common/sys/src/interpolation.rs
Normal file
63
common/sys/src/interpolation.rs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
use common::{
|
||||||
|
comp::{Ori, Pos, Vel},
|
||||||
|
resources::{PlayerEntity, Time},
|
||||||
|
};
|
||||||
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
|
use common_net::sync::InterpolatableComponent;
|
||||||
|
use specs::{
|
||||||
|
prelude::ParallelIterator, shred::ResourceId, Entities, ParJoin, Read, ReadStorage, SystemData,
|
||||||
|
World, WriteStorage,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(SystemData)]
|
||||||
|
pub struct InterpolationSystemData<'a> {
|
||||||
|
time: Read<'a, Time>,
|
||||||
|
player: Read<'a, PlayerEntity>,
|
||||||
|
entities: Entities<'a>,
|
||||||
|
pos: WriteStorage<'a, Pos>,
|
||||||
|
pos_interpdata: ReadStorage<'a, <Pos as InterpolatableComponent>::InterpData>,
|
||||||
|
vel: WriteStorage<'a, Vel>,
|
||||||
|
vel_interpdata: ReadStorage<'a, <Vel as InterpolatableComponent>::InterpData>,
|
||||||
|
ori: WriteStorage<'a, Ori>,
|
||||||
|
ori_interpdata: ReadStorage<'a, <Ori as InterpolatableComponent>::InterpData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct InterpolationSystem;
|
||||||
|
|
||||||
|
impl<'a> System<'a> for InterpolationSystem {
|
||||||
|
type SystemData = InterpolationSystemData<'a>;
|
||||||
|
|
||||||
|
const NAME: &'static str = "interpolation";
|
||||||
|
const ORIGIN: Origin = Origin::Common;
|
||||||
|
const PHASE: Phase = Phase::Apply;
|
||||||
|
|
||||||
|
fn run(_job: &mut Job<Self>, mut data: InterpolationSystemData<'a>) {
|
||||||
|
let time = data.time.0;
|
||||||
|
let player = data.player.0;
|
||||||
|
|
||||||
|
(
|
||||||
|
&data.entities,
|
||||||
|
&mut data.pos,
|
||||||
|
&data.pos_interpdata,
|
||||||
|
&data.vel_interpdata,
|
||||||
|
)
|
||||||
|
.par_join()
|
||||||
|
.filter(|(e, _, _, _)| Some(e) != player.as_ref())
|
||||||
|
.for_each(|(_, pos, interp, vel)| {
|
||||||
|
*pos = pos.interpolate(interp, time, vel);
|
||||||
|
});
|
||||||
|
(&data.entities, &mut data.vel, &data.vel_interpdata)
|
||||||
|
.par_join()
|
||||||
|
.filter(|(e, _, _)| Some(e) != player.as_ref())
|
||||||
|
.for_each(|(_, vel, interp)| {
|
||||||
|
*vel = vel.interpolate(interp, time, &());
|
||||||
|
});
|
||||||
|
(&data.entities, &mut data.ori, &data.ori_interpdata)
|
||||||
|
.par_join()
|
||||||
|
.filter(|(e, _, _)| Some(e) != player.as_ref())
|
||||||
|
.for_each(|(_, ori, interp)| {
|
||||||
|
*ori = ori.interpolate(interp, time, &());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ mod beam;
|
|||||||
mod buff;
|
mod buff;
|
||||||
pub mod character_behavior;
|
pub mod character_behavior;
|
||||||
pub mod controller;
|
pub mod controller;
|
||||||
|
mod interpolation;
|
||||||
pub mod melee;
|
pub mod melee;
|
||||||
mod mount;
|
mod mount;
|
||||||
pub mod phys;
|
pub mod phys;
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{Controller, MountState, Mounting, Ori, Pos, Vel},
|
comp::{Body, Controller, MountState, Mounting, Ori, Pos, Vel},
|
||||||
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, WriteStorage,
|
Entities, Join, Read, ReadStorage, WriteStorage,
|
||||||
};
|
};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -23,6 +23,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
WriteStorage<'a, Pos>,
|
WriteStorage<'a, Pos>,
|
||||||
WriteStorage<'a, Vel>,
|
WriteStorage<'a, Vel>,
|
||||||
WriteStorage<'a, Ori>,
|
WriteStorage<'a, Ori>,
|
||||||
|
ReadStorage<'a, Body>,
|
||||||
);
|
);
|
||||||
|
|
||||||
const NAME: &'static str = "mount";
|
const NAME: &'static str = "mount";
|
||||||
@ -40,21 +41,24 @@ impl<'a> System<'a> for Sys {
|
|||||||
mut positions,
|
mut positions,
|
||||||
mut velocities,
|
mut velocities,
|
||||||
mut orientations,
|
mut orientations,
|
||||||
|
bodies,
|
||||||
): Self::SystemData,
|
): Self::SystemData,
|
||||||
) {
|
) {
|
||||||
// Mounted entities.
|
// Mounted entities.
|
||||||
for (entity, mut mount_states) in (&entities, &mut mount_state.restrict_mut()).join() {
|
for (entity, mut mount_states, body) in
|
||||||
|
(&entities, &mut mount_state.restrict_mut(), bodies.maybe()).join()
|
||||||
|
{
|
||||||
match mount_states.get_unchecked() {
|
match mount_states.get_unchecked() {
|
||||||
MountState::Unmounted => {},
|
MountState::Unmounted => {},
|
||||||
MountState::MountedBy(mounter_uid) => {
|
MountState::MountedBy(mounter_uid) => {
|
||||||
// Note: currently controller events are not passed through since none of them
|
// Note: currently controller events are not passed through since none of them
|
||||||
// are currently relevant to controlling the mounted entity
|
// are currently relevant to controlling the mounted entity
|
||||||
if let Some((inputs, mounter)) = uid_allocator
|
if let Some((inputs, queued_inputs, mounter)) = uid_allocator
|
||||||
.retrieve_entity_internal(mounter_uid.id())
|
.retrieve_entity_internal(mounter_uid.id())
|
||||||
.and_then(|mounter| {
|
.and_then(|mounter| {
|
||||||
controllers
|
controllers
|
||||||
.get(mounter)
|
.get(mounter)
|
||||||
.map(|c| (c.inputs.clone(), mounter))
|
.map(|c| (c.inputs.clone(), c.queued_inputs.clone(), mounter))
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
// TODO: consider joining on these? (remember we can use .maybe())
|
// TODO: consider joining on these? (remember we can use .maybe())
|
||||||
@ -62,13 +66,16 @@ impl<'a> System<'a> for Sys {
|
|||||||
let ori = orientations.get(entity).copied();
|
let ori = orientations.get(entity).copied();
|
||||||
let vel = velocities.get(entity).copied();
|
let vel = velocities.get(entity).copied();
|
||||||
if let (Some(pos), Some(ori), Some(vel)) = (pos, ori, vel) {
|
if let (Some(pos), Some(ori), Some(vel)) = (pos, ori, vel) {
|
||||||
let _ = positions.insert(mounter, Pos(pos.0 + Vec3::unit_z() * 1.0));
|
let mounting_offset =
|
||||||
|
body.map_or(Vec3::unit_z(), Body::mounting_offset);
|
||||||
|
let _ = positions.insert(mounter, Pos(pos.0 + mounting_offset));
|
||||||
let _ = orientations.insert(mounter, ori);
|
let _ = orientations.insert(mounter, ori);
|
||||||
let _ = velocities.insert(mounter, vel);
|
let _ = velocities.insert(mounter, vel);
|
||||||
}
|
}
|
||||||
if let Some(controller) = controllers.get_mut(entity) {
|
if let Some(controller) = controllers.get_mut(entity) {
|
||||||
*controller = Controller {
|
*controller = Controller {
|
||||||
inputs,
|
inputs,
|
||||||
|
queued_inputs,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@ use common::{
|
|||||||
comp,
|
comp,
|
||||||
event::{EventBus, LocalEvent, ServerEvent},
|
event::{EventBus, LocalEvent, ServerEvent},
|
||||||
region::RegionMap,
|
region::RegionMap,
|
||||||
resources::{DeltaTime, GameMode, Time, TimeOfDay},
|
resources::{DeltaTime, GameMode, PlayerEntity, Time, TimeOfDay},
|
||||||
terrain::{Block, TerrainChunk, TerrainGrid},
|
terrain::{Block, TerrainChunk, TerrainGrid},
|
||||||
time::DayPeriod,
|
time::DayPeriod,
|
||||||
trade::Trades,
|
trade::Trades,
|
||||||
@ -14,8 +14,8 @@ use common::{
|
|||||||
vol::{ReadVol, WriteVol},
|
vol::{ReadVol, WriteVol},
|
||||||
};
|
};
|
||||||
use common_base::span;
|
use common_base::span;
|
||||||
use common_ecs::{PhysicsMetrics, SysMetrics};
|
use common_ecs::{run_now, PhysicsMetrics, SysMetrics};
|
||||||
use common_net::sync::WorldSyncExt;
|
use common_net::sync::{interpolation as sync_interp, WorldSyncExt};
|
||||||
use hashbrown::{HashMap, HashSet};
|
use hashbrown::{HashMap, HashSet};
|
||||||
use rayon::{ThreadPool, ThreadPoolBuilder};
|
use rayon::{ThreadPool, ThreadPoolBuilder};
|
||||||
use specs::{
|
use specs::{
|
||||||
@ -36,7 +36,6 @@ const DAY_CYCLE_FACTOR: f64 = 24.0 * 2.0;
|
|||||||
/// this value, the game's physics will begin to produce time lag. Ideally, we'd
|
/// this value, the game's physics will begin to produce time lag. Ideally, we'd
|
||||||
/// avoid such a situation.
|
/// avoid such a situation.
|
||||||
const MAX_DELTA_TIME: f32 = 1.0;
|
const MAX_DELTA_TIME: f32 = 1.0;
|
||||||
const HUMANOID_JUMP_ACCEL: f32 = 16.0;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct BlockChange {
|
pub struct BlockChange {
|
||||||
@ -165,6 +164,9 @@ impl State {
|
|||||||
// Register client-local components
|
// Register client-local components
|
||||||
// TODO: only register on the client
|
// TODO: only register on the client
|
||||||
ecs.register::<comp::LightAnimation>();
|
ecs.register::<comp::LightAnimation>();
|
||||||
|
ecs.register::<sync_interp::InterpBuffer<comp::Pos>>();
|
||||||
|
ecs.register::<sync_interp::InterpBuffer<comp::Vel>>();
|
||||||
|
ecs.register::<sync_interp::InterpBuffer<comp::Ori>>();
|
||||||
|
|
||||||
// Register server-local components
|
// Register server-local components
|
||||||
// TODO: only register on the server
|
// TODO: only register on the server
|
||||||
@ -194,6 +196,7 @@ impl State {
|
|||||||
// Register unsynced resources used by the ECS.
|
// Register unsynced resources used by the ECS.
|
||||||
ecs.insert(Time(0.0));
|
ecs.insert(Time(0.0));
|
||||||
ecs.insert(DeltaTime(0.0));
|
ecs.insert(DeltaTime(0.0));
|
||||||
|
ecs.insert(PlayerEntity(None));
|
||||||
ecs.insert(TerrainGrid::new().unwrap());
|
ecs.insert(TerrainGrid::new().unwrap());
|
||||||
ecs.insert(BlockChange::default());
|
ecs.insert(BlockChange::default());
|
||||||
ecs.insert(TerrainChanges::default());
|
ecs.insert(TerrainChanges::default());
|
||||||
@ -438,6 +441,7 @@ impl State {
|
|||||||
let mut dispatcher = dispatch_builder.build();
|
let mut dispatcher = dispatch_builder.build();
|
||||||
drop(guard);
|
drop(guard);
|
||||||
span!(guard, "run systems");
|
span!(guard, "run systems");
|
||||||
|
run_now::<crate::interpolation::InterpolationSystem>(&self.ecs);
|
||||||
dispatcher.dispatch(&self.ecs);
|
dispatcher.dispatch(&self.ecs);
|
||||||
drop(guard);
|
drop(guard);
|
||||||
|
|
||||||
@ -454,11 +458,11 @@ impl State {
|
|||||||
let events = self.ecs.read_resource::<EventBus<LocalEvent>>().recv_all();
|
let events = self.ecs.read_resource::<EventBus<LocalEvent>>().recv_all();
|
||||||
for event in events {
|
for event in events {
|
||||||
let mut velocities = self.ecs.write_storage::<comp::Vel>();
|
let mut velocities = self.ecs.write_storage::<comp::Vel>();
|
||||||
let mut controllers = self.ecs.write_storage::<comp::Controller>();
|
let physics = self.ecs.read_storage::<comp::PhysicsState>();
|
||||||
match event {
|
match event {
|
||||||
LocalEvent::Jump(entity) => {
|
LocalEvent::Jump(entity, impulse) => {
|
||||||
if let Some(vel) = velocities.get_mut(entity) {
|
if let Some(vel) = velocities.get_mut(entity) {
|
||||||
vel.0.z = HUMANOID_JUMP_ACCEL;
|
vel.0.z = impulse + physics.get(entity).map_or(0.0, |ps| ps.ground_vel.z);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
LocalEvent::ApplyImpulse { entity, impulse } => {
|
LocalEvent::ApplyImpulse { entity, impulse } => {
|
||||||
@ -466,21 +470,6 @@ impl State {
|
|||||||
vel.0 = impulse;
|
vel.0 = impulse;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
LocalEvent::WallLeap { entity, wall_dir } => {
|
|
||||||
if let (Some(vel), Some(_controller)) =
|
|
||||||
(velocities.get_mut(entity), controllers.get_mut(entity))
|
|
||||||
{
|
|
||||||
let hspeed = Vec2::<f32>::from(vel.0).magnitude();
|
|
||||||
if hspeed > 0.001 && hspeed < 0.5 {
|
|
||||||
vel.0 += vel.0.normalized()
|
|
||||||
* Vec3::new(1.0, 1.0, 0.0)
|
|
||||||
* HUMANOID_JUMP_ACCEL
|
|
||||||
* 1.5
|
|
||||||
- wall_dir * 0.03;
|
|
||||||
vel.0.z = HUMANOID_JUMP_ACCEL * 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
LocalEvent::Boost {
|
LocalEvent::Boost {
|
||||||
entity,
|
entity,
|
||||||
vel: extra_vel,
|
vel: extra_vel,
|
||||||
|
@ -77,6 +77,7 @@ type CommandHandler = fn(&mut Server, EcsEntity, EcsEntity, String, &ChatCommand
|
|||||||
fn get_handler(cmd: &ChatCommand) -> CommandHandler {
|
fn get_handler(cmd: &ChatCommand) -> CommandHandler {
|
||||||
match cmd {
|
match cmd {
|
||||||
ChatCommand::Adminify => handle_adminify,
|
ChatCommand::Adminify => handle_adminify,
|
||||||
|
ChatCommand::Airship => handle_spawn_airship,
|
||||||
ChatCommand::Alias => handle_alias,
|
ChatCommand::Alias => handle_alias,
|
||||||
ChatCommand::Ban => handle_ban,
|
ChatCommand::Ban => handle_ban,
|
||||||
ChatCommand::Build => handle_build,
|
ChatCommand::Build => handle_build,
|
||||||
@ -984,6 +985,51 @@ fn handle_spawn_training_dummy(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_spawn_airship(
|
||||||
|
server: &mut Server,
|
||||||
|
client: EcsEntity,
|
||||||
|
target: EcsEntity,
|
||||||
|
args: String,
|
||||||
|
action: &ChatCommand,
|
||||||
|
) {
|
||||||
|
let angle = scan_fmt!(&args, &action.arg_fmt(), f32).ok();
|
||||||
|
match server.state.read_component_copied::<comp::Pos>(target) {
|
||||||
|
Some(mut pos) => {
|
||||||
|
pos.0.z += 50.0;
|
||||||
|
const DESTINATION_RADIUS: f32 = 2000.0;
|
||||||
|
let angle = angle.map(|a| a * std::f32::consts::PI / 180.0);
|
||||||
|
let destination = angle.map(|a| {
|
||||||
|
pos.0
|
||||||
|
+ Vec3::new(
|
||||||
|
DESTINATION_RADIUS * a.cos(),
|
||||||
|
DESTINATION_RADIUS * a.sin(),
|
||||||
|
200.0,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
server
|
||||||
|
.state
|
||||||
|
.create_ship(pos, comp::ship::Body::DefaultAirship, 1, destination)
|
||||||
|
.with(comp::Scale(comp::ship::AIRSHIP_SCALE))
|
||||||
|
.with(LightEmitter {
|
||||||
|
col: Rgb::new(1.0, 0.65, 0.2),
|
||||||
|
strength: 2.0,
|
||||||
|
flicker: 1.0,
|
||||||
|
animated: true,
|
||||||
|
})
|
||||||
|
.build();
|
||||||
|
|
||||||
|
server.notify_client(
|
||||||
|
client,
|
||||||
|
ServerGeneral::server_msg(ChatType::CommandInfo, "Spawned an airship"),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
None => server.notify_client(
|
||||||
|
client,
|
||||||
|
ServerGeneral::server_msg(ChatType::CommandError, "You have no position!"),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_spawn_campfire(
|
fn handle_spawn_campfire(
|
||||||
server: &mut Server,
|
server: &mut Server,
|
||||||
client: EcsEntity,
|
client: EcsEntity,
|
||||||
|
@ -129,7 +129,12 @@ pub fn handle_shoot(
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
let vel = *dir * speed;
|
let vel = *dir * speed
|
||||||
|
+ state
|
||||||
|
.ecs()
|
||||||
|
.read_storage::<Vel>()
|
||||||
|
.get(entity)
|
||||||
|
.map_or(Vec3::zero(), |v| v.0);
|
||||||
|
|
||||||
// Add an outcome
|
// Add an outcome
|
||||||
state
|
state
|
||||||
|
@ -69,7 +69,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
|||||||
find_dist::Cylinder::from_components(
|
find_dist::Cylinder::from_components(
|
||||||
p.0,
|
p.0,
|
||||||
scales.get(entity).copied(),
|
scales.get(entity).copied(),
|
||||||
colliders.get(entity).copied(),
|
colliders.get(entity),
|
||||||
char_states.get(entity),
|
char_states.get(entity),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -27,7 +27,10 @@ impl Entity {
|
|||||||
|
|
||||||
pub fn get_body(&self) -> comp::Body {
|
pub fn get_body(&self) -> comp::Body {
|
||||||
match self.rng(PERM_GENUS).gen::<f32>() {
|
match self.rng(PERM_GENUS).gen::<f32>() {
|
||||||
//we want 50% birds, 50% humans for now
|
// we want 5% airships, 45% birds, 50% humans
|
||||||
|
// TODO: uncomment this to re-enable RtSim airships once physics is interpolated well
|
||||||
|
// in multiplayer.
|
||||||
|
//x if x < 0.05 => comp::Body::Ship(comp::ship::Body::DefaultAirship),
|
||||||
x if x < 0.50 => {
|
x if x < 0.50 => {
|
||||||
let species = *(&comp::bird_medium::ALL_SPECIES)
|
let species = *(&comp::bird_medium::ALL_SPECIES)
|
||||||
.choose(&mut self.rng(PERM_SPECIES))
|
.choose(&mut self.rng(PERM_SPECIES))
|
||||||
@ -53,6 +56,7 @@ impl Entity {
|
|||||||
comp::Body::BirdSmall(_) => "Warbler".to_string(),
|
comp::Body::BirdSmall(_) => "Warbler".to_string(),
|
||||||
comp::Body::Dragon(b) => get_npc_name(&npc_names.dragon, b.species).to_string(),
|
comp::Body::Dragon(b) => get_npc_name(&npc_names.dragon, b.species).to_string(),
|
||||||
comp::Body::Humanoid(b) => get_npc_name(&npc_names.humanoid, b.species).to_string(),
|
comp::Body::Humanoid(b) => get_npc_name(&npc_names.humanoid, b.species).to_string(),
|
||||||
|
comp::Body::Ship(_) => "Veloren Air".to_string(),
|
||||||
//TODO: finish match as necessary
|
//TODO: finish match as necessary
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
@ -131,6 +135,7 @@ impl Entity {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter(|s| match self.get_body() {
|
.filter(|s| match self.get_body() {
|
||||||
comp::Body::Humanoid(_) => s.1.is_settlement() | s.1.is_castle(),
|
comp::Body::Humanoid(_) => s.1.is_settlement() | s.1.is_castle(),
|
||||||
|
comp::Body::Ship(_) => s.1.is_settlement(),
|
||||||
_ => s.1.is_dungeon(),
|
_ => s.1.is_dungeon(),
|
||||||
})
|
})
|
||||||
.filter(|_| thread_rng().gen_range(0i32..4) == 0)
|
.filter(|_| thread_rng().gen_range(0i32..4) == 0)
|
||||||
|
@ -122,7 +122,10 @@ impl<'a> System<'a> for Sys {
|
|||||||
comp::Body::Humanoid(_) => comp::Alignment::Npc,
|
comp::Body::Humanoid(_) => comp::Alignment::Npc,
|
||||||
_ => comp::Alignment::Wild,
|
_ => comp::Alignment::Wild,
|
||||||
},
|
},
|
||||||
scale: comp::Scale(1.0),
|
scale: match body {
|
||||||
|
comp::Body::Ship(_) => comp::Scale(comp::ship::AIRSHIP_SCALE),
|
||||||
|
_ => comp::Scale(1.0),
|
||||||
|
},
|
||||||
drop_item: None,
|
drop_item: None,
|
||||||
home_chunk: None,
|
home_chunk: None,
|
||||||
rtsim_entity: Some(RtSimEntity(id)),
|
rtsim_entity: Some(RtSimEntity(id)),
|
||||||
|
@ -41,6 +41,13 @@ pub trait StateExt {
|
|||||||
) -> EcsEntityBuilder;
|
) -> EcsEntityBuilder;
|
||||||
/// Build a static object entity
|
/// Build a static object entity
|
||||||
fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder;
|
fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder;
|
||||||
|
fn create_ship(
|
||||||
|
&mut self,
|
||||||
|
pos: comp::Pos,
|
||||||
|
ship: comp::ship::Body,
|
||||||
|
level: u16,
|
||||||
|
destination: Option<Vec3<f32>>,
|
||||||
|
) -> EcsEntityBuilder;
|
||||||
/// Build a projectile
|
/// Build a projectile
|
||||||
fn create_projectile(
|
fn create_projectile(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -172,10 +179,15 @@ impl StateExt for State {
|
|||||||
))
|
))
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
)
|
)
|
||||||
.with(comp::Collider::Box {
|
.with(match body {
|
||||||
radius: body.radius(),
|
comp::Body::Ship(ship) => comp::Collider::Voxel {
|
||||||
z_min: 0.0,
|
id: ship.manifest_entry().to_string(),
|
||||||
z_max: body.height(),
|
},
|
||||||
|
_ => comp::Collider::Box {
|
||||||
|
radius: body.radius(),
|
||||||
|
z_min: 0.0,
|
||||||
|
z_max: body.height(),
|
||||||
|
},
|
||||||
})
|
})
|
||||||
.with(comp::Controller::default())
|
.with(comp::Controller::default())
|
||||||
.with(body)
|
.with(body)
|
||||||
@ -215,6 +227,42 @@ impl StateExt for State {
|
|||||||
.with(comp::Gravity(1.0))
|
.with(comp::Gravity(1.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_ship(
|
||||||
|
&mut self,
|
||||||
|
pos: comp::Pos,
|
||||||
|
ship: comp::ship::Body,
|
||||||
|
level: u16,
|
||||||
|
destination: Option<Vec3<f32>>,
|
||||||
|
) -> EcsEntityBuilder {
|
||||||
|
let mut builder = self
|
||||||
|
.ecs_mut()
|
||||||
|
.create_entity_synced()
|
||||||
|
.with(pos)
|
||||||
|
.with(comp::Vel(Vec3::zero()))
|
||||||
|
.with(comp::Ori::default())
|
||||||
|
.with(comp::Mass(50.0))
|
||||||
|
.with(comp::Collider::Voxel {
|
||||||
|
id: ship.manifest_entry().to_string(),
|
||||||
|
})
|
||||||
|
.with(comp::Body::Ship(ship))
|
||||||
|
.with(comp::Gravity(1.0))
|
||||||
|
.with(comp::Controller::default())
|
||||||
|
.with(comp::inventory::Inventory::new_empty())
|
||||||
|
.with(comp::CharacterState::default())
|
||||||
|
// TODO: some of these are required in order for the character_behavior system to
|
||||||
|
// recognize a possesed airship; that system should be refactored to use `.maybe()`
|
||||||
|
.with(comp::Energy::new(ship.into(), level))
|
||||||
|
.with(comp::Health::new(ship.into(), level))
|
||||||
|
.with(comp::Stats::new("Airship".to_string()))
|
||||||
|
.with(comp::Buffs::default())
|
||||||
|
.with(comp::MountState::Unmounted)
|
||||||
|
.with(comp::Combo::default());
|
||||||
|
if let Some(pos) = destination {
|
||||||
|
builder = builder.with(comp::Agent::with_destination(pos))
|
||||||
|
}
|
||||||
|
builder
|
||||||
|
}
|
||||||
|
|
||||||
fn create_projectile(
|
fn create_projectile(
|
||||||
&mut self,
|
&mut self,
|
||||||
pos: comp::Pos,
|
pos: comp::Pos,
|
||||||
|
@ -34,7 +34,7 @@ use specs::{
|
|||||||
Entities, Entity as EcsEntity, Join, ParJoin, Read, ReadExpect, ReadStorage, SystemData, World,
|
Entities, Entity as EcsEntity, Join, ParJoin, Read, ReadExpect, ReadStorage, SystemData, World,
|
||||||
Write, WriteStorage,
|
Write, WriteStorage,
|
||||||
};
|
};
|
||||||
use std::f32::consts::PI;
|
use std::{f32::consts::PI, sync::Arc};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
struct AgentData<'a> {
|
struct AgentData<'a> {
|
||||||
@ -82,6 +82,7 @@ pub struct ReadData<'a> {
|
|||||||
mount_states: ReadStorage<'a, MountState>,
|
mount_states: ReadStorage<'a, MountState>,
|
||||||
time_of_day: Read<'a, TimeOfDay>,
|
time_of_day: Read<'a, TimeOfDay>,
|
||||||
light_emitter: ReadStorage<'a, LightEmitter>,
|
light_emitter: ReadStorage<'a, LightEmitter>,
|
||||||
|
world: ReadExpect<'a, Arc<world::World>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is 3.1 to last longer than the last damage timer (3.0 seconds)
|
// This is 3.1 to last longer than the last damage timer (3.0 seconds)
|
||||||
@ -213,7 +214,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
in_liquid: physics_state.in_liquid.is_some(),
|
in_liquid: physics_state.in_liquid.is_some(),
|
||||||
min_tgt_dist: 1.0,
|
min_tgt_dist: 1.0,
|
||||||
can_climb: body.map(|b| b.can_climb()).unwrap_or(false),
|
can_climb: body.map(|b| b.can_climb()).unwrap_or(false),
|
||||||
can_fly: body.map(|b| b.can_fly()).unwrap_or(false),
|
can_fly: body.map(|b| b.can_fly().is_some()).unwrap_or(false),
|
||||||
};
|
};
|
||||||
|
|
||||||
let flees = alignment
|
let flees = alignment
|
||||||
@ -619,19 +620,43 @@ impl<'a> AgentData<'a> {
|
|||||||
|
|
||||||
controller.inputs.move_z = bearing.z
|
controller.inputs.move_z = bearing.z
|
||||||
+ if self.traversal_config.can_fly {
|
+ if self.traversal_config.can_fly {
|
||||||
if read_data
|
let obstacle_ahead = read_data
|
||||||
.terrain
|
.terrain
|
||||||
.ray(
|
.ray(
|
||||||
self.pos.0 + Vec3::unit_z(),
|
self.pos.0 + Vec3::unit_z(),
|
||||||
self.pos.0
|
self.pos.0
|
||||||
+ bearing.try_normalized().unwrap_or_else(Vec3::unit_y) * 60.0
|
+ bearing.try_normalized().unwrap_or_else(Vec3::unit_y) * 80.0
|
||||||
+ Vec3::unit_z(),
|
+ Vec3::unit_z(),
|
||||||
)
|
)
|
||||||
.until(Block::is_solid)
|
.until(Block::is_solid)
|
||||||
.cast()
|
.cast()
|
||||||
.1
|
.1
|
||||||
.map_or(true, |b| b.is_some())
|
.map_or(true, |b| b.is_some());
|
||||||
{
|
let ground_too_close = self
|
||||||
|
.body
|
||||||
|
.map(|body| {
|
||||||
|
let height_approx = self.pos.0.y
|
||||||
|
- read_data
|
||||||
|
.world
|
||||||
|
.sim()
|
||||||
|
.get_alt_approx(self.pos.0.xy().map(|x: f32| x as i32))
|
||||||
|
.unwrap_or(0.0);
|
||||||
|
|
||||||
|
height_approx < body.flying_height()
|
||||||
|
|| read_data
|
||||||
|
.terrain
|
||||||
|
.ray(
|
||||||
|
self.pos.0,
|
||||||
|
self.pos.0 - body.flying_height() * Vec3::unit_z(),
|
||||||
|
)
|
||||||
|
.until(|b: &Block| b.is_solid() || b.is_liquid())
|
||||||
|
.cast()
|
||||||
|
.1
|
||||||
|
.map_or(false, |b| b.is_some())
|
||||||
|
})
|
||||||
|
.unwrap_or(false);
|
||||||
|
|
||||||
|
if obstacle_ahead || ground_too_close {
|
||||||
1.0 //fly up when approaching obstacles
|
1.0 //fly up when approaching obstacles
|
||||||
} else {
|
} else {
|
||||||
-0.1
|
-0.1
|
||||||
|
@ -249,9 +249,18 @@ impl<'a> System<'a> for Sys {
|
|||||||
{
|
{
|
||||||
let mut comp_sync_package = CompSyncPackage::new();
|
let mut comp_sync_package = CompSyncPackage::new();
|
||||||
let mut throttle = true;
|
let mut throttle = true;
|
||||||
|
|
||||||
|
// Extrapolation depends on receiving several frames indicating that something
|
||||||
|
// has stopped in order for the extrapolated value to have
|
||||||
|
// stopped
|
||||||
|
const SEND_UNCHANGED_PHYSICS_DATA: bool = true;
|
||||||
|
|
||||||
// TODO: An entity that stopped moving on a tick that it wasn't sent to the
|
// TODO: An entity that stopped moving on a tick that it wasn't sent to the
|
||||||
// player will never have its position updated
|
// player will never have its position updated
|
||||||
match last_pos.get(entity).map(|&l| l.0 != pos) {
|
match last_pos
|
||||||
|
.get(entity)
|
||||||
|
.map(|&l| l.0 != pos || SEND_UNCHANGED_PHYSICS_DATA)
|
||||||
|
{
|
||||||
Some(false) => {},
|
Some(false) => {},
|
||||||
Some(true) => {
|
Some(true) => {
|
||||||
let _ = last_pos.insert(entity, Last(pos));
|
let _ = last_pos.insert(entity, Last(pos));
|
||||||
@ -265,7 +274,10 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(&vel) = maybe_vel {
|
if let Some(&vel) = maybe_vel {
|
||||||
match last_vel.get(entity).map(|&l| l.0 != vel) {
|
match last_vel
|
||||||
|
.get(entity)
|
||||||
|
.map(|&l| l.0 != vel || SEND_UNCHANGED_PHYSICS_DATA)
|
||||||
|
{
|
||||||
Some(false) => {},
|
Some(false) => {},
|
||||||
Some(true) => {
|
Some(true) => {
|
||||||
let _ = last_vel.insert(entity, Last(vel));
|
let _ = last_vel.insert(entity, Last(vel));
|
||||||
@ -286,7 +298,10 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(&ori) = maybe_ori {
|
if let Some(&ori) = maybe_ori {
|
||||||
match last_ori.get(entity).map(|&l| l.0 != ori) {
|
match last_ori
|
||||||
|
.get(entity)
|
||||||
|
.map(|&l| l.0 != ori || SEND_UNCHANGED_PHYSICS_DATA)
|
||||||
|
{
|
||||||
Some(false) => {},
|
Some(false) => {},
|
||||||
Some(true) => {
|
Some(true) => {
|
||||||
let _ = last_ori.insert(entity, Last(ori));
|
let _ = last_ori.insert(entity, Last(ori));
|
||||||
|
@ -140,7 +140,7 @@ impl<'a> TrackedComps<'a> {
|
|||||||
self.mass.get(entity).copied().map(|c| comps.push(c.into()));
|
self.mass.get(entity).copied().map(|c| comps.push(c.into()));
|
||||||
self.collider
|
self.collider
|
||||||
.get(entity)
|
.get(entity)
|
||||||
.copied()
|
.cloned()
|
||||||
.map(|c| comps.push(c.into()));
|
.map(|c| comps.push(c.into()));
|
||||||
self.sticky
|
self.sticky
|
||||||
.get(entity)
|
.get(entity)
|
||||||
|
@ -54,6 +54,7 @@ pub mod object;
|
|||||||
pub mod quadruped_low;
|
pub mod quadruped_low;
|
||||||
pub mod quadruped_medium;
|
pub mod quadruped_medium;
|
||||||
pub mod quadruped_small;
|
pub mod quadruped_small;
|
||||||
|
pub mod ship;
|
||||||
pub mod theropod;
|
pub mod theropod;
|
||||||
pub mod vek;
|
pub mod vek;
|
||||||
|
|
||||||
|
33
voxygen/anim/src/ship/idle.rs
Normal file
33
voxygen/anim/src/ship/idle.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
use super::{
|
||||||
|
super::{vek::*, Animation},
|
||||||
|
ShipSkeleton, SkeletonAttr,
|
||||||
|
};
|
||||||
|
use common::comp::item::ToolKind;
|
||||||
|
|
||||||
|
pub struct IdleAnimation;
|
||||||
|
|
||||||
|
impl Animation for IdleAnimation {
|
||||||
|
type Dependency = (Option<ToolKind>, Option<ToolKind>, f32);
|
||||||
|
type Skeleton = ShipSkeleton;
|
||||||
|
|
||||||
|
#[cfg(feature = "use-dyn-lib")]
|
||||||
|
const UPDATE_FN: &'static [u8] = b"ship_idle\0";
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "be-dyn-lib", export_name = "ship_idle")]
|
||||||
|
#[allow(clippy::approx_constant)] // TODO: Pending review in #587
|
||||||
|
fn update_skeleton_inner(
|
||||||
|
skeleton: &Self::Skeleton,
|
||||||
|
(_active_tool_kind, _second_tool_kind, _global_time): Self::Dependency,
|
||||||
|
_anim_time: f32,
|
||||||
|
_rate: &mut f32,
|
||||||
|
s_a: &SkeletonAttr,
|
||||||
|
) -> Self::Skeleton {
|
||||||
|
let mut next = (*skeleton).clone();
|
||||||
|
|
||||||
|
next.bone0.position = Vec3::new(s_a.bone0.0, s_a.bone0.1, s_a.bone0.2) / 11.0;
|
||||||
|
|
||||||
|
next.bone1.position = Vec3::new(s_a.bone1.0, s_a.bone1.1, s_a.bone1.2) / 11.0;
|
||||||
|
|
||||||
|
next
|
||||||
|
}
|
||||||
|
}
|
68
voxygen/anim/src/ship/mod.rs
Normal file
68
voxygen/anim/src/ship/mod.rs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
pub mod idle;
|
||||||
|
|
||||||
|
// Reexports
|
||||||
|
pub use self::idle::IdleAnimation;
|
||||||
|
|
||||||
|
use super::{make_bone, vek::*, FigureBoneData, Skeleton};
|
||||||
|
use common::comp::{self};
|
||||||
|
use core::convert::TryFrom;
|
||||||
|
|
||||||
|
pub type Body = comp::ship::Body;
|
||||||
|
|
||||||
|
skeleton_impls!(struct ShipSkeleton {
|
||||||
|
+ bone0,
|
||||||
|
+ bone1,
|
||||||
|
});
|
||||||
|
|
||||||
|
impl Skeleton for ShipSkeleton {
|
||||||
|
type Attr = SkeletonAttr;
|
||||||
|
type Body = Body;
|
||||||
|
|
||||||
|
const BONE_COUNT: usize = 2;
|
||||||
|
#[cfg(feature = "use-dyn-lib")]
|
||||||
|
const COMPUTE_FN: &'static [u8] = b"ship_compute_mats\0";
|
||||||
|
|
||||||
|
#[cfg_attr(feature = "be-dyn-lib", export_name = "ship_compute_mats")]
|
||||||
|
fn compute_matrices_inner(
|
||||||
|
&self,
|
||||||
|
base_mat: Mat4<f32>,
|
||||||
|
buf: &mut [FigureBoneData; super::MAX_BONE_COUNT],
|
||||||
|
) -> Vec3<f32> {
|
||||||
|
let bone0_mat = base_mat * Mat4::<f32>::from(self.bone0);
|
||||||
|
|
||||||
|
*(<&mut [_; Self::BONE_COUNT]>::try_from(&mut buf[0..Self::BONE_COUNT]).unwrap()) = [
|
||||||
|
make_bone(bone0_mat * Mat4::scaling_3d(1.0 / 11.0)),
|
||||||
|
make_bone(Mat4::<f32>::from(self.bone1) * Mat4::scaling_3d(1.0 / 11.0)), /* Decorellated from ori */
|
||||||
|
];
|
||||||
|
Vec3::unit_z() * 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct SkeletonAttr {
|
||||||
|
bone0: (f32, f32, f32),
|
||||||
|
bone1: (f32, f32, f32),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> std::convert::TryFrom<&'a comp::Body> for SkeletonAttr {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(body: &'a comp::Body) -> Result<Self, Self::Error> {
|
||||||
|
match body {
|
||||||
|
comp::Body::Ship(body) => Ok(SkeletonAttr::from(body)),
|
||||||
|
_ => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SkeletonAttr {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
bone0: (0.0, 0.0, 0.0),
|
||||||
|
bone1: (0.0, 0.0, 0.0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Body> for SkeletonAttr {
|
||||||
|
fn from(_: &'a Body) -> Self { Self::default() }
|
||||||
|
}
|
@ -16,13 +16,17 @@ use common::{
|
|||||||
quadruped_low::{self, BodyType as QLBodyType, Species as QLSpecies},
|
quadruped_low::{self, BodyType as QLBodyType, Species as QLSpecies},
|
||||||
quadruped_medium::{self, BodyType as QMBodyType, Species as QMSpecies},
|
quadruped_medium::{self, BodyType as QMBodyType, Species as QMSpecies},
|
||||||
quadruped_small::{self, BodyType as QSBodyType, Species as QSSpecies},
|
quadruped_small::{self, BodyType as QSBodyType, Species as QSSpecies},
|
||||||
|
ship::{
|
||||||
|
self,
|
||||||
|
figuredata::{ShipCentralSubSpec, ShipSpec},
|
||||||
|
},
|
||||||
theropod::{self, BodyType as TBodyType, Species as TSpecies},
|
theropod::{self, BodyType as TBodyType, Species as TSpecies},
|
||||||
},
|
},
|
||||||
figure::{DynaUnionizer, MatSegment, Material, Segment},
|
figure::{DynaUnionizer, MatSegment, Material, Segment},
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::sync::Arc;
|
use std::{fmt, hash::Hash, sync::Arc};
|
||||||
use tracing::{error, warn};
|
use tracing::{error, warn};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
@ -34,6 +38,9 @@ fn load_segment(mesh_name: &str) -> Segment {
|
|||||||
}
|
}
|
||||||
fn graceful_load_vox(mesh_name: &str) -> AssetHandle<DotVoxAsset> {
|
fn graceful_load_vox(mesh_name: &str) -> AssetHandle<DotVoxAsset> {
|
||||||
let full_specifier: String = ["voxygen.voxel.", mesh_name].concat();
|
let full_specifier: String = ["voxygen.voxel.", mesh_name].concat();
|
||||||
|
graceful_load_vox_fullspec(&full_specifier)
|
||||||
|
}
|
||||||
|
fn graceful_load_vox_fullspec(full_specifier: &str) -> AssetHandle<DotVoxAsset> {
|
||||||
match DotVoxAsset::load(&full_specifier) {
|
match DotVoxAsset::load(&full_specifier) {
|
||||||
Ok(dot_vox) => dot_vox,
|
Ok(dot_vox) => dot_vox,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
@ -45,6 +52,9 @@ fn graceful_load_vox(mesh_name: &str) -> AssetHandle<DotVoxAsset> {
|
|||||||
fn graceful_load_segment(mesh_name: &str) -> Segment {
|
fn graceful_load_segment(mesh_name: &str) -> Segment {
|
||||||
Segment::from(&graceful_load_vox(mesh_name).read().0)
|
Segment::from(&graceful_load_vox(mesh_name).read().0)
|
||||||
}
|
}
|
||||||
|
fn graceful_load_segment_fullspec(full_specifier: &str) -> Segment {
|
||||||
|
Segment::from(&graceful_load_vox_fullspec(full_specifier).read().0)
|
||||||
|
}
|
||||||
fn graceful_load_segment_flipped(mesh_name: &str, flipped: bool) -> Segment {
|
fn graceful_load_segment_flipped(mesh_name: &str, flipped: bool) -> Segment {
|
||||||
Segment::from_vox(&graceful_load_vox(mesh_name).read().0, flipped)
|
Segment::from_vox(&graceful_load_vox(mesh_name).read().0, flipped)
|
||||||
}
|
}
|
||||||
@ -4171,3 +4181,53 @@ impl ObjectCentralSpec {
|
|||||||
(central, Vec3::from(spec.bone1.offset))
|
(central, Vec3::from(spec.bone1.offset))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn mesh_ship_bone<K: fmt::Debug + Eq + Hash, V, F: Fn(&V) -> &ShipCentralSubSpec>(
|
||||||
|
map: &HashMap<K, V>,
|
||||||
|
obj: &K,
|
||||||
|
f: F,
|
||||||
|
) -> BoneMeshes {
|
||||||
|
let spec = match map.get(&obj) {
|
||||||
|
Some(spec) => spec,
|
||||||
|
None => {
|
||||||
|
error!("No specification exists for {:?}", obj);
|
||||||
|
return load_mesh("not_found", Vec3::new(-5.0, -5.0, -2.5));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let bone = f(spec);
|
||||||
|
let central = graceful_load_segment_fullspec(&["server.voxel.", &bone.central.0].concat());
|
||||||
|
|
||||||
|
(central, Vec3::from(bone.offset))
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BodySpec for ship::Body {
|
||||||
|
type Spec = ShipSpec;
|
||||||
|
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
fn load_spec() -> Result<AssetHandle<Self::Spec>, assets::Error> { Self::Spec::load("") }
|
||||||
|
|
||||||
|
fn bone_meshes(
|
||||||
|
FigureKey { body, .. }: &FigureKey<Self>,
|
||||||
|
spec: &Self::Spec,
|
||||||
|
) -> [Option<BoneMeshes>; anim::MAX_BONE_COUNT] {
|
||||||
|
let map = &(spec.central.read().0).0;
|
||||||
|
[
|
||||||
|
Some(mesh_ship_bone(map, body, |spec| &spec.bone0)),
|
||||||
|
Some(mesh_ship_bone(map, body, |spec| &spec.bone1)),
|
||||||
|
Some(mesh_ship_bone(map, body, |spec| &spec.bone2)),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -199,6 +199,7 @@ impl Scene {
|
|||||||
&camera,
|
&camera,
|
||||||
&mut buf,
|
&mut buf,
|
||||||
None,
|
None,
|
||||||
|
Vec3::zero(),
|
||||||
);
|
);
|
||||||
(model, state)
|
(model, state)
|
||||||
}),
|
}),
|
||||||
@ -378,6 +379,7 @@ impl Scene {
|
|||||||
&self.camera,
|
&self.camera,
|
||||||
&mut buf,
|
&mut buf,
|
||||||
None,
|
None,
|
||||||
|
Vec3::zero(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1562,7 +1562,7 @@ fn under_cursor(
|
|||||||
let player_cylinder = Cylinder::from_components(
|
let player_cylinder = Cylinder::from_components(
|
||||||
player_pos,
|
player_pos,
|
||||||
scales.get(player_entity).copied(),
|
scales.get(player_entity).copied(),
|
||||||
colliders.get(player_entity).copied(),
|
colliders.get(player_entity),
|
||||||
char_states.get(player_entity),
|
char_states.get(player_entity),
|
||||||
);
|
);
|
||||||
let terrain = client.state().terrain();
|
let terrain = client.state().terrain();
|
||||||
@ -1643,7 +1643,7 @@ fn under_cursor(
|
|||||||
let target_cylinder = Cylinder::from_components(
|
let target_cylinder = Cylinder::from_components(
|
||||||
p,
|
p,
|
||||||
scales.get(*e).copied(),
|
scales.get(*e).copied(),
|
||||||
colliders.get(*e).copied(),
|
colliders.get(*e),
|
||||||
char_states.get(*e),
|
char_states.get(*e),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1706,7 +1706,7 @@ fn select_interactable(
|
|||||||
let player_cylinder = Cylinder::from_components(
|
let player_cylinder = Cylinder::from_components(
|
||||||
player_pos,
|
player_pos,
|
||||||
scales.get(player_entity).copied(),
|
scales.get(player_entity).copied(),
|
||||||
colliders.get(player_entity).copied(),
|
colliders.get(player_entity),
|
||||||
char_states.get(player_entity),
|
char_states.get(player_entity),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -1720,7 +1720,7 @@ fn select_interactable(
|
|||||||
.join()
|
.join()
|
||||||
.filter(|(e, _, _, _, _)| *e != player_entity)
|
.filter(|(e, _, _, _, _)| *e != player_entity)
|
||||||
.map(|(e, p, s, c, cs)| {
|
.map(|(e, p, s, c, cs)| {
|
||||||
let cylinder = Cylinder::from_components(p.0, s.copied(), c.copied(), cs);
|
let cylinder = Cylinder::from_components(p.0, s.copied(), c, cs);
|
||||||
(e, cylinder)
|
(e, cylinder)
|
||||||
})
|
})
|
||||||
// Roughly filter out entities farther than interaction distance
|
// Roughly filter out entities farther than interaction distance
|
||||||
|
Loading…
Reference in New Issue
Block a user