Draft of airships (spawn command, visuals, some physics refactoring, no collision yet).

This commit is contained in:
Avi Weinstock 2021-03-11 11:48:59 -05:00
parent 5570b57282
commit 8b9e84972a
30 changed files with 1211 additions and 552 deletions

View File

@ -969,6 +969,15 @@
),
species: ()
),
ship: (
body: (
keyword: "ship",
names_0: [
"Boaty McBoatface",
],
),
species: (),
),
biped_small: (
body: (
keyword: "biped_small",

View 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

Binary file not shown.

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

Binary file not shown.

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

Binary file not shown.

View File

@ -0,0 +1 @@
../../../server/voxel/airship.vox

View File

@ -0,0 +1 @@
../../../server/voxel/propeller-l.vox

View File

@ -0,0 +1 @@
../../../server/voxel/propeller-r.vox

View File

@ -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",

View File

@ -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,
},
}
}

View File

@ -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 {

View 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")?
})
}
}
}

View File

@ -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::{

View File

@ -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),
}

View File

@ -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)
)
}

View File

@ -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

View File

@ -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,

View File

@ -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),
)
})

View File

@ -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,

View File

@ -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)

View File

@ -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;

View 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
}
}

View 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()
}
}

View File

@ -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

View File

@ -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,
]
}
}

View File

@ -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?;

View File

@ -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