mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Draft of airships (spawn command, visuals, some physics refactoring, no collision yet).
This commit is contained in:
parent
5570b57282
commit
8b9e84972a
@ -969,6 +969,15 @@
|
||||
),
|
||||
species: ()
|
||||
),
|
||||
ship: (
|
||||
body: (
|
||||
keyword: "ship",
|
||||
names_0: [
|
||||
"Boaty McBoatface",
|
||||
],
|
||||
),
|
||||
species: (),
|
||||
),
|
||||
biped_small: (
|
||||
body: (
|
||||
keyword: "biped_small",
|
||||
|
16
assets/server/manifests/ship_manifest.ron
Normal file
16
assets/server/manifests/ship_manifest.ron
Normal file
@ -0,0 +1,16 @@
|
||||
({
|
||||
DefaultAirship: (
|
||||
bone0: (
|
||||
offset: (-20.0, -35.0, 1.0),
|
||||
central: ("object.Human_Airship"),
|
||||
),
|
||||
bone1: (
|
||||
offset: (0.0, 0.0, 0.0),
|
||||
central: ("propeller-l"),
|
||||
),
|
||||
bone2: (
|
||||
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.
BIN
assets/voxygen/voxel/object/Human_Airship.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/object/Human_Airship.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
1
assets/voxygen/voxel/object/airship.vox
Symbolic link
1
assets/voxygen/voxel/object/airship.vox
Symbolic link
@ -0,0 +1 @@
|
||||
../../../server/voxel/airship.vox
|
1
assets/voxygen/voxel/object/propeller-l.vox
Symbolic link
1
assets/voxygen/voxel/object/propeller-l.vox
Symbolic link
@ -0,0 +1 @@
|
||||
../../../server/voxel/propeller-l.vox
|
1
assets/voxygen/voxel/object/propeller-r.vox
Symbolic link
1
assets/voxygen/voxel/object/propeller-r.vox
Symbolic link
@ -0,0 +1 @@
|
||||
../../../server/voxel/propeller-r.vox
|
@ -36,6 +36,7 @@ impl ChatCommandData {
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ChatCommand {
|
||||
Adminify,
|
||||
Airship,
|
||||
Alias,
|
||||
Ban,
|
||||
Build,
|
||||
@ -89,6 +90,7 @@ pub enum ChatCommand {
|
||||
// Thank you for keeping this sorted alphabetically :-)
|
||||
pub static CHAT_COMMANDS: &[ChatCommand] = &[
|
||||
ChatCommand::Adminify,
|
||||
ChatCommand::Airship,
|
||||
ChatCommand::Alias,
|
||||
ChatCommand::Ban,
|
||||
ChatCommand::Build,
|
||||
@ -222,6 +224,7 @@ impl ChatCommand {
|
||||
"Temporarily gives a player admin permissions or removes them",
|
||||
Admin,
|
||||
),
|
||||
ChatCommand::Airship => cmd(vec![], "Spawns an airship", Admin),
|
||||
ChatCommand::Alias => cmd(vec![Any("name", Required)], "Change your alias", NoAdmin),
|
||||
ChatCommand::Ban => cmd(
|
||||
vec![Any("username", Required), Message(Optional)],
|
||||
@ -449,6 +452,7 @@ impl ChatCommand {
|
||||
pub fn keyword(&self) -> &'static str {
|
||||
match self {
|
||||
ChatCommand::Adminify => "adminify",
|
||||
ChatCommand::Airship => "airship",
|
||||
ChatCommand::Alias => "alias",
|
||||
ChatCommand::Ban => "ban",
|
||||
ChatCommand::Build => "build",
|
||||
|
@ -168,6 +168,7 @@ impl<'a> From<&'a Body> for Psyche {
|
||||
Body::Golem(_) => 1.0,
|
||||
Body::Theropod(_) => 1.0,
|
||||
Body::Dragon(_) => 1.0,
|
||||
Body::Ship(_) => 1.0,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ pub mod object;
|
||||
pub mod quadruped_low;
|
||||
pub mod quadruped_medium;
|
||||
pub mod quadruped_small;
|
||||
pub mod ship;
|
||||
pub mod theropod;
|
||||
|
||||
use crate::{
|
||||
@ -44,6 +45,7 @@ make_case_elim!(
|
||||
Golem(body: golem::Body) = 11,
|
||||
Theropod(body: theropod::Body) = 12,
|
||||
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 theropod: BodyData<BodyMeta, theropod::AllSpecies<SpeciesMeta>>,
|
||||
pub quadruped_low: BodyData<BodyMeta, quadruped_low::AllSpecies<SpeciesMeta>>,
|
||||
pub ship: BodyData<BodyMeta, ()>,
|
||||
}
|
||||
|
||||
/// 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::Theropod(_) => &self.theropod.body,
|
||||
Body::QuadrupedLow(_) => &self.quadruped_low.body,
|
||||
Body::Ship(_) => &self.ship.body,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -218,6 +222,7 @@ impl Body {
|
||||
Body::Golem(_) => 2.5,
|
||||
Body::BipedSmall(_) => 0.75,
|
||||
Body::Object(_) => 0.4,
|
||||
Body::Ship(_) => 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,6 +299,7 @@ impl Body {
|
||||
object::Body::Crossbow => 1.7,
|
||||
_ => 1.0,
|
||||
},
|
||||
Body::Ship(_) => 1.0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -416,6 +422,7 @@ impl Body {
|
||||
quadruped_low::Species::Deadwood => 600,
|
||||
_ => 200,
|
||||
},
|
||||
Body::Ship(_) => 10000,
|
||||
}
|
||||
}
|
||||
|
||||
@ -508,12 +515,13 @@ impl Body {
|
||||
quadruped_low::Species::Deadwood => 30,
|
||||
_ => 20,
|
||||
},
|
||||
Body::Ship(_) => 500,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn immune_to(&self, buff: BuffKind) -> bool {
|
||||
match buff {
|
||||
BuffKind::Bleeding => matches!(self, Body::Object(_) | Body::Golem(_)),
|
||||
BuffKind::Bleeding => matches!(self, Body::Object(_) | Body::Golem(_) | Body::Ship(_)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -521,7 +529,7 @@ impl Body {
|
||||
/// Returns a multiplier representing increased difficulty not accounted for
|
||||
/// due to AI or not using an actual weapon
|
||||
// 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 {
|
||||
match self {
|
||||
|
69
common/src/comp/body/ship.rs
Normal file
69
common/src/comp/body/ship.rs
Normal file
@ -0,0 +1,69 @@
|
||||
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_id(&self) -> &'static str {
|
||||
match self {
|
||||
Body::DefaultAirship => "server.manifests.ship_manifest",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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},
|
||||
volumes::dyna::Dyna,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use hashbrown::HashMap;
|
||||
|
||||
#[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 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>>,
|
||||
}
|
||||
|
||||
impl assets::Compound for ShipSpec {
|
||||
fn load<S: assets::source::Source>(_: &assets::AssetCache<S>, _: &str) -> Result<Self, assets::Error> {
|
||||
Ok(ShipSpec {
|
||||
central: AssetExt::load("server.manifests.ship_manifest")?
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
@ -48,7 +48,7 @@ pub use self::{
|
||||
beam::{Beam, BeamSegment},
|
||||
body::{
|
||||
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, theropod, ship, AllBodies,
|
||||
Body, BodyData,
|
||||
},
|
||||
buff::{
|
||||
|
@ -55,9 +55,12 @@ impl Component for Mass {
|
||||
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
||||
}
|
||||
|
||||
// Mass
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
// Collider
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
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 },
|
||||
Point,
|
||||
}
|
||||
@ -65,6 +68,7 @@ pub enum Collider {
|
||||
impl Collider {
|
||||
pub fn get_radius(&self) -> f32 {
|
||||
match self {
|
||||
Collider::Voxel { .. } => 0.0,
|
||||
Collider::Box { radius, .. } => *radius,
|
||||
Collider::Point => 0.0,
|
||||
}
|
||||
@ -72,6 +76,7 @@ impl Collider {
|
||||
|
||||
pub fn get_z_limits(&self, modifier: f32) -> (f32, f32) {
|
||||
match self {
|
||||
Collider::Voxel { .. } => (0.0, 0.0),
|
||||
Collider::Box { z_min, z_max, .. } => (*z_min * modifier, *z_max * modifier),
|
||||
Collider::Point => (0.0, 0.0),
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use crate::{
|
||||
item::{Hands, ItemKind, Tool, ToolKind},
|
||||
quadruped_low, quadruped_medium, quadruped_small,
|
||||
skills::Skill,
|
||||
theropod, Body, CharacterAbility, CharacterState, InputKind, InventoryAction, StateUpdate,
|
||||
theropod, ship, Body, CharacterAbility, CharacterState, InputKind, InventoryAction, StateUpdate,
|
||||
},
|
||||
consts::{FRIC_GROUND, GRAVITY},
|
||||
event::{LocalEvent, ServerEvent},
|
||||
@ -117,6 +117,7 @@ impl Body {
|
||||
quadruped_low::Species::Basilisk => 120.0,
|
||||
quadruped_low::Species::Deadwood => 140.0,
|
||||
},
|
||||
Body::Ship(_) => 30.0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,13 +169,14 @@ impl Body {
|
||||
quadruped_low::Species::Lavadrake => 4.0,
|
||||
_ => 6.0,
|
||||
},
|
||||
Body::Ship(_) => 10.0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn can_fly(&self) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Body::BirdMedium(_) | Body::Dragon(_) | Body::BirdSmall(_)
|
||||
Body::BirdMedium(_) | Body::Dragon(_) | Body::BirdSmall(_) | Body::Ship(ship::Body::DefaultAirship)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,7 @@ impl Cylinder {
|
||||
char_state: Option<&crate::comp::CharacterState>,
|
||||
) -> Self {
|
||||
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
|
||||
.filter(|char_state| char_state.is_dodge())
|
||||
.map_or(1.0, |_| 0.5)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -77,6 +77,7 @@ type CommandHandler = fn(&mut Server, EcsEntity, EcsEntity, String, &ChatCommand
|
||||
fn get_handler(cmd: &ChatCommand) -> CommandHandler {
|
||||
match cmd {
|
||||
ChatCommand::Adminify => handle_adminify,
|
||||
ChatCommand::Airship => handle_spawn_airship,
|
||||
ChatCommand::Alias => handle_alias,
|
||||
ChatCommand::Ban => handle_ban,
|
||||
ChatCommand::Build => handle_build,
|
||||
@ -984,6 +985,39 @@ fn handle_spawn_training_dummy(
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_spawn_airship(
|
||||
server: &mut Server,
|
||||
client: EcsEntity,
|
||||
target: EcsEntity,
|
||||
_args: String,
|
||||
_action: &ChatCommand,
|
||||
) {
|
||||
match server.state.read_component_copied::<comp::Pos>(target) {
|
||||
Some(pos) => {
|
||||
server
|
||||
.state
|
||||
.create_ship(pos, comp::ship::Body::DefaultAirship)
|
||||
.with(comp::Scale(50.0))
|
||||
.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(
|
||||
server: &mut Server,
|
||||
client: EcsEntity,
|
||||
|
@ -69,7 +69,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv
|
||||
find_dist::Cylinder::from_components(
|
||||
p.0,
|
||||
scales.get(entity).copied(),
|
||||
colliders.get(entity).copied(),
|
||||
colliders.get(entity).cloned(),
|
||||
char_states.get(entity),
|
||||
)
|
||||
})
|
||||
|
@ -41,6 +41,7 @@ pub trait StateExt {
|
||||
) -> EcsEntityBuilder;
|
||||
/// Build a static object entity
|
||||
fn create_object(&mut self, pos: comp::Pos, object: comp::object::Body) -> EcsEntityBuilder;
|
||||
fn create_ship(&mut self, pos: comp::Pos, object: comp::ship::Body) -> EcsEntityBuilder;
|
||||
/// Build a projectile
|
||||
fn create_projectile(
|
||||
&mut self,
|
||||
@ -215,6 +216,18 @@ impl StateExt for State {
|
||||
.with(comp::Gravity(1.0))
|
||||
}
|
||||
|
||||
fn create_ship(&mut self, pos: comp::Pos, object: comp::ship::Body) -> EcsEntityBuilder {
|
||||
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: object.manifest_id().to_string() })
|
||||
.with(comp::Body::Ship(object))
|
||||
.with(comp::Gravity(1.0))
|
||||
}
|
||||
|
||||
fn create_projectile(
|
||||
&mut self,
|
||||
pos: comp::Pos,
|
||||
|
@ -140,7 +140,7 @@ impl<'a> TrackedComps<'a> {
|
||||
self.mass.get(entity).copied().map(|c| comps.push(c.into()));
|
||||
self.collider
|
||||
.get(entity)
|
||||
.copied()
|
||||
.cloned()
|
||||
.map(|c| comps.push(c.into()));
|
||||
self.sticky
|
||||
.get(entity)
|
||||
|
@ -51,6 +51,7 @@ pub mod fish_small;
|
||||
pub mod fixture;
|
||||
pub mod golem;
|
||||
pub mod object;
|
||||
pub mod ship;
|
||||
pub mod quadruped_low;
|
||||
pub mod quadruped_medium;
|
||||
pub mod quadruped_small;
|
||||
|
34
voxygen/anim/src/ship/idle.rs
Normal file
34
voxygen/anim/src/ship/idle.rs
Normal file
@ -0,0 +1,34 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
|
71
voxygen/anim/src/ship/mod.rs
Normal file
71
voxygen/anim/src/ship/mod.rs
Normal file
@ -0,0 +1,71 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -1972,7 +1972,7 @@ fn create_pipelines(
|
||||
&shaders.figure_vert.read().0,
|
||||
&shaders.figure_frag.read().0,
|
||||
&include_ctx,
|
||||
gfx::state::CullFace::Back,
|
||||
gfx::state::CullFace::Nothing,
|
||||
)?;
|
||||
|
||||
// Construct a pipeline for rendering terrain
|
||||
|
@ -13,6 +13,7 @@ use common::{
|
||||
humanoid::{self, Body, BodyType, EyeColor, Skin, Species},
|
||||
item::{ItemDef, ModularComponentKind},
|
||||
object,
|
||||
ship::{self, figuredata::{ShipSpec, ShipCentralSubSpec}},
|
||||
quadruped_low::{self, BodyType as QLBodyType, Species as QLSpecies},
|
||||
quadruped_medium::{self, BodyType as QMBodyType, Species as QMSpecies},
|
||||
quadruped_small::{self, BodyType as QSBodyType, Species as QSSpecies},
|
||||
@ -22,7 +23,7 @@ use common::{
|
||||
};
|
||||
use hashbrown::HashMap;
|
||||
use serde::Deserialize;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, hash::Hash, sync::Arc};
|
||||
use tracing::{error, warn};
|
||||
use vek::*;
|
||||
|
||||
@ -4102,6 +4103,17 @@ impl QuadrupedLowLateralSpec {
|
||||
#[derive(Deserialize)]
|
||||
struct ObjectCentralSpec(HashMap<object::Body, SidedObjectCentralVoxSpec>);
|
||||
|
||||
/*
|
||||
#[derive(Deserialize)]
|
||||
struct ShipCentralSpec(HashMap<ship::Body, SidedShipCentralVoxSpec>);
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SidedShipCentralVoxSpec {
|
||||
bone0: ObjectCentralSubSpec,
|
||||
bone1: ObjectCentralSubSpec,
|
||||
bone2: ObjectCentralSubSpec,
|
||||
}*/
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SidedObjectCentralVoxSpec {
|
||||
bone0: ObjectCentralSubSpec,
|
||||
@ -4171,3 +4183,99 @@ impl ObjectCentralSpec {
|
||||
(central, Vec3::from(spec.bone1.offset))
|
||||
}
|
||||
}
|
||||
|
||||
/*make_vox_spec!(
|
||||
ship::Body,
|
||||
struct ShipSpec {
|
||||
central: ShipCentralSpec = "server.manifests.ship_manifest",
|
||||
},
|
||||
|FigureKey { body, .. }, spec| {
|
||||
[
|
||||
Some(spec.central.read().0.mesh_bone(
|
||||
body, |spec| &spec.bone0,
|
||||
)),
|
||||
Some(spec.central.read().0.mesh_bone(
|
||||
body, |spec| &spec.bone1
|
||||
)),
|
||||
Some(spec.central.read().0.mesh_bone(
|
||||
body, |spec| &spec.bone2
|
||||
)),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
]
|
||||
},
|
||||
);
|
||||
|
||||
impl ShipCentralSpec {
|
||||
fn mesh_bone<F: Fn(&SidedShipCentralVoxSpec) -> &ObjectCentralSubSpec>(&self, obj: &ship::Body, f: F) -> BoneMeshes {
|
||||
let spec = match self.0.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(&bone.central.0);
|
||||
|
||||
(central, Vec3::from(bone.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(&bone.central.0);
|
||||
|
||||
(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,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ use anim::{
|
||||
biped_large::BipedLargeSkeleton, biped_small::BipedSmallSkeleton,
|
||||
bird_medium::BirdMediumSkeleton, bird_small::BirdSmallSkeleton, character::CharacterSkeleton,
|
||||
dragon::DragonSkeleton, fish_medium::FishMediumSkeleton, fish_small::FishSmallSkeleton,
|
||||
golem::GolemSkeleton, object::ObjectSkeleton, quadruped_low::QuadrupedLowSkeleton,
|
||||
golem::GolemSkeleton, object::ObjectSkeleton, ship::ShipSkeleton, quadruped_low::QuadrupedLowSkeleton,
|
||||
quadruped_medium::QuadrupedMediumSkeleton, quadruped_small::QuadrupedSmallSkeleton,
|
||||
theropod::TheropodSkeleton, Animation, Skeleton,
|
||||
};
|
||||
@ -101,6 +101,7 @@ struct FigureMgrStates {
|
||||
biped_small_states: HashMap<EcsEntity, FigureState<BipedSmallSkeleton>>,
|
||||
golem_states: HashMap<EcsEntity, FigureState<GolemSkeleton>>,
|
||||
object_states: HashMap<EcsEntity, FigureState<ObjectSkeleton>>,
|
||||
ship_states: HashMap<EcsEntity, FigureState<ShipSkeleton>>,
|
||||
}
|
||||
|
||||
impl FigureMgrStates {
|
||||
@ -120,6 +121,7 @@ impl FigureMgrStates {
|
||||
biped_small_states: HashMap::new(),
|
||||
golem_states: HashMap::new(),
|
||||
object_states: HashMap::new(),
|
||||
ship_states: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,6 +182,7 @@ impl FigureMgrStates {
|
||||
.map(DerefMut::deref_mut),
|
||||
Body::Golem(_) => self.golem_states.get_mut(&entity).map(DerefMut::deref_mut),
|
||||
Body::Object(_) => self.object_states.get_mut(&entity).map(DerefMut::deref_mut),
|
||||
Body::Ship(_) => self.ship_states.get_mut(&entity).map(DerefMut::deref_mut),
|
||||
}
|
||||
}
|
||||
|
||||
@ -205,6 +208,7 @@ impl FigureMgrStates {
|
||||
Body::BipedSmall(_) => self.biped_small_states.remove(&entity).map(|e| e.meta),
|
||||
Body::Golem(_) => self.golem_states.remove(&entity).map(|e| e.meta),
|
||||
Body::Object(_) => self.object_states.remove(&entity).map(|e| e.meta),
|
||||
Body::Ship(_) => self.ship_states.remove(&entity).map(|e| e.meta),
|
||||
}
|
||||
}
|
||||
|
||||
@ -224,6 +228,7 @@ impl FigureMgrStates {
|
||||
self.biped_small_states.retain(|k, v| f(k, &mut *v));
|
||||
self.golem_states.retain(|k, v| f(k, &mut *v));
|
||||
self.object_states.retain(|k, v| f(k, &mut *v));
|
||||
self.ship_states.retain(|k, v| f(k, &mut *v));
|
||||
}
|
||||
|
||||
fn count(&self) -> usize {
|
||||
@ -242,6 +247,7 @@ impl FigureMgrStates {
|
||||
+ self.biped_small_states.len()
|
||||
+ self.golem_states.len()
|
||||
+ self.object_states.len()
|
||||
+ self.ship_states.len()
|
||||
}
|
||||
|
||||
fn count_visible(&self) -> usize {
|
||||
@ -314,6 +320,11 @@ impl FigureMgrStates {
|
||||
.iter()
|
||||
.filter(|(_, c)| c.visible())
|
||||
.count()
|
||||
+ self
|
||||
.ship_states
|
||||
.iter()
|
||||
.filter(|(_, c)| c.visible())
|
||||
.count()
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,6 +343,7 @@ pub struct FigureMgr {
|
||||
biped_large_model_cache: FigureModelCache<BipedLargeSkeleton>,
|
||||
biped_small_model_cache: FigureModelCache<BipedSmallSkeleton>,
|
||||
object_model_cache: FigureModelCache<ObjectSkeleton>,
|
||||
ship_model_cache: FigureModelCache<ShipSkeleton>,
|
||||
golem_model_cache: FigureModelCache<GolemSkeleton>,
|
||||
states: FigureMgrStates,
|
||||
}
|
||||
@ -353,6 +365,7 @@ impl FigureMgr {
|
||||
biped_large_model_cache: FigureModelCache::new(),
|
||||
biped_small_model_cache: FigureModelCache::new(),
|
||||
object_model_cache: FigureModelCache::new(),
|
||||
ship_model_cache: FigureModelCache::new(),
|
||||
golem_model_cache: FigureModelCache::new(),
|
||||
states: FigureMgrStates::default(),
|
||||
}
|
||||
@ -384,6 +397,7 @@ impl FigureMgr {
|
||||
self.biped_small_model_cache
|
||||
.clean(&mut self.col_lights, tick);
|
||||
self.object_model_cache.clean(&mut self.col_lights, tick);
|
||||
self.ship_model_cache.clean(&mut self.col_lights, tick);
|
||||
self.golem_model_cache.clean(&mut self.col_lights, tick);
|
||||
}
|
||||
|
||||
@ -4088,6 +4102,79 @@ impl FigureMgr {
|
||||
_ => target_base,
|
||||
};
|
||||
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
pos.0,
|
||||
ori,
|
||||
scale,
|
||||
col,
|
||||
dt,
|
||||
state_animation_rate,
|
||||
model,
|
||||
lpindex,
|
||||
true,
|
||||
is_player,
|
||||
camera,
|
||||
&mut update_buf,
|
||||
terrain,
|
||||
);
|
||||
},
|
||||
Body::Ship(body) => {
|
||||
let (model, skeleton_attr) = self.ship_model_cache.get_or_create_model(
|
||||
renderer,
|
||||
&mut self.col_lights,
|
||||
*body,
|
||||
inventory,
|
||||
tick,
|
||||
player_camera_mode,
|
||||
player_character_state,
|
||||
scene_data.runtime,
|
||||
);
|
||||
|
||||
let state =
|
||||
self.states.ship_states.entry(entity).or_insert_with(|| {
|
||||
FigureState::new(renderer, ShipSkeleton::default())
|
||||
});
|
||||
|
||||
let (character, last_character) = match (character, last_character) {
|
||||
(Some(c), Some(l)) => (c, l),
|
||||
_ => (&CharacterState::Idle, &Last {
|
||||
0: CharacterState::Idle,
|
||||
}),
|
||||
};
|
||||
|
||||
if !character.same_variant(&last_character.0) {
|
||||
state.state_time = 0.0;
|
||||
}
|
||||
|
||||
let target_base = match (
|
||||
physics.on_ground,
|
||||
vel.0.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||
physics.in_liquid.is_some(), // In water
|
||||
) {
|
||||
// Standing
|
||||
(true, false, false) => anim::ship::IdleAnimation::update_skeleton(
|
||||
&ShipSkeleton::default(),
|
||||
(active_tool_kind, second_tool_kind, time),
|
||||
state.state_time,
|
||||
&mut state_animation_rate,
|
||||
skeleton_attr,
|
||||
),
|
||||
_ => anim::ship::IdleAnimation::update_skeleton(
|
||||
&ShipSkeleton::default(),
|
||||
(active_tool_kind, second_tool_kind, time),
|
||||
state.state_time,
|
||||
&mut state_animation_rate,
|
||||
skeleton_attr,
|
||||
),
|
||||
};
|
||||
|
||||
let target_bones = match &character {
|
||||
// TODO!
|
||||
_ => target_base,
|
||||
};
|
||||
|
||||
state.skeleton = anim::vek::Lerp::lerp(&state.skeleton, &target_bones, dt_lerp);
|
||||
state.update(
|
||||
renderer,
|
||||
@ -4313,6 +4400,7 @@ impl FigureMgr {
|
||||
biped_large_model_cache,
|
||||
biped_small_model_cache,
|
||||
object_model_cache,
|
||||
ship_model_cache,
|
||||
golem_model_cache,
|
||||
states:
|
||||
FigureMgrStates {
|
||||
@ -4330,6 +4418,7 @@ impl FigureMgr {
|
||||
biped_small_states,
|
||||
golem_states,
|
||||
object_states,
|
||||
ship_states,
|
||||
},
|
||||
} = self;
|
||||
let col_lights = &*col_lights_;
|
||||
@ -4572,6 +4661,23 @@ impl FigureMgr {
|
||||
),
|
||||
)
|
||||
}),
|
||||
Body::Ship(body) => ship_states
|
||||
.get(&entity)
|
||||
.filter(|state| filter_state(&*state))
|
||||
.map(move |state| {
|
||||
(
|
||||
state.locals(),
|
||||
state.bone_consts(),
|
||||
ship_model_cache.get_model(
|
||||
col_lights,
|
||||
*body,
|
||||
inventory,
|
||||
tick,
|
||||
player_camera_mode,
|
||||
character_state,
|
||||
),
|
||||
)
|
||||
}),
|
||||
} {
|
||||
let model_entry = model_entry?;
|
||||
|
||||
|
@ -1562,7 +1562,7 @@ fn under_cursor(
|
||||
let player_cylinder = Cylinder::from_components(
|
||||
player_pos,
|
||||
scales.get(player_entity).copied(),
|
||||
colliders.get(player_entity).copied(),
|
||||
colliders.get(player_entity).cloned(),
|
||||
char_states.get(player_entity),
|
||||
);
|
||||
let terrain = client.state().terrain();
|
||||
@ -1643,7 +1643,7 @@ fn under_cursor(
|
||||
let target_cylinder = Cylinder::from_components(
|
||||
p,
|
||||
scales.get(*e).copied(),
|
||||
colliders.get(*e).copied(),
|
||||
colliders.get(*e).cloned(),
|
||||
char_states.get(*e),
|
||||
);
|
||||
|
||||
@ -1706,7 +1706,7 @@ fn select_interactable(
|
||||
let player_cylinder = Cylinder::from_components(
|
||||
player_pos,
|
||||
scales.get(player_entity).copied(),
|
||||
colliders.get(player_entity).copied(),
|
||||
colliders.get(player_entity).cloned(),
|
||||
char_states.get(player_entity),
|
||||
);
|
||||
|
||||
@ -1720,7 +1720,7 @@ fn select_interactable(
|
||||
.join()
|
||||
.filter(|(e, _, _, _, _)| *e != player_entity)
|
||||
.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.cloned(), cs);
|
||||
(e, cylinder)
|
||||
})
|
||||
// Roughly filter out entities farther than interaction distance
|
||||
|
Loading…
Reference in New Issue
Block a user