mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Basic fluid dynamics and physical properties for entities
This commit is contained in:
parent
bcf9a8089f
commit
762c68cfbb
14
CHANGELOG.md
14
CHANGELOG.md
@ -38,7 +38,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Sort inventory button
|
- Sort inventory button
|
||||||
- Option to change the master volume when window is unfocused
|
- Option to change the master volume when window is unfocused
|
||||||
- Crafting stations in towns
|
- Crafting stations in towns
|
||||||
|
- Option to change the master volume when window is unfocused
|
||||||
|
- Entities now have mass
|
||||||
|
- Entities now have density
|
||||||
|
- Buoyancy is calculated from the difference in density between an entity and surrounding fluid
|
||||||
|
- Drag is now calculated based on physical properties
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
@ -64,10 +68,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Certain uses of client-authoritative physics now subject the player to server-authoritative physics.
|
- Certain uses of client-authoritative physics now subject the player to server-authoritative physics.
|
||||||
- Dodge roll iframes and staff explosion are now unlocked by default, with points refunded for existing characters.
|
- Dodge roll iframes and staff explosion are now unlocked by default, with points refunded for existing characters.
|
||||||
- Dash melee now stops after hitting something. Infinite dash also now replaced with dash through.
|
- Dash melee now stops after hitting something. Infinite dash also now replaced with dash through.
|
||||||
|
- Collisions, knockbacks, jumping and drag are now physical forces applied to the entity's body mass
|
||||||
|
- Turning rate has been made more consistent across angles
|
||||||
|
- Gravity has been lowered so that physics can work more reasonably
|
||||||
|
- Jump has been decreased in height but extended in length as a result of the new gravity
|
||||||
|
- Fall damage has been adjusted with the new gravity in mind
|
||||||
|
- Projectiles now generally have a different arc because they no longer have their own gravity modifier
|
||||||
|
|
||||||
### Removed
|
### Removed
|
||||||
|
|
||||||
- Removed command: "debug", use "/kit debug" instead
|
- Removed command: "debug", use "/kit debug" instead
|
||||||
|
- Gravity component has been removed
|
||||||
|
- In-air movement has been removed
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
@ -9,6 +9,6 @@ LeapMelee(
|
|||||||
knockback: 12.0,
|
knockback: 12.0,
|
||||||
range: 4.5,
|
range: 4.5,
|
||||||
max_angle: 30.0,
|
max_angle: 30.0,
|
||||||
forward_leap_strength: 28.0,
|
forward_leap_strength: 20.0,
|
||||||
vertical_leap_strength: 8.0,
|
vertical_leap_strength: 8.0,
|
||||||
)
|
)
|
||||||
|
@ -9,6 +9,5 @@ BasicRanged(
|
|||||||
),
|
),
|
||||||
projectile_body: Object(Arrow),
|
projectile_body: Object(Arrow),
|
||||||
projectile_light: None,
|
projectile_light: None,
|
||||||
projectile_gravity: Some(Gravity(0.2)),
|
projectile_speed: 60.0,
|
||||||
projectile_speed: 100.0,
|
|
||||||
)
|
)
|
||||||
|
@ -11,8 +11,7 @@ ChargedRanged(
|
|||||||
recover_duration: 0.5,
|
recover_duration: 0.5,
|
||||||
projectile_body: Object(MultiArrow),
|
projectile_body: Object(MultiArrow),
|
||||||
projectile_light: None,
|
projectile_light: None,
|
||||||
projectile_gravity: Some(Gravity(0.2)),
|
initial_projectile_speed: 60.0,
|
||||||
initial_projectile_speed: 100.0,
|
scaled_projectile_speed: 90.0,
|
||||||
scaled_projectile_speed: 400.0,
|
|
||||||
move_speed: 0.3,
|
move_speed: 0.3,
|
||||||
)
|
)
|
||||||
|
@ -12,7 +12,6 @@ RepeaterRanged(
|
|||||||
),
|
),
|
||||||
projectile_body: Object(Arrow),
|
projectile_body: Object(Arrow),
|
||||||
projectile_light: None,
|
projectile_light: None,
|
||||||
projectile_gravity: Some(Gravity(0.2)),
|
projectile_speed: 60.0,
|
||||||
projectile_speed: 100.0,
|
|
||||||
reps_remaining: 3,
|
reps_remaining: 3,
|
||||||
)
|
)
|
||||||
|
@ -9,6 +9,5 @@ BasicRanged(
|
|||||||
),
|
),
|
||||||
projectile_body: Object(Arrow),
|
projectile_body: Object(Arrow),
|
||||||
projectile_light: None,
|
projectile_light: None,
|
||||||
projectile_gravity: Some(Gravity(0.2)),
|
|
||||||
projectile_speed: 100.0,
|
projectile_speed: 100.0,
|
||||||
)
|
)
|
||||||
|
@ -8,6 +8,5 @@ BasicRanged(
|
|||||||
col: (0.0, 1.0, 0.33).into(),
|
col: (0.0, 1.0, 0.33).into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),*/
|
}),*/
|
||||||
projectile_gravity: None,
|
|
||||||
projectile_speed: 100.0,
|
projectile_speed: 100.0,
|
||||||
)
|
)
|
@ -9,6 +9,6 @@ LeapMelee(
|
|||||||
knockback: 25.0,
|
knockback: 25.0,
|
||||||
range: 4.5,
|
range: 4.5,
|
||||||
max_angle: 360.0,
|
max_angle: 360.0,
|
||||||
forward_leap_strength: 24.0,
|
forward_leap_strength: 20.0,
|
||||||
vertical_leap_strength: 8.0,
|
vertical_leap_strength: 8.0,
|
||||||
)
|
)
|
||||||
|
@ -5,7 +5,7 @@ ComboMelee(
|
|||||||
damage_increase: 10,
|
damage_increase: 10,
|
||||||
base_poise_damage: 25,
|
base_poise_damage: 25,
|
||||||
poise_damage_increase: 0,
|
poise_damage_increase: 0,
|
||||||
knockback: 10.0,
|
knockback: 5.0,
|
||||||
range: 4.5,
|
range: 4.5,
|
||||||
angle: 50.0,
|
angle: 50.0,
|
||||||
base_buildup_duration: 0.2,
|
base_buildup_duration: 0.2,
|
||||||
|
@ -12,6 +12,5 @@ BasicRanged(
|
|||||||
col: (1.0, 0.75, 0.11).into(),
|
col: (1.0, 0.75, 0.11).into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),*/
|
}),*/
|
||||||
projectile_gravity: Some(Gravity(0.3)),
|
|
||||||
projectile_speed: 60.0,
|
projectile_speed: 60.0,
|
||||||
)
|
)
|
||||||
|
@ -12,6 +12,5 @@ BasicRanged(
|
|||||||
col: (1.0, 0.75, 0.11).into(),
|
col: (1.0, 0.75, 0.11).into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),*/
|
}),*/
|
||||||
projectile_gravity: Some(Gravity(0.3)),
|
|
||||||
projectile_speed: 60.0,
|
projectile_speed: 60.0,
|
||||||
)
|
)
|
||||||
|
@ -6,7 +6,7 @@ ComboMelee(
|
|||||||
damage_increase: 10,
|
damage_increase: 10,
|
||||||
base_poise_damage: 10,
|
base_poise_damage: 10,
|
||||||
poise_damage_increase: 0,
|
poise_damage_increase: 0,
|
||||||
knockback: 10.0,
|
knockback: 1.0,
|
||||||
range: 4.0,
|
range: 4.0,
|
||||||
angle: 30.0,
|
angle: 30.0,
|
||||||
base_buildup_duration: 0.15,
|
base_buildup_duration: 0.15,
|
||||||
@ -20,7 +20,7 @@ ComboMelee(
|
|||||||
damage_increase: 15,
|
damage_increase: 15,
|
||||||
base_poise_damage: 13,
|
base_poise_damage: 13,
|
||||||
poise_damage_increase: 0,
|
poise_damage_increase: 0,
|
||||||
knockback: 12.0,
|
knockback: 2.0,
|
||||||
range: 3.5,
|
range: 3.5,
|
||||||
angle: 40.0,
|
angle: 40.0,
|
||||||
base_buildup_duration: 0.1,
|
base_buildup_duration: 0.1,
|
||||||
@ -34,7 +34,7 @@ ComboMelee(
|
|||||||
damage_increase: 20,
|
damage_increase: 20,
|
||||||
base_poise_damage: 15,
|
base_poise_damage: 15,
|
||||||
poise_damage_increase: 0,
|
poise_damage_increase: 0,
|
||||||
knockback: 14.0,
|
knockback: 4.0,
|
||||||
range: 6.0,
|
range: 6.0,
|
||||||
angle: 10.0,
|
angle: 10.0,
|
||||||
base_buildup_duration: 0.15,
|
base_buildup_duration: 0.15,
|
||||||
|
@ -12,6 +12,5 @@ BasicRanged(
|
|||||||
col: (1.0, 0.75, 0.11).into(),
|
col: (1.0, 0.75, 0.11).into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),*/
|
}),*/
|
||||||
projectile_gravity: Some(Gravity(5.0)),
|
|
||||||
projectile_speed: 70.0,
|
projectile_speed: 70.0,
|
||||||
)
|
)
|
||||||
|
@ -9,6 +9,5 @@ BasicRanged(
|
|||||||
),
|
),
|
||||||
projectile_body: Object(ArrowTurret),
|
projectile_body: Object(ArrowTurret),
|
||||||
projectile_light: None,
|
projectile_light: None,
|
||||||
projectile_gravity: Some(Gravity(0.1)),
|
projectile_speed: 90.0,
|
||||||
projectile_speed: 100.0,
|
|
||||||
)
|
)
|
||||||
|
@ -11,6 +11,5 @@ BasicRanged(
|
|||||||
col: (1.0, 0.75, 0.11).into(),
|
col: (1.0, 0.75, 0.11).into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),*/
|
}),*/
|
||||||
projectile_gravity: Some(Gravity(0.3)),
|
|
||||||
projectile_speed: 60.0,
|
projectile_speed: 60.0,
|
||||||
)
|
)
|
||||||
|
@ -15,7 +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"
|
# inline_tweak = "1.0.8"
|
||||||
|
|
||||||
# Serde
|
# Serde
|
||||||
serde = { version = "1.0.110", features = ["derive", "rc"] }
|
serde = { version = "1.0.110", features = ["derive", "rc"] }
|
||||||
|
@ -29,8 +29,8 @@ sum_type! {
|
|||||||
MountState(comp::MountState),
|
MountState(comp::MountState),
|
||||||
Mounting(comp::Mounting),
|
Mounting(comp::Mounting),
|
||||||
Mass(comp::Mass),
|
Mass(comp::Mass),
|
||||||
|
Density(comp::Density),
|
||||||
Collider(comp::Collider),
|
Collider(comp::Collider),
|
||||||
Gravity(comp::Gravity),
|
|
||||||
Sticky(comp::Sticky),
|
Sticky(comp::Sticky),
|
||||||
CharacterState(comp::CharacterState),
|
CharacterState(comp::CharacterState),
|
||||||
Pos(comp::Pos),
|
Pos(comp::Pos),
|
||||||
@ -64,8 +64,8 @@ sum_type! {
|
|||||||
MountState(PhantomData<comp::MountState>),
|
MountState(PhantomData<comp::MountState>),
|
||||||
Mounting(PhantomData<comp::Mounting>),
|
Mounting(PhantomData<comp::Mounting>),
|
||||||
Mass(PhantomData<comp::Mass>),
|
Mass(PhantomData<comp::Mass>),
|
||||||
|
Density(PhantomData<comp::Density>),
|
||||||
Collider(PhantomData<comp::Collider>),
|
Collider(PhantomData<comp::Collider>),
|
||||||
Gravity(PhantomData<comp::Gravity>),
|
|
||||||
Sticky(PhantomData<comp::Sticky>),
|
Sticky(PhantomData<comp::Sticky>),
|
||||||
CharacterState(PhantomData<comp::CharacterState>),
|
CharacterState(PhantomData<comp::CharacterState>),
|
||||||
Pos(PhantomData<comp::Pos>),
|
Pos(PhantomData<comp::Pos>),
|
||||||
@ -99,8 +99,8 @@ impl sync::CompPacket for EcsCompPacket {
|
|||||||
EcsCompPacket::MountState(comp) => sync::handle_insert(comp, entity, world),
|
EcsCompPacket::MountState(comp) => sync::handle_insert(comp, entity, world),
|
||||||
EcsCompPacket::Mounting(comp) => sync::handle_insert(comp, entity, world),
|
EcsCompPacket::Mounting(comp) => sync::handle_insert(comp, entity, world),
|
||||||
EcsCompPacket::Mass(comp) => sync::handle_insert(comp, entity, world),
|
EcsCompPacket::Mass(comp) => sync::handle_insert(comp, entity, world),
|
||||||
|
EcsCompPacket::Density(comp) => sync::handle_insert(comp, entity, world),
|
||||||
EcsCompPacket::Collider(comp) => sync::handle_insert(comp, entity, world),
|
EcsCompPacket::Collider(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) => {
|
EcsCompPacket::Pos(comp) => {
|
||||||
@ -138,8 +138,8 @@ impl sync::CompPacket for EcsCompPacket {
|
|||||||
EcsCompPacket::MountState(comp) => sync::handle_modify(comp, entity, world),
|
EcsCompPacket::MountState(comp) => sync::handle_modify(comp, entity, world),
|
||||||
EcsCompPacket::Mounting(comp) => sync::handle_modify(comp, entity, world),
|
EcsCompPacket::Mounting(comp) => sync::handle_modify(comp, entity, world),
|
||||||
EcsCompPacket::Mass(comp) => sync::handle_modify(comp, entity, world),
|
EcsCompPacket::Mass(comp) => sync::handle_modify(comp, entity, world),
|
||||||
|
EcsCompPacket::Density(comp) => sync::handle_modify(comp, entity, world),
|
||||||
EcsCompPacket::Collider(comp) => sync::handle_modify(comp, entity, world),
|
EcsCompPacket::Collider(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) => {
|
EcsCompPacket::Pos(comp) => {
|
||||||
@ -179,8 +179,8 @@ impl sync::CompPacket for EcsCompPacket {
|
|||||||
EcsCompPhantom::MountState(_) => sync::handle_remove::<comp::MountState>(entity, world),
|
EcsCompPhantom::MountState(_) => sync::handle_remove::<comp::MountState>(entity, world),
|
||||||
EcsCompPhantom::Mounting(_) => sync::handle_remove::<comp::Mounting>(entity, world),
|
EcsCompPhantom::Mounting(_) => sync::handle_remove::<comp::Mounting>(entity, world),
|
||||||
EcsCompPhantom::Mass(_) => sync::handle_remove::<comp::Mass>(entity, world),
|
EcsCompPhantom::Mass(_) => sync::handle_remove::<comp::Mass>(entity, world),
|
||||||
|
EcsCompPhantom::Density(_) => sync::handle_remove::<comp::Density>(entity, world),
|
||||||
EcsCompPhantom::Collider(_) => sync::handle_remove::<comp::Collider>(entity, world),
|
EcsCompPhantom::Collider(_) => sync::handle_remove::<comp::Collider>(entity, world),
|
||||||
EcsCompPhantom::Gravity(_) => sync::handle_remove::<comp::Gravity>(entity, world),
|
|
||||||
EcsCompPhantom::Sticky(_) => sync::handle_remove::<comp::Sticky>(entity, world),
|
EcsCompPhantom::Sticky(_) => sync::handle_remove::<comp::Sticky>(entity, world),
|
||||||
EcsCompPhantom::CharacterState(_) => {
|
EcsCompPhantom::CharacterState(_) => {
|
||||||
sync::handle_remove::<comp::CharacterState>(entity, world)
|
sync::handle_remove::<comp::CharacterState>(entity, world)
|
||||||
|
@ -631,7 +631,8 @@ pub enum KnockbackDir {
|
|||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
impl Knockback {
|
impl Knockback {
|
||||||
pub fn calculate_impulse(self, dir: Dir) -> Vec3<f32> {
|
pub fn calculate_impulse(self, dir: Dir) -> Vec3<f32> {
|
||||||
match self.direction {
|
// TEMP until source knockback values have been updated
|
||||||
|
50.0 * match self.direction {
|
||||||
KnockbackDir::Away => self.strength * *Dir::slerp(dir, Dir::new(Vec3::unit_z()), 0.5),
|
KnockbackDir::Away => self.strength * *Dir::slerp(dir, Dir::new(Vec3::unit_z()), 0.5),
|
||||||
KnockbackDir::Towards => {
|
KnockbackDir::Towards => {
|
||||||
self.strength * *Dir::slerp(-dir, Dir::new(Vec3::unit_z()), 0.5)
|
self.strength * *Dir::slerp(-dir, Dir::new(Vec3::unit_z()), 0.5)
|
||||||
|
@ -3,7 +3,7 @@ use crate::{
|
|||||||
combat::{self, CombatEffect, Knockback},
|
combat::{self, CombatEffect, Knockback},
|
||||||
comp::{
|
comp::{
|
||||||
aura, beam, inventory::item::tool::ToolKind, projectile::ProjectileConstructor, skills,
|
aura, beam, inventory::item::tool::ToolKind, projectile::ProjectileConstructor, skills,
|
||||||
Body, CharacterState, EnergySource, Gravity, LightEmitter, StateUpdate,
|
Body, CharacterState, EnergySource, LightEmitter, StateUpdate,
|
||||||
},
|
},
|
||||||
states::{
|
states::{
|
||||||
behavior::JoinData,
|
behavior::JoinData,
|
||||||
@ -74,7 +74,6 @@ pub enum CharacterAbility {
|
|||||||
projectile: ProjectileConstructor,
|
projectile: ProjectileConstructor,
|
||||||
projectile_body: Body,
|
projectile_body: Body,
|
||||||
projectile_light: Option<LightEmitter>,
|
projectile_light: Option<LightEmitter>,
|
||||||
projectile_gravity: Option<Gravity>,
|
|
||||||
projectile_speed: f32,
|
projectile_speed: f32,
|
||||||
},
|
},
|
||||||
RepeaterRanged {
|
RepeaterRanged {
|
||||||
@ -87,7 +86,6 @@ pub enum CharacterAbility {
|
|||||||
projectile: ProjectileConstructor,
|
projectile: ProjectileConstructor,
|
||||||
projectile_body: Body,
|
projectile_body: Body,
|
||||||
projectile_light: Option<LightEmitter>,
|
projectile_light: Option<LightEmitter>,
|
||||||
projectile_gravity: Option<Gravity>,
|
|
||||||
projectile_speed: f32,
|
projectile_speed: f32,
|
||||||
reps_remaining: u32,
|
reps_remaining: u32,
|
||||||
},
|
},
|
||||||
@ -197,7 +195,6 @@ pub enum CharacterAbility {
|
|||||||
recover_duration: f32,
|
recover_duration: f32,
|
||||||
projectile_body: Body,
|
projectile_body: Body,
|
||||||
projectile_light: Option<LightEmitter>,
|
projectile_light: Option<LightEmitter>,
|
||||||
projectile_gravity: Option<Gravity>,
|
|
||||||
initial_projectile_speed: f32,
|
initial_projectile_speed: f32,
|
||||||
scaled_projectile_speed: f32,
|
scaled_projectile_speed: f32,
|
||||||
move_speed: f32,
|
move_speed: f32,
|
||||||
@ -1163,7 +1160,6 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
|||||||
projectile,
|
projectile,
|
||||||
projectile_body,
|
projectile_body,
|
||||||
projectile_light,
|
projectile_light,
|
||||||
projectile_gravity,
|
|
||||||
projectile_speed,
|
projectile_speed,
|
||||||
energy_cost: _,
|
energy_cost: _,
|
||||||
} => CharacterState::BasicRanged(basic_ranged::Data {
|
} => CharacterState::BasicRanged(basic_ranged::Data {
|
||||||
@ -1173,7 +1169,6 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
|||||||
projectile: *projectile,
|
projectile: *projectile,
|
||||||
projectile_body: *projectile_body,
|
projectile_body: *projectile_body,
|
||||||
projectile_light: *projectile_light,
|
projectile_light: *projectile_light,
|
||||||
projectile_gravity: *projectile_gravity,
|
|
||||||
projectile_speed: *projectile_speed,
|
projectile_speed: *projectile_speed,
|
||||||
ability_info,
|
ability_info,
|
||||||
},
|
},
|
||||||
@ -1416,7 +1411,6 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
|||||||
recover_duration,
|
recover_duration,
|
||||||
projectile_body,
|
projectile_body,
|
||||||
projectile_light,
|
projectile_light,
|
||||||
projectile_gravity,
|
|
||||||
initial_projectile_speed,
|
initial_projectile_speed,
|
||||||
scaled_projectile_speed,
|
scaled_projectile_speed,
|
||||||
move_speed,
|
move_speed,
|
||||||
@ -1433,7 +1427,6 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
|||||||
scaled_knockback: *scaled_knockback,
|
scaled_knockback: *scaled_knockback,
|
||||||
projectile_body: *projectile_body,
|
projectile_body: *projectile_body,
|
||||||
projectile_light: *projectile_light,
|
projectile_light: *projectile_light,
|
||||||
projectile_gravity: *projectile_gravity,
|
|
||||||
initial_projectile_speed: *initial_projectile_speed,
|
initial_projectile_speed: *initial_projectile_speed,
|
||||||
scaled_projectile_speed: *scaled_projectile_speed,
|
scaled_projectile_speed: *scaled_projectile_speed,
|
||||||
move_speed: *move_speed,
|
move_speed: *move_speed,
|
||||||
@ -1453,7 +1446,6 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
|||||||
projectile,
|
projectile,
|
||||||
projectile_body,
|
projectile_body,
|
||||||
projectile_light,
|
projectile_light,
|
||||||
projectile_gravity,
|
|
||||||
projectile_speed,
|
projectile_speed,
|
||||||
reps_remaining,
|
reps_remaining,
|
||||||
} => CharacterState::RepeaterRanged(repeater_ranged::Data {
|
} => CharacterState::RepeaterRanged(repeater_ranged::Data {
|
||||||
@ -1466,7 +1458,6 @@ impl From<(&CharacterAbility, AbilityInfo)> for CharacterState {
|
|||||||
projectile: *projectile,
|
projectile: *projectile,
|
||||||
projectile_body: *projectile_body,
|
projectile_body: *projectile_body,
|
||||||
projectile_light: *projectile_light,
|
projectile_light: *projectile_light,
|
||||||
projectile_gravity: *projectile_gravity,
|
|
||||||
projectile_speed: *projectile_speed,
|
projectile_speed: *projectile_speed,
|
||||||
ability_info,
|
ability_info,
|
||||||
},
|
},
|
||||||
|
@ -16,6 +16,7 @@ pub mod theropod;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
assets::{self, Asset},
|
assets::{self, Asset},
|
||||||
|
consts::{HUMAN_DENSITY, WATER_DENSITY},
|
||||||
make_case_elim,
|
make_case_elim,
|
||||||
npc::NpcKind,
|
npc::NpcKind,
|
||||||
};
|
};
|
||||||
@ -24,7 +25,7 @@ use specs::{Component, DerefFlaggedStorage};
|
|||||||
use specs_idvs::IdvStorage;
|
use specs_idvs::IdvStorage;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
use super::BuffKind;
|
use super::{BuffKind, Density, Mass};
|
||||||
|
|
||||||
make_case_elim!(
|
make_case_elim!(
|
||||||
body,
|
body,
|
||||||
@ -145,165 +146,228 @@ impl<
|
|||||||
impl Body {
|
impl Body {
|
||||||
pub fn is_humanoid(&self) -> bool { matches!(self, Body::Humanoid(_)) }
|
pub fn is_humanoid(&self) -> bool { matches!(self, Body::Humanoid(_)) }
|
||||||
|
|
||||||
// Note: this might need to be refined to something more complex for realistic
|
/// Average density of the body
|
||||||
// behavior with less cylindrical bodies (e.g. wolfs)
|
// Units are based on kg/m³
|
||||||
#[allow(unreachable_patterns)]
|
pub fn density(&self) -> Density {
|
||||||
pub fn radius(&self) -> f32 {
|
let d = match self {
|
||||||
// TODO: Improve these values (some might be reliant on more info in inner type)
|
// based on a house sparrow (Passer domesticus)
|
||||||
match self {
|
Body::BirdMedium(_) => 700.0,
|
||||||
Body::Humanoid(humanoid) => match (humanoid.species, humanoid.body_type) {
|
Body::BirdSmall(_) => 700.0,
|
||||||
(humanoid::Species::Orc, humanoid::BodyType::Male) => 0.75,
|
|
||||||
(humanoid::Species::Orc, humanoid::BodyType::Female) => 0.75,
|
|
||||||
(humanoid::Species::Human, humanoid::BodyType::Male) => 0.75,
|
|
||||||
(humanoid::Species::Human, humanoid::BodyType::Female) => 0.75,
|
|
||||||
(humanoid::Species::Elf, humanoid::BodyType::Male) => 0.75,
|
|
||||||
(humanoid::Species::Elf, humanoid::BodyType::Female) => 0.75,
|
|
||||||
(humanoid::Species::Dwarf, humanoid::BodyType::Male) => 0.75,
|
|
||||||
(humanoid::Species::Dwarf, humanoid::BodyType::Female) => 0.75,
|
|
||||||
(humanoid::Species::Undead, humanoid::BodyType::Male) => 0.75,
|
|
||||||
(humanoid::Species::Undead, humanoid::BodyType::Female) => 0.75,
|
|
||||||
(humanoid::Species::Danari, humanoid::BodyType::Male) => 0.75,
|
|
||||||
(humanoid::Species::Danari, humanoid::BodyType::Female) => 0.75,
|
|
||||||
_ => 0.75,
|
|
||||||
},
|
|
||||||
Body::QuadrupedSmall(_) => 0.6,
|
|
||||||
Body::QuadrupedMedium(body) => match body.species {
|
|
||||||
quadruped_medium::Species::Grolgar => 2.0,
|
|
||||||
quadruped_medium::Species::Tarasque => 2.0,
|
|
||||||
quadruped_medium::Species::Lion => 2.0,
|
|
||||||
quadruped_medium::Species::Saber => 2.0,
|
|
||||||
quadruped_medium::Species::Catoblepas => 2.0,
|
|
||||||
quadruped_medium::Species::Horse => 1.5,
|
|
||||||
quadruped_medium::Species::Deer => 1.5,
|
|
||||||
quadruped_medium::Species::Donkey => 1.5,
|
|
||||||
quadruped_medium::Species::Kelpie => 1.5,
|
|
||||||
quadruped_medium::Species::Barghest => 1.8,
|
|
||||||
quadruped_medium::Species::Cattle => 1.8,
|
|
||||||
quadruped_medium::Species::Highland => 1.8,
|
|
||||||
quadruped_medium::Species::Yak => 1.8,
|
|
||||||
quadruped_medium::Species::Panda => 1.8,
|
|
||||||
quadruped_medium::Species::Bear => 1.8,
|
|
||||||
_ => 1.5,
|
|
||||||
},
|
|
||||||
Body::QuadrupedLow(body) => match body.species {
|
|
||||||
quadruped_low::Species::Asp => 2.5,
|
|
||||||
quadruped_low::Species::Monitor => 2.3,
|
|
||||||
quadruped_low::Species::Crocodile => 2.4,
|
|
||||||
quadruped_low::Species::Salamander => 2.4,
|
|
||||||
quadruped_low::Species::Pangolin => 2.0,
|
|
||||||
quadruped_low::Species::Lavadrake => 2.5,
|
|
||||||
quadruped_low::Species::Deadwood => 0.5,
|
|
||||||
_ => 1.6,
|
|
||||||
},
|
|
||||||
Body::Theropod(body) => match body.species {
|
|
||||||
theropod::Species::Snowraptor => 1.5,
|
|
||||||
theropod::Species::Sandraptor => 1.5,
|
|
||||||
theropod::Species::Woodraptor => 1.5,
|
|
||||||
theropod::Species::Archaeos => 3.5,
|
|
||||||
theropod::Species::Odonto => 3.5,
|
|
||||||
theropod::Species::Yale => 0.8,
|
|
||||||
theropod::Species::Ntouka => 3.0,
|
|
||||||
_ => 1.8,
|
|
||||||
},
|
|
||||||
Body::BirdMedium(_) => 1.0,
|
|
||||||
Body::FishMedium(_) => 1.0,
|
|
||||||
Body::Dragon(_) => 8.0,
|
|
||||||
Body::BirdSmall(_) => 0.6,
|
|
||||||
Body::FishSmall(_) => 0.6,
|
|
||||||
Body::BipedLarge(body) => match body.species {
|
|
||||||
biped_large::Species::Slysaurok => 2.0,
|
|
||||||
biped_large::Species::Occultsaurok => 2.0,
|
|
||||||
biped_large::Species::Mightysaurok => 2.0,
|
|
||||||
biped_large::Species::Mindflayer => 2.2,
|
|
||||||
biped_large::Species::Minotaur => 3.0,
|
|
||||||
|
|
||||||
_ => 2.3,
|
// based on its mass divided by the volume of a bird scaled up to the size of the dragon
|
||||||
},
|
Body::Dragon(_) => 3_700.0,
|
||||||
Body::Golem(_) => 2.5,
|
|
||||||
Body::BipedSmall(_) => 0.75,
|
Body::Golem(_) => WATER_DENSITY * 2.5,
|
||||||
Body::Object(_) => 0.4,
|
Body::Humanoid(_) => HUMAN_DENSITY,
|
||||||
Body::Ship(_) => 1.0,
|
Body::Ship(ship) => ship.density().0,
|
||||||
}
|
Body::Object(object) => object.density().0,
|
||||||
|
_ => HUMAN_DENSITY,
|
||||||
|
};
|
||||||
|
Density(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn height(&self) -> f32 {
|
// Values marked with ~✅ are checked based on their RL equivalent.
|
||||||
match self {
|
// Discrepancy in size compared to their RL equivalents has not necessarily been
|
||||||
Body::Humanoid(humanoid) => match (humanoid.species, humanoid.body_type) {
|
// taken into account.
|
||||||
(humanoid::Species::Orc, humanoid::BodyType::Male) => 2.3,
|
pub fn mass(&self) -> Mass {
|
||||||
(humanoid::Species::Orc, humanoid::BodyType::Female) => 2.2,
|
let m = match self {
|
||||||
(humanoid::Species::Human, humanoid::BodyType::Male) => 2.3,
|
Body::BipedLarge(body) => match body.species {
|
||||||
(humanoid::Species::Human, humanoid::BodyType::Female) => 2.2,
|
biped_large::Species::Slysaurok => 400.0,
|
||||||
(humanoid::Species::Elf, humanoid::BodyType::Male) => 2.3,
|
biped_large::Species::Occultsaurok => 400.0,
|
||||||
(humanoid::Species::Elf, humanoid::BodyType::Female) => 2.2,
|
biped_large::Species::Mightysaurok => 400.0,
|
||||||
(humanoid::Species::Dwarf, humanoid::BodyType::Male) => 1.9,
|
biped_large::Species::Mindflayer => 420.0,
|
||||||
(humanoid::Species::Dwarf, humanoid::BodyType::Female) => 1.8,
|
biped_large::Species::Minotaur => 500.0,
|
||||||
(humanoid::Species::Undead, humanoid::BodyType::Male) => 2.2,
|
_ => 400.0,
|
||||||
(humanoid::Species::Undead, humanoid::BodyType::Female) => 2.1,
|
},
|
||||||
(humanoid::Species::Danari, humanoid::BodyType::Male) => 1.5,
|
Body::BipedSmall(_) => 50.0,
|
||||||
(humanoid::Species::Danari, humanoid::BodyType::Female) => 1.4,
|
|
||||||
|
// ravens are 0.69-2 kg, crows are 0.51 kg on average
|
||||||
|
Body::BirdMedium(_) => 1.0,
|
||||||
|
// australian magpies are around 0.22-0.35 kg
|
||||||
|
Body::BirdSmall(_) => 0.3,
|
||||||
|
|
||||||
|
Body::Dragon(_) => 20_000.0,
|
||||||
|
Body::FishMedium(_) => 2.5,
|
||||||
|
Body::FishSmall(_) => 1.0,
|
||||||
|
Body::Golem(_) => 10_000.0,
|
||||||
|
Body::Humanoid(humanoid) => {
|
||||||
|
// humanoids are quite a bit larger than in real life, so we multiply their mass
|
||||||
|
// to scale it up proportionally (remember cube law)
|
||||||
|
1.0 * match (humanoid.species, humanoid.body_type) {
|
||||||
|
(humanoid::Species::Orc, humanoid::BodyType::Male) => 120.0,
|
||||||
|
(humanoid::Species::Orc, humanoid::BodyType::Female) => 120.0,
|
||||||
|
(humanoid::Species::Human, humanoid::BodyType::Male) => 77.0, // ~✅
|
||||||
|
(humanoid::Species::Human, humanoid::BodyType::Female) => 59.0, // ~✅
|
||||||
|
(humanoid::Species::Elf, humanoid::BodyType::Male) => 77.0,
|
||||||
|
(humanoid::Species::Elf, humanoid::BodyType::Female) => 59.0,
|
||||||
|
(humanoid::Species::Dwarf, humanoid::BodyType::Male) => 70.0,
|
||||||
|
(humanoid::Species::Dwarf, humanoid::BodyType::Female) => 70.0,
|
||||||
|
(humanoid::Species::Undead, humanoid::BodyType::Male) => 70.0,
|
||||||
|
(humanoid::Species::Undead, humanoid::BodyType::Female) => 50.0,
|
||||||
|
(humanoid::Species::Danari, humanoid::BodyType::Male) => 80.0,
|
||||||
|
(humanoid::Species::Danari, humanoid::BodyType::Female) => 60.0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Body::Object(obj) => obj.mass().0,
|
||||||
|
Body::QuadrupedLow(body) => match body.species {
|
||||||
|
quadruped_low::Species::Alligator => 360.0, // ~✅
|
||||||
|
quadruped_low::Species::Asp => 300.0,
|
||||||
|
// saltwater crocodiles can weigh around 1 ton, but our version is the size of an
|
||||||
|
// alligator or smaller, so whatever
|
||||||
|
quadruped_low::Species::Crocodile => 360.0,
|
||||||
|
quadruped_low::Species::Deadwood => 400.0,
|
||||||
|
quadruped_low::Species::Lavadrake => 500.0,
|
||||||
|
quadruped_low::Species::Monitor => 100.0,
|
||||||
|
quadruped_low::Species::Pangolin => 100.0,
|
||||||
|
quadruped_low::Species::Salamander => 65.0,
|
||||||
|
quadruped_low::Species::Tortoise => 200.0,
|
||||||
|
_ => 200.0,
|
||||||
|
},
|
||||||
|
Body::QuadrupedMedium(body) => match body.species {
|
||||||
|
quadruped_medium::Species::Bear => 500.0, // ~✅ (350-700 kg)
|
||||||
|
quadruped_medium::Species::Cattle => 575.0, // ~✅ (500-650 kg)
|
||||||
|
quadruped_medium::Species::Deer => 80.0,
|
||||||
|
quadruped_medium::Species::Donkey => 200.0,
|
||||||
|
quadruped_medium::Species::Highland => 200.0,
|
||||||
|
quadruped_medium::Species::Horse => 500.0, // ~✅
|
||||||
|
quadruped_medium::Species::Kelpie => 200.0,
|
||||||
|
quadruped_medium::Species::Lion => 170.0, // ~✅ (110-225 kg)
|
||||||
|
quadruped_medium::Species::Panda => 200.0,
|
||||||
|
quadruped_medium::Species::Saber => 130.0,
|
||||||
|
quadruped_medium::Species::Yak => 200.0,
|
||||||
|
_ => 200.0,
|
||||||
},
|
},
|
||||||
Body::QuadrupedSmall(body) => match body.species {
|
Body::QuadrupedSmall(body) => match body.species {
|
||||||
quadruped_small::Species::Dodarock => 1.5,
|
quadruped_small::Species::Batfox => 50.0,
|
||||||
quadruped_small::Species::Holladon => 1.5,
|
quadruped_small::Species::Boar => 80.0, // ~✅ (60-100 kg)
|
||||||
quadruped_small::Species::Truffler => 2.0,
|
quadruped_small::Species::Dodarock => 150.0,
|
||||||
_ => 1.0,
|
quadruped_small::Species::Holladon => 150.0,
|
||||||
},
|
quadruped_small::Species::Hyena => 70.0, // ~✅ (vaguely)
|
||||||
Body::QuadrupedMedium(body) => match body.species {
|
quadruped_small::Species::Truffler => 150.0,
|
||||||
quadruped_medium::Species::Tarasque => 2.6,
|
_ => 80.0,
|
||||||
quadruped_medium::Species::Lion => 2.0,
|
|
||||||
quadruped_medium::Species::Saber => 2.0,
|
|
||||||
quadruped_medium::Species::Catoblepas => 2.9,
|
|
||||||
quadruped_medium::Species::Barghest => 2.5,
|
|
||||||
quadruped_medium::Species::Dreadhorn => 2.5,
|
|
||||||
quadruped_medium::Species::Moose => 2.5,
|
|
||||||
_ => 1.6,
|
|
||||||
},
|
|
||||||
Body::QuadrupedLow(body) => match body.species {
|
|
||||||
quadruped_low::Species::Monitor => 1.5,
|
|
||||||
quadruped_low::Species::Tortoise => 2.0,
|
|
||||||
quadruped_low::Species::Rocksnapper => 2.9,
|
|
||||||
quadruped_low::Species::Maneater => 4.0,
|
|
||||||
_ => 1.3,
|
|
||||||
},
|
},
|
||||||
Body::Theropod(body) => match body.species {
|
Body::Theropod(body) => match body.species {
|
||||||
theropod::Species::Snowraptor => 2.6,
|
// for reference, elephants are in the range of 2.6-6.9 tons
|
||||||
theropod::Species::Sandraptor => 2.6,
|
// and Tyrannosaurus rex were ~8.4-14 tons
|
||||||
theropod::Species::Woodraptor => 2.6,
|
theropod::Species::Archaeos => 13_000.0,
|
||||||
theropod::Species::Sunlizard => 2.5,
|
theropod::Species::Ntouka => 13_000.0,
|
||||||
theropod::Species::Yale => 3.0,
|
theropod::Species::Odonto => 13_000.0,
|
||||||
_ => 8.0,
|
|
||||||
},
|
|
||||||
Body::BirdMedium(body) => match body.species {
|
|
||||||
bird_medium::Species::Cockatrice => 1.8,
|
|
||||||
_ => 1.1,
|
|
||||||
},
|
|
||||||
Body::FishMedium(_) => 0.8,
|
|
||||||
Body::Dragon(_) => 16.0,
|
|
||||||
Body::BirdSmall(_) => 1.1,
|
|
||||||
Body::FishSmall(_) => 0.6,
|
|
||||||
Body::BipedLarge(body) => match body.species {
|
|
||||||
biped_large::Species::Slysaurok => 3.4,
|
|
||||||
biped_large::Species::Occultsaurok => 3.4,
|
|
||||||
biped_large::Species::Mightysaurok => 3.4,
|
|
||||||
biped_large::Species::Mindflayer => 8.0,
|
|
||||||
biped_large::Species::Minotaur => 8.0,
|
|
||||||
biped_large::Species::Dullahan => 5.5,
|
|
||||||
biped_large::Species::Cyclops => 6.5,
|
|
||||||
biped_large::Species::Werewolf => 3.5,
|
|
||||||
|
|
||||||
_ => 6.0,
|
theropod::Species::Sandraptor => 500.0,
|
||||||
|
theropod::Species::Snowraptor => 500.0,
|
||||||
|
theropod::Species::Sunlizard => 500.0,
|
||||||
|
theropod::Species::Woodraptor => 500.0,
|
||||||
|
theropod::Species::Yale => 1_000.0,
|
||||||
},
|
},
|
||||||
Body::Golem(_) => 5.0,
|
Body::Ship(ship) => ship.mass().0,
|
||||||
Body::BipedSmall(_) => 1.4,
|
};
|
||||||
Body::Object(object) => match object {
|
Mass(m)
|
||||||
object::Body::Crossbow => 1.7,
|
}
|
||||||
object::Body::TrainingDummy => 2.2,
|
|
||||||
_ => 1.0,
|
/// The width (shoulder to shoulder), length (nose to tail) and height
|
||||||
|
/// respectively
|
||||||
|
pub fn dimensions(&self) -> Vec3<f32> {
|
||||||
|
match self {
|
||||||
|
Body::BipedLarge(body) => match body.species {
|
||||||
|
biped_large::Species::Cyclops => Vec3::new(4.6, 3.0, 6.5),
|
||||||
|
biped_large::Species::Dullahan => Vec3::new(4.6, 3.0, 5.5),
|
||||||
|
biped_large::Species::Mightysaurok => Vec3::new(4.0, 3.0, 3.4),
|
||||||
|
biped_large::Species::Mindflayer => Vec3::new(4.4, 3.0, 8.0),
|
||||||
|
biped_large::Species::Minotaur => Vec3::new(6.0, 3.0, 8.0),
|
||||||
|
biped_large::Species::Occultsaurok => Vec3::new(4.0, 3.0, 3.4),
|
||||||
|
biped_large::Species::Slysaurok => Vec3::new(4.0, 3.0, 3.4),
|
||||||
|
biped_large::Species::Werewolf => Vec3::new(4.0, 3.0, 3.5),
|
||||||
|
|
||||||
|
_ => Vec3::new(4.6, 3.0, 6.0),
|
||||||
|
},
|
||||||
|
Body::BipedSmall(_) => Vec3::new(1.0, 0.75, 1.4),
|
||||||
|
Body::BirdMedium(body) => match body.species {
|
||||||
|
bird_medium::Species::Cockatrice => Vec3::new(2.0, 1.0, 1.8),
|
||||||
|
_ => Vec3::new(2.0, 1.0, 1.1),
|
||||||
|
},
|
||||||
|
Body::BirdSmall(_) => Vec3::new(1.2, 0.6, 1.1),
|
||||||
|
Body::Dragon(_) => Vec3::new(16.0, 10.0, 16.0),
|
||||||
|
Body::FishMedium(_) => Vec3::new(0.5, 2.0, 0.8),
|
||||||
|
Body::FishSmall(_) => Vec3::new(0.3, 1.2, 0.6),
|
||||||
|
Body::Golem(_) => Vec3::new(5.0, 5.0, 5.0),
|
||||||
|
Body::Humanoid(humanoid) => {
|
||||||
|
let height = match (humanoid.species, humanoid.body_type) {
|
||||||
|
(humanoid::Species::Orc, humanoid::BodyType::Male) => 2.3,
|
||||||
|
(humanoid::Species::Orc, humanoid::BodyType::Female) => 2.2,
|
||||||
|
(humanoid::Species::Human, humanoid::BodyType::Male) => 2.3,
|
||||||
|
(humanoid::Species::Human, humanoid::BodyType::Female) => 2.2,
|
||||||
|
(humanoid::Species::Elf, humanoid::BodyType::Male) => 2.3,
|
||||||
|
(humanoid::Species::Elf, humanoid::BodyType::Female) => 2.2,
|
||||||
|
(humanoid::Species::Dwarf, humanoid::BodyType::Male) => 1.9,
|
||||||
|
(humanoid::Species::Dwarf, humanoid::BodyType::Female) => 1.8,
|
||||||
|
(humanoid::Species::Undead, humanoid::BodyType::Male) => 2.2,
|
||||||
|
(humanoid::Species::Undead, humanoid::BodyType::Female) => 2.1,
|
||||||
|
(humanoid::Species::Danari, humanoid::BodyType::Male) => 1.5,
|
||||||
|
(humanoid::Species::Danari, humanoid::BodyType::Female) => 1.4,
|
||||||
|
};
|
||||||
|
Vec3::new(1.5, 0.5, height)
|
||||||
|
},
|
||||||
|
Body::Object(object) => object.dimensions(),
|
||||||
|
Body::QuadrupedMedium(body) => match body.species {
|
||||||
|
quadruped_medium::Species::Barghest => Vec3::new(2.0, 3.6, 2.5),
|
||||||
|
quadruped_medium::Species::Bear => Vec3::new(2.0, 3.6, 2.0),
|
||||||
|
quadruped_medium::Species::Catoblepas => Vec3::new(2.0, 4.0, 2.9),
|
||||||
|
quadruped_medium::Species::Cattle => Vec3::new(2.0, 3.6, 2.0),
|
||||||
|
quadruped_medium::Species::Deer => Vec3::new(2.0, 3.0, 2.0),
|
||||||
|
quadruped_medium::Species::Dreadhorn => Vec3::new(2.0, 3.0, 2.5),
|
||||||
|
quadruped_medium::Species::Grolgar => Vec3::new(2.0, 4.0, 2.0),
|
||||||
|
quadruped_medium::Species::Highland => Vec3::new(2.0, 3.6, 2.0),
|
||||||
|
quadruped_medium::Species::Horse => Vec3::new(2.0, 3.0, 2.0),
|
||||||
|
quadruped_medium::Species::Lion => Vec3::new(2.0, 3.0, 2.0),
|
||||||
|
quadruped_medium::Species::Moose => Vec3::new(2.0, 4.0, 2.5),
|
||||||
|
quadruped_medium::Species::Saber => Vec3::new(2.0, 4.0, 2.0),
|
||||||
|
quadruped_medium::Species::Tarasque => Vec3::new(2.0, 4.0, 2.6),
|
||||||
|
quadruped_medium::Species::Yak => Vec3::new(2.0, 3.6, 2.0),
|
||||||
|
_ => Vec3::new(2.0, 3.0, 2.0),
|
||||||
|
},
|
||||||
|
Body::QuadrupedSmall(body) => match body.species {
|
||||||
|
quadruped_small::Species::Dodarock => Vec3::new(1.2, 1.2, 1.5),
|
||||||
|
quadruped_small::Species::Holladon => Vec3::new(1.2, 1.2, 1.5),
|
||||||
|
quadruped_small::Species::Truffler => Vec3::new(1.2, 1.2, 2.0),
|
||||||
|
_ => Vec3::new(1.2, 1.2, 1.0),
|
||||||
|
},
|
||||||
|
Body::QuadrupedLow(body) => match body.species {
|
||||||
|
quadruped_low::Species::Asp => Vec3::new(1.0, 2.5, 1.3),
|
||||||
|
quadruped_low::Species::Crocodile => Vec3::new(1.0, 2.4, 1.3),
|
||||||
|
quadruped_low::Species::Deadwood => Vec3::new(1.0, 0.5, 1.3),
|
||||||
|
quadruped_low::Species::Lavadrake => Vec3::new(1.0, 2.5, 1.3),
|
||||||
|
quadruped_low::Species::Maneater => Vec3::new(1.0, 1.6, 4.0),
|
||||||
|
quadruped_low::Species::Monitor => Vec3::new(1.0, 2.3, 1.5),
|
||||||
|
quadruped_low::Species::Pangolin => Vec3::new(1.0, 2.0, 1.3),
|
||||||
|
quadruped_low::Species::Rocksnapper => Vec3::new(1.0, 1.6, 2.9),
|
||||||
|
quadruped_low::Species::Salamander => Vec3::new(1.0, 2.4, 1.3),
|
||||||
|
quadruped_low::Species::Tortoise => Vec3::new(1.0, 1.6, 2.0),
|
||||||
|
_ => Vec3::new(1.0, 1.6, 1.3),
|
||||||
|
},
|
||||||
|
Body::Ship(ship) => ship.dimensions(),
|
||||||
|
Body::Theropod(body) => match body.species {
|
||||||
|
theropod::Species::Archaeos => Vec3::new(4.0, 7.0, 8.0),
|
||||||
|
theropod::Species::Ntouka => Vec3::new(4.0, 6.0, 8.0),
|
||||||
|
theropod::Species::Odonto => Vec3::new(4.0, 6.5, 8.0),
|
||||||
|
theropod::Species::Sandraptor => Vec3::new(2.0, 3.0, 2.6),
|
||||||
|
theropod::Species::Snowraptor => Vec3::new(2.0, 3.0, 2.6),
|
||||||
|
theropod::Species::Sunlizard => Vec3::new(2.0, 3.6, 2.5),
|
||||||
|
theropod::Species::Woodraptor => Vec3::new(2.0, 3.0, 2.6),
|
||||||
|
theropod::Species::Yale => Vec3::new(1.5, 3.2, 6.0),
|
||||||
},
|
},
|
||||||
Body::Ship(_) => 1.0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: This is used for collisions, but it's not very accurate for shapes that
|
||||||
|
// are very much not cylindrical. Eventually this ought to be replaced by more
|
||||||
|
// accurate collision shapes.
|
||||||
|
pub fn radius(&self) -> f32 {
|
||||||
|
let dim = self.dimensions();
|
||||||
|
dim.x.max(dim.y) / 2.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn height(&self) -> f32 { self.dimensions().z }
|
||||||
|
|
||||||
pub fn base_energy(&self) -> u32 {
|
pub fn base_energy(&self) -> u32 {
|
||||||
match self {
|
match self {
|
||||||
Body::BipedLarge(biped_large) => match biped_large.species {
|
Body::BipedLarge(biped_large) => match biped_large.species {
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
use crate::{comp::item::Reagent, make_case_elim};
|
use crate::{
|
||||||
|
comp::{item::Reagent, Density, Mass},
|
||||||
|
consts::{IRON_DENSITY, WATER_DENSITY},
|
||||||
|
make_case_elim,
|
||||||
|
};
|
||||||
use rand::{seq::SliceRandom, thread_rng};
|
use rand::{seq::SliceRandom, thread_rng};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use vek::Vec3;
|
||||||
|
|
||||||
make_case_elim!(
|
make_case_elim!(
|
||||||
body,
|
body,
|
||||||
@ -241,4 +246,43 @@ impl Body {
|
|||||||
Reagent::Yellow => Body::FireworkYellow,
|
Reagent::Yellow => Body::FireworkYellow,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn density(&self) -> Density {
|
||||||
|
let density = match self {
|
||||||
|
Body::Anvil | Body::Cauldron => IRON_DENSITY,
|
||||||
|
Body::Arrow | Body::MultiArrow => 500.0,
|
||||||
|
Body::Bomb => 2000.0, // I have no idea what it's supposed to be
|
||||||
|
Body::Crate => 300.0, // let's say it's a lot of wood and maybe some contents
|
||||||
|
Body::Scarecrow => 900.0,
|
||||||
|
Body::TrainingDummy => 2000.0,
|
||||||
|
// let them sink
|
||||||
|
_ => 1.1 * WATER_DENSITY,
|
||||||
|
};
|
||||||
|
|
||||||
|
Density(density)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mass(&self) -> Mass {
|
||||||
|
let m = match self {
|
||||||
|
// I think MultiArrow is one of several arrows, not several arrows combined?
|
||||||
|
Body::Arrow | Body::MultiArrow => 0.003,
|
||||||
|
Body::Bomb => {
|
||||||
|
0.5 * IRON_DENSITY * std::f32::consts::PI / 6.0 * self.dimensions().x.powi(3)
|
||||||
|
},
|
||||||
|
Body::Scarecrow => 50.0,
|
||||||
|
Body::Cauldron => 5.0,
|
||||||
|
Body::TrainingDummy => 60.0,
|
||||||
|
_ => 1.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
Mass(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dimensions(&self) -> Vec3<f32> {
|
||||||
|
match self {
|
||||||
|
Body::Arrow | Body::ArrowSnake | Body::MultiArrow => Vec3::new(0.01, 0.8, 0.01),
|
||||||
|
Body::BoltFire => Vec3::new(0.1, 0.1, 0.1),
|
||||||
|
_ => Vec3::broadcast(0.2),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
use crate::make_case_elim;
|
use crate::{
|
||||||
|
comp::{Density, Mass},
|
||||||
|
consts::AIR_DENSITY,
|
||||||
|
make_case_elim,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use vek::Vec3;
|
||||||
|
|
||||||
make_case_elim!(
|
make_case_elim!(
|
||||||
body,
|
body,
|
||||||
@ -20,6 +25,33 @@ impl Body {
|
|||||||
Body::DefaultAirship => "Human_Airship",
|
Body::DefaultAirship => "Human_Airship",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dimensions(&self) -> Vec3<f32> { Vec3::new(25.0, 50.0, 40.0) }
|
||||||
|
|
||||||
|
fn balloon_vol(&self) -> f32 {
|
||||||
|
let spheroid_vol = |equat_d: f32, polar_d: f32| -> f32 {
|
||||||
|
(std::f32::consts::PI / 6.0) * equat_d.powi(2) * polar_d
|
||||||
|
};
|
||||||
|
let dim = self.dimensions();
|
||||||
|
spheroid_vol(dim.z, dim.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hull_vol(&self) -> f32 {
|
||||||
|
// height from bottom of keel to deck
|
||||||
|
let deck_height = 10_f32;
|
||||||
|
let dim = self.dimensions();
|
||||||
|
(std::f32::consts::PI / 6.0) * (deck_height * 1.5).powi(2) * dim.y
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hull_density(&self) -> Density {
|
||||||
|
let oak_density = 600_f32;
|
||||||
|
let ratio = 0.1;
|
||||||
|
Density(ratio * oak_density + (1.0 - ratio) * AIR_DENSITY)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn density(&self) -> Density { Density(AIR_DENSITY) }
|
||||||
|
|
||||||
|
pub fn mass(&self) -> Mass { Mass((self.hull_vol() + self.balloon_vol()) * self.density().0) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Terrain is 11.0 scale relative to small-scale voxels, and all figures get
|
/// Terrain is 11.0 scale relative to small-scale voxels, and all figures get
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
combat::Attack,
|
combat::Attack,
|
||||||
comp::{tool::ToolKind, Energy, InputAttr, InputKind, Ori, Pos, Vel},
|
comp::{tool::ToolKind, Density, Energy, InputAttr, InputKind, Ori, Pos, Vel},
|
||||||
event::{LocalEvent, ServerEvent},
|
event::{LocalEvent, ServerEvent},
|
||||||
states::{behavior::JoinData, *},
|
states::{behavior::JoinData, *},
|
||||||
};
|
};
|
||||||
@ -16,6 +16,7 @@ pub struct StateUpdate {
|
|||||||
pub pos: Pos,
|
pub pos: Pos,
|
||||||
pub vel: Vel,
|
pub vel: Vel,
|
||||||
pub ori: Ori,
|
pub ori: Ori,
|
||||||
|
pub density: Density,
|
||||||
pub energy: Energy,
|
pub energy: Energy,
|
||||||
pub swap_equipped_weapons: bool,
|
pub swap_equipped_weapons: bool,
|
||||||
pub queued_inputs: BTreeMap<InputKind, InputAttr>,
|
pub queued_inputs: BTreeMap<InputKind, InputAttr>,
|
||||||
@ -30,6 +31,7 @@ impl From<&JoinData<'_>> for StateUpdate {
|
|||||||
pos: *data.pos,
|
pos: *data.pos,
|
||||||
vel: *data.vel,
|
vel: *data.vel,
|
||||||
ori: *data.ori,
|
ori: *data.ori,
|
||||||
|
density: *data.density,
|
||||||
energy: *data.energy,
|
energy: *data.energy,
|
||||||
swap_equipped_weapons: false,
|
swap_equipped_weapons: false,
|
||||||
character: data.character.clone(),
|
character: data.character.clone(),
|
||||||
|
204
common/src/comp/fluid_dynamics.rs
Normal file
204
common/src/comp/fluid_dynamics.rs
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
use super::{
|
||||||
|
body::{object, Body},
|
||||||
|
Density, Vel,
|
||||||
|
};
|
||||||
|
use crate::{
|
||||||
|
consts::{AIR_DENSITY, WATER_DENSITY},
|
||||||
|
util::Dir,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::f32::consts::PI;
|
||||||
|
use vek::*;
|
||||||
|
|
||||||
|
/// Fluid medium in which the entity exists
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum Fluid {
|
||||||
|
Air { vel: Vel, elevation: f32 },
|
||||||
|
Water { vel: Vel, depth: f32 },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fluid {
|
||||||
|
/// Specific mass
|
||||||
|
pub fn density(&self) -> Density {
|
||||||
|
match self {
|
||||||
|
Self::Air { .. } => Density(AIR_DENSITY),
|
||||||
|
Self::Water { .. } => Density(WATER_DENSITY),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Pressure from entity velocity
|
||||||
|
pub fn dynamic_pressure(&self, vel: &Vel) -> f32 {
|
||||||
|
0.5 * self.density().0 * self.relative_flow(vel).0.magnitude_squared()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub fn static_pressure(&self) -> f32 {
|
||||||
|
match self {
|
||||||
|
Self::Air { elevation, .. } => Self::air_pressure(*elevation),
|
||||||
|
Self::Water { depth, .. } => Self::water_pressure(*depth),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Absolute static pressure of air at elevation
|
||||||
|
pub fn air_pressure(elevation: f32) -> f32 {
|
||||||
|
// At low altitudes above sea level, the pressure decreases by about 1.2 kPa for
|
||||||
|
// every 100 metres.
|
||||||
|
// https://en.wikipedia.org/wiki/Atmospheric_pressure#Altitude_variation
|
||||||
|
ATMOSPHERE - elevation / 12.0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Absolute static pressure of water at depth
|
||||||
|
pub fn water_pressure(depth: f32) -> f32 { WATER_DENSITY * GRAVITY * depth + ATMOSPHERE }
|
||||||
|
*/
|
||||||
|
/// Velocity of fluid, if applicable
|
||||||
|
pub fn flow_vel(&self) -> Vel {
|
||||||
|
match self {
|
||||||
|
Self::Air { vel, .. } => *vel,
|
||||||
|
Self::Water { vel, .. } => *vel,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Very simple but useful in reducing mental overhead
|
||||||
|
pub fn relative_flow(&self, vel: &Vel) -> Vel { Vel(self.flow_vel().0 - vel.0) }
|
||||||
|
|
||||||
|
pub fn is_liquid(&self) -> bool { matches!(self, Fluid::Water { .. }) }
|
||||||
|
|
||||||
|
pub fn elevation(&self) -> Option<f32> {
|
||||||
|
match self {
|
||||||
|
Fluid::Air { elevation, .. } => Some(*elevation),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn depth(&self) -> Option<f32> {
|
||||||
|
match self {
|
||||||
|
Fluid::Water { depth, .. } => Some(*depth),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Fluid {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Air {
|
||||||
|
elevation: 0.0,
|
||||||
|
vel: Vel::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Body {
|
||||||
|
pub fn aerodynamic_forces(&self, rel_flow: &Vel, fluid_density: f32) -> Vec3<f32> {
|
||||||
|
let v_sq = rel_flow.0.magnitude_squared();
|
||||||
|
if v_sq < 0.25 {
|
||||||
|
// don't bother with miniscule forces
|
||||||
|
Vec3::zero()
|
||||||
|
} else {
|
||||||
|
let rel_flow_dir = Dir::new(rel_flow.0 / v_sq.sqrt());
|
||||||
|
// All the coefficients come pre-multiplied by their reference area
|
||||||
|
0.5 * fluid_density * v_sq * self.parasite_drag_coefficient() * *rel_flow_dir
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Parasite drag is the sum of pressure drag and skin friction.
|
||||||
|
/// Skin friction is the drag arising from the shear forces between a fluid
|
||||||
|
/// and a surface, while pressure drag is due to flow separation. Both are
|
||||||
|
/// viscous effects.
|
||||||
|
fn parasite_drag_coefficient(&self) -> f32 {
|
||||||
|
// Reference area and drag coefficient assumes best-case scenario of the
|
||||||
|
// orientation producing least amount of drag
|
||||||
|
match self {
|
||||||
|
// Cross-section, head/feet first
|
||||||
|
Body::BipedLarge(_) | Body::BipedSmall(_) | Body::Golem(_) | Body::Humanoid(_) => {
|
||||||
|
let dim = self.dimensions().xy().map(|a| a * 0.5);
|
||||||
|
0.7 * PI * dim.x * dim.y
|
||||||
|
},
|
||||||
|
|
||||||
|
// Cross-section, nose/tail first
|
||||||
|
Body::Theropod(_)
|
||||||
|
| Body::QuadrupedMedium(_)
|
||||||
|
| Body::QuadrupedSmall(_)
|
||||||
|
| Body::QuadrupedLow(_) => {
|
||||||
|
let dim = self.dimensions().map(|a| a * 0.5);
|
||||||
|
let cd = if matches!(self, Body::QuadrupedLow(_)) {
|
||||||
|
0.7
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
};
|
||||||
|
cd * std::f32::consts::PI * dim.x * dim.z
|
||||||
|
},
|
||||||
|
|
||||||
|
// Cross-section, zero-lift angle; exclude the wings (width * 0.2)
|
||||||
|
Body::BirdMedium(_) | Body::BirdSmall(_) | Body::Dragon(_) => {
|
||||||
|
let dim = self.dimensions().map(|a| a * 0.5);
|
||||||
|
let cd = match self {
|
||||||
|
Body::BirdMedium(_) => 0.2,
|
||||||
|
Body::BirdSmall(_) => 0.4,
|
||||||
|
_ => 0.7,
|
||||||
|
};
|
||||||
|
cd * std::f32::consts::PI * dim.x * 0.2 * dim.z
|
||||||
|
},
|
||||||
|
|
||||||
|
// Cross-section, zero-lift angle; exclude the fins (width * 0.2)
|
||||||
|
Body::FishMedium(_) | Body::FishSmall(_) => {
|
||||||
|
let dim = self.dimensions().map(|a| a * 0.5);
|
||||||
|
0.031 * std::f32::consts::PI * dim.x * 0.2 * dim.z
|
||||||
|
},
|
||||||
|
|
||||||
|
Body::Object(object) => match object {
|
||||||
|
// very streamlined objects
|
||||||
|
object::Body::Arrow
|
||||||
|
| object::Body::ArrowSnake
|
||||||
|
| object::Body::FireworkBlue
|
||||||
|
| object::Body::FireworkGreen
|
||||||
|
| object::Body::FireworkPurple
|
||||||
|
| object::Body::FireworkRed
|
||||||
|
| object::Body::FireworkWhite
|
||||||
|
| object::Body::FireworkYellow
|
||||||
|
| object::Body::MultiArrow => {
|
||||||
|
let dim = self.dimensions().map(|a| a * 0.5);
|
||||||
|
0.02 * std::f32::consts::PI * dim.x * dim.z
|
||||||
|
},
|
||||||
|
|
||||||
|
// spherical-ish objects
|
||||||
|
object::Body::BoltFire
|
||||||
|
| object::Body::BoltFireBig
|
||||||
|
| object::Body::BoltNature
|
||||||
|
| object::Body::Bomb
|
||||||
|
| object::Body::PotionBlue
|
||||||
|
| object::Body::PotionGreen
|
||||||
|
| object::Body::PotionRed
|
||||||
|
| object::Body::Pouch
|
||||||
|
| object::Body::Pumpkin
|
||||||
|
| object::Body::Pumpkin2
|
||||||
|
| object::Body::Pumpkin3
|
||||||
|
| object::Body::Pumpkin4
|
||||||
|
| object::Body::Pumpkin5 => {
|
||||||
|
let dim = self.dimensions().map(|a| a * 0.5);
|
||||||
|
0.5 * std::f32::consts::PI * dim.x * dim.z
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
let dim = self.dimensions();
|
||||||
|
2.0 * (std::f32::consts::PI / 6.0 * dim.x * dim.y * dim.z).powf(2.0 / 3.0)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
Body::Ship(_) => {
|
||||||
|
// Airships tend to use the square of the cube root of its volume for
|
||||||
|
// reference area
|
||||||
|
let dim = self.dimensions();
|
||||||
|
(std::f32::consts::PI / 6.0 * dim.x * dim.y * dim.z).powf(2.0 / 3.0)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
## References:
|
||||||
|
|
||||||
|
1. "Field Estimates of Body Drag Coefficient on the Basis of Dives in Passerine Birds",
|
||||||
|
Anders Hedenström and Felix Liechti, 2001
|
||||||
|
2. "A Simple Method to Determine Drag Coefficients in Aquatic Animals",
|
||||||
|
D. Bilo and W. Nachtigall, 1980
|
||||||
|
*/
|
@ -14,6 +14,7 @@ pub mod compass;
|
|||||||
mod controller;
|
mod controller;
|
||||||
pub mod dialogue;
|
pub mod dialogue;
|
||||||
#[cfg(not(target_arch = "wasm32"))] mod energy;
|
#[cfg(not(target_arch = "wasm32"))] mod energy;
|
||||||
|
pub mod fluid_dynamics;
|
||||||
#[cfg(not(target_arch = "wasm32"))] pub mod group;
|
#[cfg(not(target_arch = "wasm32"))] pub mod group;
|
||||||
mod health;
|
mod health;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
@ -67,6 +68,7 @@ pub use self::{
|
|||||||
InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting,
|
InputKind, InventoryAction, InventoryEvent, InventoryManip, MountState, Mounting,
|
||||||
},
|
},
|
||||||
energy::{Energy, EnergyChange, EnergySource},
|
energy::{Energy, EnergyChange, EnergySource},
|
||||||
|
fluid_dynamics::Fluid,
|
||||||
group::Group,
|
group::Group,
|
||||||
home_chunk::HomeChunk,
|
home_chunk::HomeChunk,
|
||||||
inputs::CanBuild,
|
inputs::CanBuild,
|
||||||
@ -79,7 +81,7 @@ pub use self::{
|
|||||||
misc::Object,
|
misc::Object,
|
||||||
ori::Ori,
|
ori::Ori,
|
||||||
phys::{
|
phys::{
|
||||||
Collider, ForceUpdate, Gravity, Mass, PhysicsState, Pos, PosVelDefer, PreviousPhysCache,
|
Collider, Density, ForceUpdate, Mass, PhysicsState, Pos, PosVelDefer, PreviousPhysCache,
|
||||||
Scale, Sticky, Vel,
|
Scale, Sticky, Vel,
|
||||||
},
|
},
|
||||||
player::Player,
|
player::Player,
|
||||||
|
@ -148,6 +148,10 @@ impl Ori {
|
|||||||
self.to_quat() * local
|
self.to_quat() * local
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_horizontal(self) -> Option<Self> {
|
||||||
|
Dir::from_unnormalized(self.look_dir().xy().into()).map(|ori| ori.into())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn pitched_up(self, angle_radians: f32) -> Self {
|
pub fn pitched_up(self, angle_radians: f32) -> Self {
|
||||||
self.rotated(Quaternion::rotation_x(angle_radians))
|
self.rotated(Quaternion::rotation_x(angle_radians))
|
||||||
}
|
}
|
||||||
@ -252,7 +256,7 @@ impl From<Ori> for vek::vec::repr_simd::Vec3<f32> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl From<Ori> for Vec2<f32> {
|
impl From<Ori> for Vec2<f32> {
|
||||||
fn from(ori: Ori) -> Self { ori.look_vec().xy() }
|
fn from(ori: Ori) -> Self { ori.look_dir().to_horizontal().unwrap_or_default().xy() }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Ori> for vek::vec::repr_simd::Vec2<f32> {
|
impl From<Ori> for vek::vec::repr_simd::Vec2<f32> {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::uid::Uid;
|
use super::Fluid;
|
||||||
|
use crate::{consts::WATER_DENSITY, uid::Uid};
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use specs::{Component, DerefFlaggedStorage, NullStorage};
|
use specs::{Component, DerefFlaggedStorage, NullStorage};
|
||||||
@ -19,6 +20,10 @@ impl Component for Pos {
|
|||||||
#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Vel(pub Vec3<f32>);
|
pub struct Vel(pub Vec3<f32>);
|
||||||
|
|
||||||
|
impl Vel {
|
||||||
|
pub fn zero() -> Self { Vel(Vec3::zero()) }
|
||||||
|
}
|
||||||
|
|
||||||
impl Component for Vel {
|
impl Component for Vel {
|
||||||
// TODO: why not regular vec storage????
|
// TODO: why not regular vec storage????
|
||||||
type Storage = IdvStorage<Self>;
|
type Storage = IdvStorage<Self>;
|
||||||
@ -66,13 +71,30 @@ impl Component for Scale {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mass
|
// Mass
|
||||||
#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Mass(pub f32);
|
pub struct Mass(pub f32);
|
||||||
|
|
||||||
|
impl Default for Mass {
|
||||||
|
fn default() -> Mass { Mass(1.0) }
|
||||||
|
}
|
||||||
|
|
||||||
impl Component for Mass {
|
impl Component for Mass {
|
||||||
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The average density (specific mass) of an entity.
|
||||||
|
/// Units used for reference is kg/m³
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Density(pub f32);
|
||||||
|
|
||||||
|
impl Default for Density {
|
||||||
|
fn default() -> Density { Density(WATER_DENSITY) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Density {
|
||||||
|
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
||||||
|
}
|
||||||
|
|
||||||
// Collider
|
// Collider
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum Collider {
|
pub enum Collider {
|
||||||
@ -92,6 +114,11 @@ impl Collider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_height(&self) -> f32 {
|
||||||
|
let (z_min, z_max) = self.get_z_limits(1.0);
|
||||||
|
z_max - z_min
|
||||||
|
}
|
||||||
|
|
||||||
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::Voxel { .. } => (0.0, 1.0),
|
||||||
@ -105,13 +132,6 @@ impl Component for Collider {
|
|||||||
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct Gravity(pub f32);
|
|
||||||
|
|
||||||
impl Component for Gravity {
|
|
||||||
type Storage = DerefFlaggedStorage<Self, IdvStorage<Self>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Sticky;
|
pub struct Sticky;
|
||||||
|
|
||||||
@ -126,7 +146,7 @@ pub struct PhysicsState {
|
|||||||
pub on_ceiling: bool,
|
pub on_ceiling: bool,
|
||||||
pub on_wall: Option<Vec3<f32>>,
|
pub on_wall: Option<Vec3<f32>>,
|
||||||
pub touch_entities: HashSet<Uid>,
|
pub touch_entities: HashSet<Uid>,
|
||||||
pub in_liquid: Option<f32>, // Depth
|
pub in_fluid: Option<Fluid>,
|
||||||
pub ground_vel: Vec3<f32>,
|
pub ground_vel: Vec3<f32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,6 +169,8 @@ impl PhysicsState {
|
|||||||
.or_else(|| self.on_ceiling.then_some(Vec3::unit_z()))
|
.or_else(|| self.on_ceiling.then_some(Vec3::unit_z()))
|
||||||
.or(self.on_wall)
|
.or(self.on_wall)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn in_liquid(&self) -> Option<f32> { self.in_fluid.and_then(|fluid| fluid.depth()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for PhysicsState {
|
impl Component for PhysicsState {
|
||||||
|
@ -2,5 +2,17 @@
|
|||||||
pub const MAX_PICKUP_RANGE: f32 = 8.0;
|
pub const MAX_PICKUP_RANGE: f32 = 8.0;
|
||||||
pub const MAX_MOUNT_RANGE: f32 = 14.0;
|
pub const MAX_MOUNT_RANGE: f32 = 14.0;
|
||||||
|
|
||||||
pub const GRAVITY: f32 = 9.81 * 5.0;
|
pub const GRAVITY: f32 = 25.0;
|
||||||
pub const FRIC_GROUND: f32 = 0.15;
|
pub const FRIC_GROUND: f32 = 0.15;
|
||||||
|
|
||||||
|
// Values for air taken from http://www-mdp.eng.cam.ac.uk/web/library/enginfo/aerothermal_dvd_only/aero/atmos/atmos.html
|
||||||
|
// Values below are for dry air at 15°C, sea level, 1 standard atmosphere
|
||||||
|
|
||||||
|
// pub const ATMOSPHERE: f32 = 101325.0; // Pa
|
||||||
|
|
||||||
|
// kg/m³
|
||||||
|
pub const AIR_DENSITY: f32 = 1.225;
|
||||||
|
pub const WATER_DENSITY: f32 = 999.1026;
|
||||||
|
pub const IRON_DENSITY: f32 = 7870.0;
|
||||||
|
// pub const HUMAN_DENSITY: f32 = 1010.0; // real value
|
||||||
|
pub const HUMAN_DENSITY: f32 = 990.0; // value we use to make humanoids gently float
|
||||||
|
@ -63,7 +63,6 @@ pub enum ServerEvent {
|
|||||||
body: comp::Body,
|
body: comp::Body,
|
||||||
light: Option<comp::LightEmitter>,
|
light: Option<comp::LightEmitter>,
|
||||||
projectile: comp::Projectile,
|
projectile: comp::Projectile,
|
||||||
gravity: Option<comp::Gravity>,
|
|
||||||
speed: f32,
|
speed: f32,
|
||||||
object: Option<comp::Object>,
|
object: Option<comp::Object>,
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::{Body, CharacterState, Gravity, LightEmitter, ProjectileConstructor, StateUpdate},
|
comp::{Body, CharacterState, LightEmitter, ProjectileConstructor, StateUpdate},
|
||||||
event::ServerEvent,
|
event::ServerEvent,
|
||||||
states::{
|
states::{
|
||||||
behavior::{CharacterBehavior, JoinData},
|
behavior::{CharacterBehavior, JoinData},
|
||||||
@ -20,7 +20,6 @@ pub struct StaticData {
|
|||||||
pub projectile: ProjectileConstructor,
|
pub projectile: ProjectileConstructor,
|
||||||
pub projectile_body: Body,
|
pub projectile_body: Body,
|
||||||
pub projectile_light: Option<LightEmitter>,
|
pub projectile_light: Option<LightEmitter>,
|
||||||
pub projectile_gravity: Option<Gravity>,
|
|
||||||
pub projectile_speed: f32,
|
pub projectile_speed: f32,
|
||||||
/// What key is used to press ability
|
/// What key is used to press ability
|
||||||
pub ability_info: AbilityInfo,
|
pub ability_info: AbilityInfo,
|
||||||
@ -82,7 +81,6 @@ impl CharacterBehavior for Data {
|
|||||||
body: self.static_data.projectile_body,
|
body: self.static_data.projectile_body,
|
||||||
projectile,
|
projectile,
|
||||||
light: self.static_data.projectile_light,
|
light: self.static_data.projectile_light,
|
||||||
gravity: self.static_data.projectile_gravity,
|
|
||||||
speed: self.static_data.projectile_speed,
|
speed: self.static_data.projectile_speed,
|
||||||
object: None,
|
object: None,
|
||||||
});
|
});
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::{
|
comp::{
|
||||||
self, item::MaterialStatManifest, Beam, Body, CharacterState, Combo, ControlAction,
|
self, item::MaterialStatManifest, Beam, Body, CharacterState, Combo, ControlAction,
|
||||||
Controller, ControllerInputs, Energy, Health, InputAttr, InputKind, Inventory,
|
Controller, ControllerInputs, Density, Energy, Health, InputAttr, InputKind, Inventory,
|
||||||
InventoryAction, Melee, Ori, PhysicsState, Pos, SkillSet, StateUpdate, Stats, Vel,
|
InventoryAction, Mass, Melee, Ori, PhysicsState, Pos, SkillSet, StateUpdate, Stats, Vel,
|
||||||
},
|
},
|
||||||
resources::DeltaTime,
|
resources::DeltaTime,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
@ -80,6 +80,8 @@ pub struct JoinData<'a> {
|
|||||||
pub pos: &'a Pos,
|
pub pos: &'a Pos,
|
||||||
pub vel: &'a Vel,
|
pub vel: &'a Vel,
|
||||||
pub ori: &'a Ori,
|
pub ori: &'a Ori,
|
||||||
|
pub mass: &'a Mass,
|
||||||
|
pub density: &'a Density,
|
||||||
pub dt: &'a DeltaTime,
|
pub dt: &'a DeltaTime,
|
||||||
pub controller: &'a Controller,
|
pub controller: &'a Controller,
|
||||||
pub inputs: &'a ControllerInputs,
|
pub inputs: &'a ControllerInputs,
|
||||||
@ -113,6 +115,8 @@ pub struct JoinStruct<'a> {
|
|||||||
pub pos: &'a mut Pos,
|
pub pos: &'a mut Pos,
|
||||||
pub vel: &'a mut Vel,
|
pub vel: &'a mut Vel,
|
||||||
pub ori: &'a mut Ori,
|
pub ori: &'a mut Ori,
|
||||||
|
pub mass: &'a Mass,
|
||||||
|
pub density: &'a mut Density,
|
||||||
pub energy: RestrictedMut<'a, Energy>,
|
pub energy: RestrictedMut<'a, Energy>,
|
||||||
pub inventory: RestrictedMut<'a, Inventory>,
|
pub inventory: RestrictedMut<'a, Inventory>,
|
||||||
pub controller: &'a mut Controller,
|
pub controller: &'a mut Controller,
|
||||||
@ -141,6 +145,8 @@ impl<'a> JoinData<'a> {
|
|||||||
pos: j.pos,
|
pos: j.pos,
|
||||||
vel: j.vel,
|
vel: j.vel,
|
||||||
ori: j.ori,
|
ori: j.ori,
|
||||||
|
mass: j.mass,
|
||||||
|
density: j.density,
|
||||||
energy: j.energy.get_unchecked(),
|
energy: j.energy.get_unchecked(),
|
||||||
inventory: j.inventory.get_unchecked(),
|
inventory: j.inventory.get_unchecked(),
|
||||||
controller: j.controller,
|
controller: j.controller,
|
||||||
|
@ -4,8 +4,8 @@ use crate::{
|
|||||||
DamageSource, GroupTarget, Knockback, KnockbackDir,
|
DamageSource, GroupTarget, Knockback, KnockbackDir,
|
||||||
},
|
},
|
||||||
comp::{
|
comp::{
|
||||||
projectile, Body, CharacterState, EnergyChange, EnergySource, Gravity, LightEmitter,
|
projectile, Body, CharacterState, EnergyChange, EnergySource, LightEmitter, Projectile,
|
||||||
Projectile, StateUpdate,
|
StateUpdate,
|
||||||
},
|
},
|
||||||
event::ServerEvent,
|
event::ServerEvent,
|
||||||
states::{
|
states::{
|
||||||
@ -40,7 +40,6 @@ pub struct StaticData {
|
|||||||
/// Projectile information
|
/// Projectile information
|
||||||
pub projectile_body: Body,
|
pub projectile_body: Body,
|
||||||
pub projectile_light: Option<LightEmitter>,
|
pub projectile_light: Option<LightEmitter>,
|
||||||
pub projectile_gravity: Option<Gravity>,
|
|
||||||
pub initial_projectile_speed: f32,
|
pub initial_projectile_speed: f32,
|
||||||
pub scaled_projectile_speed: f32,
|
pub scaled_projectile_speed: f32,
|
||||||
/// Move speed efficiency
|
/// Move speed efficiency
|
||||||
@ -138,7 +137,6 @@ impl CharacterBehavior for Data {
|
|||||||
body: self.static_data.projectile_body,
|
body: self.static_data.projectile_body,
|
||||||
projectile,
|
projectile,
|
||||||
light: self.static_data.projectile_light,
|
light: self.static_data.projectile_light,
|
||||||
gravity: self.static_data.projectile_gravity,
|
|
||||||
speed: self.static_data.initial_projectile_speed
|
speed: self.static_data.initial_projectile_speed
|
||||||
+ charge_frac * self.static_data.scaled_projectile_speed,
|
+ charge_frac * self.static_data.scaled_projectile_speed,
|
||||||
object: None,
|
object: None,
|
||||||
|
@ -64,12 +64,15 @@ impl CharacterBehavior for Data {
|
|||||||
) {
|
) {
|
||||||
(wall_dir, climb)
|
(wall_dir, climb)
|
||||||
} else {
|
} else {
|
||||||
if input_is_pressed(data, InputKind::Jump) {
|
if let Some(impulse) = input_is_pressed(data, InputKind::Jump)
|
||||||
|
.then(|| data.body.jump_impulse())
|
||||||
|
.flatten()
|
||||||
|
{
|
||||||
// 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, BASE_JUMP_IMPULSE * 0.5));
|
.push_front(LocalEvent::Jump(data.entity, 0.5 * impulse / data.mass.0));
|
||||||
}
|
};
|
||||||
update.character = CharacterState::Idle {};
|
update.character = CharacterState::Idle {};
|
||||||
return update;
|
return update;
|
||||||
};
|
};
|
||||||
|
@ -7,11 +7,11 @@ use crate::{
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use vek::Vec2;
|
use vek::Vec2;
|
||||||
|
|
||||||
// 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 = 5.0;
|
const GLIDE_ACCEL: f32 = 5.0;
|
||||||
|
const GLIDE_MAX_SPEED: f32 = 30.0;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Data;
|
pub struct Data;
|
||||||
|
|
||||||
impl CharacterBehavior for Data {
|
impl CharacterBehavior for Data {
|
||||||
@ -25,7 +25,7 @@ impl CharacterBehavior for Data {
|
|||||||
}
|
}
|
||||||
if data
|
if data
|
||||||
.physics
|
.physics
|
||||||
.in_liquid
|
.in_liquid()
|
||||||
.map(|depth| depth > 0.5)
|
.map(|depth| depth > 0.5)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
@ -34,21 +34,21 @@ impl CharacterBehavior for Data {
|
|||||||
if data.inventory.equipped(EquipSlot::Glider).is_none() {
|
if data.inventory.equipped(EquipSlot::Glider).is_none() {
|
||||||
update.character = CharacterState::Idle
|
update.character = CharacterState::Idle
|
||||||
};
|
};
|
||||||
// If there is a wall in front of character and they are trying to climb go to
|
|
||||||
// climb
|
let horiz_vel = Vec2::<f32>::from(update.vel.0);
|
||||||
handle_climb(&data, &mut update);
|
let horiz_speed_sq = horiz_vel.magnitude_squared();
|
||||||
|
|
||||||
// Move player according to movement direction vector
|
// Move player according to movement direction vector
|
||||||
update.vel.0 += Vec2::broadcast(data.dt.0) * data.inputs.move_dir * GLIDE_ACCEL;
|
if horiz_speed_sq < GLIDE_MAX_SPEED.powi(2) {
|
||||||
|
update.vel.0 += Vec2::broadcast(data.dt.0) * data.inputs.move_dir * GLIDE_ACCEL;
|
||||||
|
}
|
||||||
|
|
||||||
// Determine orientation vector from movement direction vector
|
// Determine orientation vector from movement direction vector
|
||||||
let horiz_vel = Vec2::<f32>::from(update.vel.0);
|
|
||||||
if let Some(dir) = Dir::from_unnormalized(update.vel.0) {
|
if let Some(dir) = Dir::from_unnormalized(update.vel.0) {
|
||||||
update.ori = update.ori.slerped_towards(Ori::from(dir), 2.0 * data.dt.0);
|
update.ori = update.ori.slerped_towards(Ori::from(dir), 2.0 * data.dt.0);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Apply Glide antigrav lift
|
// Apply Glide antigrav lift
|
||||||
let horiz_speed_sq = horiz_vel.magnitude_squared();
|
|
||||||
if 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);
|
||||||
@ -56,6 +56,10 @@ impl CharacterBehavior for Data {
|
|||||||
update.vel.0.z += lift * data.dt.0;
|
update.vel.0.z += lift * data.dt.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there is a wall in front of character and they are trying to climb go to
|
||||||
|
// climb
|
||||||
|
handle_climb(&data, &mut update);
|
||||||
|
|
||||||
update
|
update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ impl CharacterBehavior for Data {
|
|||||||
}
|
}
|
||||||
if data
|
if data
|
||||||
.physics
|
.physics
|
||||||
.in_liquid
|
.in_liquid()
|
||||||
.map(|depth| depth > 0.5)
|
.map(|depth| depth > 0.5)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::{Body, CharacterState, Gravity, LightEmitter, ProjectileConstructor, StateUpdate},
|
comp::{Body, CharacterState, LightEmitter, ProjectileConstructor, StateUpdate},
|
||||||
event::ServerEvent,
|
event::ServerEvent,
|
||||||
states::{
|
states::{
|
||||||
behavior::{CharacterBehavior, JoinData},
|
behavior::{CharacterBehavior, JoinData},
|
||||||
@ -28,7 +28,6 @@ pub struct StaticData {
|
|||||||
pub projectile: ProjectileConstructor,
|
pub projectile: ProjectileConstructor,
|
||||||
pub projectile_body: Body,
|
pub projectile_body: Body,
|
||||||
pub projectile_light: Option<LightEmitter>,
|
pub projectile_light: Option<LightEmitter>,
|
||||||
pub projectile_gravity: Option<Gravity>,
|
|
||||||
pub projectile_speed: f32,
|
pub projectile_speed: f32,
|
||||||
/// What key is used to press ability
|
/// What key is used to press ability
|
||||||
pub ability_info: AbilityInfo,
|
pub ability_info: AbilityInfo,
|
||||||
@ -155,7 +154,6 @@ impl CharacterBehavior for Data {
|
|||||||
body: self.static_data.projectile_body,
|
body: self.static_data.projectile_body,
|
||||||
projectile,
|
projectile,
|
||||||
light: self.static_data.projectile_light,
|
light: self.static_data.projectile_light,
|
||||||
gravity: self.static_data.projectile_gravity,
|
|
||||||
speed: self.static_data.projectile_speed,
|
speed: self.static_data.projectile_speed,
|
||||||
object: None,
|
object: None,
|
||||||
});
|
});
|
||||||
|
@ -5,8 +5,8 @@ use crate::{
|
|||||||
item::{Hands, ItemKind, Tool, ToolKind},
|
item::{Hands, ItemKind, Tool, ToolKind},
|
||||||
quadruped_low, quadruped_medium, quadruped_small, ship,
|
quadruped_low, quadruped_medium, quadruped_small, ship,
|
||||||
skills::{Skill, SwimSkill},
|
skills::{Skill, SwimSkill},
|
||||||
theropod, Body, CharacterAbility, CharacterState, InputAttr, InputKind, InventoryAction,
|
theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind,
|
||||||
StateUpdate,
|
InventoryAction, StateUpdate,
|
||||||
},
|
},
|
||||||
consts::{FRIC_GROUND, GRAVITY},
|
consts::{FRIC_GROUND, GRAVITY},
|
||||||
event::{LocalEvent, ServerEvent},
|
event::{LocalEvent, ServerEvent},
|
||||||
@ -18,23 +18,6 @@ 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 = 2.0;
|
|
||||||
const BASE_FLIGHT_ACCEL: f32 = 2.0;
|
|
||||||
const BASE_HUMANOID_WATER_ACCEL: f32 = 150.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 ROLL_SPEED: f32 = 17.0;
|
|
||||||
// const CHARGE_SPEED: f32 = 20.0;
|
|
||||||
// const GLIDE_ACCEL: f32 = 15.0;
|
|
||||||
// const GLIDE_SPEED: f32 = 45.0;
|
|
||||||
// const BLOCK_ACCEL: f32 = 30.0;
|
|
||||||
// const BLOCK_SPEED: f32 = 75.0;
|
|
||||||
// Gravity is 9.81 * 4, so this makes gravity equal to .15 //TODO: <- is wrong
|
|
||||||
//
|
|
||||||
// const GLIDE_ANTIGRAV: f32 = GRAVITY * 0.96;
|
|
||||||
// const CLIMB_SPEED: f32 = 5.0;
|
|
||||||
// const CLIMB_COST: i32 = 5;
|
|
||||||
|
|
||||||
impl Body {
|
impl Body {
|
||||||
pub fn base_accel(&self) -> f32 {
|
pub fn base_accel(&self) -> f32 {
|
||||||
@ -119,7 +102,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,
|
Body::Ship(_) => 0.0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,8 +111,7 @@ impl Body {
|
|||||||
pub fn max_speed_approx(&self) -> f32 {
|
pub fn max_speed_approx(&self) -> f32 {
|
||||||
// Inverse kinematics: at what velocity will acceleration
|
// Inverse kinematics: at what velocity will acceleration
|
||||||
// be cancelled out by friction drag?
|
// be cancelled out by friction drag?
|
||||||
// Note: we assume no air (this is fine, current physics
|
// Note: we assume no air, since it's such a small factor.
|
||||||
// uses max(air_drag, ground_drag)).
|
|
||||||
// Derived via...
|
// Derived via...
|
||||||
// v = (v + dv / 30) * (1 - drag).powi(2) (accel cancels drag)
|
// v = (v + dv / 30) * (1 - drag).powi(2) (accel cancels drag)
|
||||||
// => 1 = (1 + (dv / 30) / v) * (1 - drag).powi(2)
|
// => 1 = (1 + (dv / 30) / v) * (1 - drag).powi(2)
|
||||||
@ -142,53 +124,94 @@ impl Body {
|
|||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The turn rate in 180°/s (or (rotations per second)/2)
|
||||||
pub fn base_ori_rate(&self) -> f32 {
|
pub fn base_ori_rate(&self) -> f32 {
|
||||||
match self {
|
match self {
|
||||||
Body::Humanoid(_) => 20.0,
|
Body::Humanoid(_) => 4.0,
|
||||||
Body::QuadrupedSmall(_) => 15.0,
|
Body::QuadrupedSmall(_) => 3.0,
|
||||||
Body::QuadrupedMedium(_) => 8.0,
|
Body::QuadrupedMedium(_) => 1.6,
|
||||||
Body::BirdMedium(_) => 30.0,
|
Body::BirdMedium(_) => 6.0,
|
||||||
Body::FishMedium(_) => 5.0,
|
Body::FishMedium(_) => 6.0,
|
||||||
Body::Dragon(_) => 5.0,
|
Body::Dragon(_) => 1.0,
|
||||||
Body::BirdSmall(_) => 35.0,
|
Body::BirdSmall(_) => 7.0,
|
||||||
Body::FishSmall(_) => 10.0,
|
Body::FishSmall(_) => 7.0,
|
||||||
Body::BipedLarge(_) => 8.0,
|
Body::BipedLarge(_) => 1.6,
|
||||||
Body::BipedSmall(_) => 12.0,
|
Body::BipedSmall(_) => 2.4,
|
||||||
Body::Object(_) => 10.0,
|
Body::Object(_) => 2.0,
|
||||||
Body::Golem(_) => 8.0,
|
Body::Golem(_) => 0.8,
|
||||||
Body::Theropod(theropod) => match theropod.species {
|
Body::Theropod(theropod) => match theropod.species {
|
||||||
theropod::Species::Archaeos => 2.5,
|
theropod::Species::Archaeos => 0.5,
|
||||||
theropod::Species::Odonto => 2.5,
|
theropod::Species::Odonto => 0.5,
|
||||||
theropod::Species::Ntouka => 2.5,
|
theropod::Species::Ntouka => 0.5,
|
||||||
_ => 7.0,
|
_ => 1.4,
|
||||||
},
|
},
|
||||||
Body::QuadrupedLow(quadruped_low) => match quadruped_low.species {
|
Body::QuadrupedLow(quadruped_low) => match quadruped_low.species {
|
||||||
quadruped_low::Species::Monitor => 9.0,
|
quadruped_low::Species::Monitor => 1.8,
|
||||||
quadruped_low::Species::Asp => 8.0,
|
quadruped_low::Species::Asp => 1.6,
|
||||||
quadruped_low::Species::Tortoise => 3.0,
|
quadruped_low::Species::Tortoise => 0.6,
|
||||||
quadruped_low::Species::Rocksnapper => 4.0,
|
quadruped_low::Species::Rocksnapper => 0.8,
|
||||||
quadruped_low::Species::Maneater => 5.0,
|
quadruped_low::Species::Maneater => 1.0,
|
||||||
quadruped_low::Species::Lavadrake => 4.0,
|
quadruped_low::Species::Lavadrake => 0.8,
|
||||||
_ => 6.0,
|
_ => 1.2,
|
||||||
},
|
},
|
||||||
Body::Ship(_) => 0.175,
|
Body::Ship(_) => 0.035,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns flying speed if the body type can fly, otherwise None
|
/// Returns thrust force if the body type can swim, otherwise None
|
||||||
pub fn can_fly(&self) -> Option<f32> {
|
pub fn swim_thrust(&self) -> Option<f32> {
|
||||||
match self {
|
match self {
|
||||||
Body::BirdMedium(_) | Body::Dragon(_) | Body::BirdSmall(_) => Some(1.0),
|
Body::Object(_) | Body::Ship(_) => None,
|
||||||
Body::Ship(ship::Body::DefaultAirship) => Some(1.0),
|
Body::BipedLarge(_) | Body::Golem(_) => Some(200.0 * self.mass().0),
|
||||||
|
Body::BipedSmall(_) => Some(100.0 * self.mass().0),
|
||||||
|
Body::BirdMedium(_) => Some(50.0 * self.mass().0),
|
||||||
|
Body::BirdSmall(_) => Some(50.0 * self.mass().0),
|
||||||
|
Body::FishMedium(_) => Some(50.0 * self.mass().0),
|
||||||
|
Body::FishSmall(_) => Some(50.0 * self.mass().0),
|
||||||
|
Body::Dragon(_) => Some(200.0 * self.mass().0),
|
||||||
|
Body::Humanoid(_) => Some(200.0 * self.mass().0),
|
||||||
|
Body::Theropod(body) => match body.species {
|
||||||
|
theropod::Species::Sandraptor
|
||||||
|
| theropod::Species::Snowraptor
|
||||||
|
| theropod::Species::Sunlizard
|
||||||
|
| theropod::Species::Woodraptor
|
||||||
|
| theropod::Species::Yale => Some(200.0 * self.mass().0),
|
||||||
|
_ => Some(100.0 * self.mass().0),
|
||||||
|
},
|
||||||
|
Body::QuadrupedLow(_) => Some(300.0 * self.mass().0),
|
||||||
|
Body::QuadrupedMedium(_) => Some(300.0 * self.mass().0),
|
||||||
|
Body::QuadrupedSmall(_) => Some(300.0 * self.mass().0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns thrust force if the body type can fly, otherwise None
|
||||||
|
pub fn fly_thrust(&self) -> Option<f32> {
|
||||||
|
match self {
|
||||||
|
Body::BirdMedium(_) => Some(GRAVITY * self.mass().0 * 2.0),
|
||||||
|
Body::BirdSmall(_) => Some(GRAVITY * self.mass().0 * 2.0),
|
||||||
|
Body::Dragon(_) => Some(200_000.0),
|
||||||
|
Body::Ship(ship::Body::DefaultAirship) => Some(300_000.0),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns jump impulse if the body type can jump, otherwise None
|
||||||
pub fn jump_impulse(&self) -> Option<f32> {
|
pub fn jump_impulse(&self) -> Option<f32> {
|
||||||
match self {
|
match self {
|
||||||
Body::Object(_) | Body::Ship(_) => None,
|
Body::Object(_) | Body::Ship(_) => None,
|
||||||
_ => Some(BASE_JUMP_IMPULSE),
|
Body::BipedLarge(_) | Body::Dragon(_) | Body::Golem(_) | Body::QuadrupedLow(_) => {
|
||||||
|
Some(0.1 * self.mass().0)
|
||||||
|
},
|
||||||
|
Body::QuadrupedMedium(_) => Some(0.4 * self.mass().0),
|
||||||
|
Body::Theropod(body) => match body.species {
|
||||||
|
theropod::Species::Snowraptor
|
||||||
|
| theropod::Species::Sandraptor
|
||||||
|
| theropod::Species::Woodraptor => Some(0.4 * self.mass().0),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => Some(0.4 * self.mass().0),
|
||||||
}
|
}
|
||||||
|
.map(|f| f * GRAVITY)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_climb(&self) -> bool { matches!(self, Body::Humanoid(_)) }
|
pub fn can_climb(&self) -> bool { matches!(self, Body::Humanoid(_)) }
|
||||||
@ -196,21 +219,22 @@ impl Body {
|
|||||||
|
|
||||||
/// Handles updating `Components` to move player based on state of `JoinData`
|
/// Handles updating `Components` to move player based on state of `JoinData`
|
||||||
pub fn handle_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
pub fn handle_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
||||||
if let Some(depth) = data.physics.in_liquid {
|
let submersion = data
|
||||||
swim_move(data, update, efficiency, depth);
|
.physics
|
||||||
} else if input_is_pressed(data, InputKind::Fly)
|
.in_liquid()
|
||||||
|
.map(|depth| depth / data.body.height());
|
||||||
|
|
||||||
|
if input_is_pressed(data, InputKind::Fly)
|
||||||
|
&& submersion.map_or(true, |sub| sub < 1.0)
|
||||||
&& (!data.physics.on_ground || data.body.jump_impulse().is_none())
|
&& (!data.physics.on_ground || data.body.jump_impulse().is_none())
|
||||||
&& data.body.can_fly().is_some()
|
&& data.body.fly_thrust().is_some()
|
||||||
{
|
{
|
||||||
fly_move(
|
fly_move(data, update, efficiency);
|
||||||
data,
|
} else if let Some(submersion) = (!data.physics.on_ground && data.body.swim_thrust().is_some())
|
||||||
update,
|
.then_some(submersion)
|
||||||
efficiency
|
.flatten()
|
||||||
* data
|
{
|
||||||
.body
|
swim_move(data, update, efficiency, submersion);
|
||||||
.can_fly()
|
|
||||||
.expect("can_fly is_some right above this"),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
basic_move(data, update, efficiency);
|
basic_move(data, update, efficiency);
|
||||||
}
|
}
|
||||||
@ -219,16 +243,23 @@ pub fn handle_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
|||||||
/// Updates components to move player as if theyre on ground or in air
|
/// Updates components to move player as if theyre on ground or in air
|
||||||
#[allow(clippy::assign_op_pattern)] // TODO: Pending review in #587
|
#[allow(clippy::assign_op_pattern)] // TODO: Pending review in #587
|
||||||
fn basic_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
fn basic_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
||||||
let accel = if data.physics.on_ground {
|
handle_orientation(data, update, efficiency);
|
||||||
data.body.base_accel()
|
|
||||||
} else {
|
|
||||||
BASE_HUMANOID_AIR_ACCEL
|
|
||||||
};
|
|
||||||
|
|
||||||
update.vel.0 =
|
if let Some(accel) = data
|
||||||
update.vel.0 + Vec2::broadcast(data.dt.0) * data.inputs.move_dir * accel * efficiency;
|
.physics
|
||||||
|
.on_ground
|
||||||
handle_orientation(data, update, data.body.base_ori_rate());
|
.then_some(data.body.base_accel() * efficiency)
|
||||||
|
{
|
||||||
|
// Should ability to backpedal be separate from ability to strafe?
|
||||||
|
update.vel.0 += Vec2::broadcast(data.dt.0)
|
||||||
|
* accel
|
||||||
|
* if data.body.can_strafe() {
|
||||||
|
data.inputs.move_dir
|
||||||
|
} else {
|
||||||
|
let fw = Vec2::from(update.ori);
|
||||||
|
fw * data.inputs.move_dir.dot(fw).max(0.0)
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handles forced movement
|
/// Handles forced movement
|
||||||
@ -238,17 +269,15 @@ pub fn handle_forced_movement(
|
|||||||
movement: ForcedMovement,
|
movement: ForcedMovement,
|
||||||
efficiency: f32,
|
efficiency: f32,
|
||||||
) {
|
) {
|
||||||
|
handle_orientation(data, update, efficiency);
|
||||||
|
|
||||||
match movement {
|
match movement {
|
||||||
ForcedMovement::Forward { strength } => {
|
ForcedMovement::Forward { strength } => {
|
||||||
let accel = if data.physics.on_ground {
|
if let Some(accel) = data.physics.on_ground.then_some(data.body.base_accel()) {
|
||||||
data.body.base_accel()
|
update.vel.0 += Vec2::broadcast(data.dt.0)
|
||||||
} else {
|
* accel
|
||||||
BASE_HUMANOID_AIR_ACCEL
|
* (data.inputs.move_dir * efficiency + Vec2::from(update.ori) * strength);
|
||||||
};
|
}
|
||||||
|
|
||||||
update.vel.0 += Vec2::broadcast(data.dt.0)
|
|
||||||
* accel
|
|
||||||
* (data.inputs.move_dir * efficiency + Vec2::from(update.ori) * strength);
|
|
||||||
},
|
},
|
||||||
ForcedMovement::Leap {
|
ForcedMovement::Leap {
|
||||||
vertical,
|
vertical,
|
||||||
@ -268,86 +297,129 @@ pub fn handle_forced_movement(
|
|||||||
// Apply direction
|
// Apply direction
|
||||||
+ Vec3::from(dir)
|
+ Vec3::from(dir)
|
||||||
// Multiply by forward leap strength
|
// Multiply by forward leap strength
|
||||||
* forward
|
* forward
|
||||||
// Control forward movement based on look direction.
|
// Control forward movement based on look direction.
|
||||||
// This allows players to stop moving forward when they
|
// This allows players to stop moving forward when they
|
||||||
// look downward at target
|
// look downward at target
|
||||||
* (1.0 - data.inputs.look_dir.z.abs());
|
* (1.0 - data.inputs.look_dir.z.abs());
|
||||||
},
|
},
|
||||||
ForcedMovement::Hover { move_input } => {
|
ForcedMovement::Hover { move_input } => {
|
||||||
update.vel.0 = Vec3::new(data.vel.0.x, data.vel.0.y, 0.0)
|
update.vel.0 = Vec3::new(data.vel.0.x, data.vel.0.y, 0.0)
|
||||||
+ move_input * data.inputs.move_dir.try_normalized().unwrap_or_default();
|
+ move_input * data.inputs.move_dir.try_normalized().unwrap_or_default();
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
handle_orientation(data, update, data.body.base_ori_rate() * efficiency);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn handle_orientation(data: &JoinData, update: &mut StateUpdate, rate: f32) {
|
pub fn handle_orientation(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
|
||||||
// Set direction based on move direction
|
let strafe_aim = update.character.is_aimed() && data.body.can_strafe();
|
||||||
let ori_dir = if (update.character.is_aimed() && data.body.can_strafe())
|
if let Some(dir) = (strafe_aim || update.character.is_attack())
|
||||||
|| update.character.is_attack()
|
.then(|| data.inputs.look_dir.to_horizontal().unwrap_or_default())
|
||||||
|
.or_else(|| Dir::from_unnormalized(data.inputs.move_dir.into()))
|
||||||
{
|
{
|
||||||
data.inputs.look_dir.xy()
|
let rate = {
|
||||||
} else if !data.inputs.move_dir.is_approx_zero() {
|
let angle = update.ori.look_dir().angle_between(*dir);
|
||||||
data.inputs.move_dir
|
data.body.base_ori_rate() * efficiency * std::f32::consts::PI / angle
|
||||||
} else {
|
};
|
||||||
update.ori.into()
|
update.ori = update
|
||||||
|
.ori
|
||||||
|
.slerped_towards(dir.into(), (data.dt.0 * rate).min(0.1));
|
||||||
};
|
};
|
||||||
|
|
||||||
// Smooth orientation
|
|
||||||
update.ori = Dir::slerp_to_vec3(update.ori.look_dir(), ori_dir.into(), rate * data.dt.0).into();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updates components to move player as if theyre swimming
|
/// Updates components to move player as if theyre swimming
|
||||||
fn swim_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32, depth: f32) {
|
fn swim_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32, submersion: f32) -> bool {
|
||||||
let mut water_accel = BASE_HUMANOID_WATER_ACCEL;
|
if let Some(force) = data.body.swim_thrust() {
|
||||||
let mut water_speed = BASE_HUMANOID_WATER_SPEED;
|
handle_orientation(data, update, efficiency * 0.2);
|
||||||
if let Ok(Some(level)) = data.skill_set.skill_level(Skill::Swim(SwimSkill::Speed)) {
|
|
||||||
water_speed *= 1.4_f32.powi(level.into());
|
|
||||||
water_accel *= 1.4_f32.powi(level.into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update velocity
|
let force = efficiency * force;
|
||||||
update.vel.0 += Vec2::broadcast(data.dt.0)
|
let mut water_accel = force / data.mass.0;
|
||||||
* data.inputs.move_dir
|
|
||||||
* if update.vel.0.magnitude_squared() < water_speed.powi(2) {
|
if let Ok(Some(level)) = data.skill_set.skill_level(Skill::Swim(SwimSkill::Speed)) {
|
||||||
water_accel
|
water_accel *= 1.4_f32.powi(level.into());
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
}
|
}
|
||||||
* efficiency;
|
|
||||||
|
|
||||||
handle_orientation(
|
let dir = if data.body.can_strafe() {
|
||||||
data,
|
data.inputs.move_dir
|
||||||
update,
|
} else {
|
||||||
data.body.base_ori_rate() * if data.physics.on_ground { 0.5 } else { 0.1 },
|
let fw = Vec2::from(update.ori);
|
||||||
);
|
fw * data.inputs.move_dir.dot(fw).max(0.0)
|
||||||
|
};
|
||||||
|
|
||||||
// Swim
|
// Autoswim to stay afloat
|
||||||
update.vel.0.z = (update.vel.0.z
|
let move_z = if submersion < 1.0 && data.inputs.move_z.abs() < std::f32::EPSILON {
|
||||||
+ data.dt.0
|
(submersion - 0.1).max(0.0)
|
||||||
* GRAVITY
|
} else {
|
||||||
* 4.0
|
data.inputs.move_z
|
||||||
* data
|
};
|
||||||
.inputs
|
|
||||||
.move_z
|
update.vel.0 += Vec3::broadcast(data.dt.0)
|
||||||
.clamped(-1.0, depth.clamped(0.0, 1.0).powi(3)))
|
* Vec3::new(dir.x, dir.y, move_z)
|
||||||
.min(water_speed);
|
.try_normalized()
|
||||||
|
.unwrap_or_default()
|
||||||
|
* water_accel
|
||||||
|
* (submersion - 0.2).clamp(0.0, 1.0).powi(2);
|
||||||
|
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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) {
|
pub fn fly_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) -> bool {
|
||||||
// Update velocity (counteract gravity with lift)
|
if let Some(force) = data.body.fly_thrust() {
|
||||||
update.vel.0 += Vec3::unit_z() * data.dt.0 * GRAVITY
|
let thrust = efficiency * force;
|
||||||
+ Vec3::new(
|
|
||||||
data.inputs.move_dir.x,
|
|
||||||
data.inputs.move_dir.y,
|
|
||||||
data.inputs.move_z,
|
|
||||||
) * data.dt.0
|
|
||||||
* BASE_FLIGHT_ACCEL
|
|
||||||
* efficiency;
|
|
||||||
|
|
||||||
handle_orientation(data, update, data.body.base_ori_rate());
|
let accel = thrust / data.mass.0;
|
||||||
|
|
||||||
|
handle_orientation(data, update, efficiency);
|
||||||
|
|
||||||
|
// Elevation control
|
||||||
|
match data.body {
|
||||||
|
// flappy flappy
|
||||||
|
Body::Dragon(_) | Body::BirdMedium(_) | Body::BirdSmall(_) => {
|
||||||
|
let anti_grav = GRAVITY * (1.0 + data.inputs.move_z.min(0.0));
|
||||||
|
update.vel.0.z += data.dt.0 * (anti_grav + accel * data.inputs.move_z.max(0.0));
|
||||||
|
},
|
||||||
|
// floaty floaty
|
||||||
|
Body::Ship(ship @ ship::Body::DefaultAirship) => {
|
||||||
|
let regulate_density = |min: f32, max: f32, def: f32, rate: f32| -> Density {
|
||||||
|
// Reset to default on no input
|
||||||
|
let change = if data.inputs.move_z.abs() > std::f32::EPSILON {
|
||||||
|
-data.inputs.move_z
|
||||||
|
} else {
|
||||||
|
(def - data.density.0).max(-1.0).min(1.0)
|
||||||
|
};
|
||||||
|
Density((update.density.0 + data.dt.0 * rate * change).clamp(min, max))
|
||||||
|
};
|
||||||
|
let def_density = ship.density().0;
|
||||||
|
if data.physics.in_liquid().is_some() {
|
||||||
|
let hull_density = ship.hull_density().0;
|
||||||
|
update.density.0 =
|
||||||
|
regulate_density(def_density * 0.6, hull_density, hull_density, 25.0).0;
|
||||||
|
} else {
|
||||||
|
update.density.0 =
|
||||||
|
regulate_density(def_density * 0.5, def_density * 1.5, def_density, 0.5).0;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
// oopsie woopsie
|
||||||
|
// TODO: refactor to make this state impossible
|
||||||
|
_ => {},
|
||||||
|
};
|
||||||
|
|
||||||
|
update.vel.0 += Vec2::broadcast(data.dt.0)
|
||||||
|
* accel
|
||||||
|
* if data.body.can_strafe() {
|
||||||
|
data.inputs.move_dir
|
||||||
|
} else {
|
||||||
|
let fw = Vec2::from(update.ori);
|
||||||
|
fw * data.inputs.move_dir.dot(fw).max(0.0)
|
||||||
|
};
|
||||||
|
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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
|
||||||
@ -408,7 +480,7 @@ pub fn handle_climb(data: &JoinData, update: &mut StateUpdate) {
|
|||||||
&& !data.physics.on_ground
|
&& !data.physics.on_ground
|
||||||
&& !data
|
&& !data
|
||||||
.physics
|
.physics
|
||||||
.in_liquid
|
.in_liquid()
|
||||||
.map(|depth| depth > 1.0)
|
.map(|depth| depth > 1.0)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
//&& update.vel.0.z < 0.0
|
//&& update.vel.0.z < 0.0
|
||||||
@ -442,7 +514,7 @@ pub fn attempt_glide_wield(data: &JoinData, update: &mut StateUpdate) {
|
|||||||
if data.inventory.equipped(EquipSlot::Glider).is_some()
|
if data.inventory.equipped(EquipSlot::Glider).is_some()
|
||||||
&& !data
|
&& !data
|
||||||
.physics
|
.physics
|
||||||
.in_liquid
|
.in_liquid()
|
||||||
.map(|depth| depth > 1.0)
|
.map(|depth| depth > 1.0)
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
&& data.body.is_humanoid()
|
&& data.body.is_humanoid()
|
||||||
@ -453,23 +525,16 @@ pub fn attempt_glide_wield(data: &JoinData, update: &mut StateUpdate) {
|
|||||||
|
|
||||||
/// Checks that player can jump and sends jump event if so
|
/// Checks that player can jump and sends jump event if so
|
||||||
pub fn handle_jump(data: &JoinData, update: &mut StateUpdate, strength: f32) -> bool {
|
pub fn handle_jump(data: &JoinData, update: &mut StateUpdate, strength: f32) -> bool {
|
||||||
if input_is_pressed(data, InputKind::Jump)
|
(input_is_pressed(data, InputKind::Jump) && data.physics.on_ground)
|
||||||
&& data.physics.on_ground
|
.then(|| data.body.jump_impulse())
|
||||||
&& !data
|
.flatten()
|
||||||
.physics
|
.map(|impulse| {
|
||||||
.in_liquid
|
update.local_events.push_front(LocalEvent::Jump(
|
||||||
.map(|depth| depth > 1.0)
|
data.entity,
|
||||||
.unwrap_or(false)
|
strength * impulse / data.mass.0,
|
||||||
&& data.body.jump_impulse().is_some()
|
));
|
||||||
{
|
})
|
||||||
update.local_events.push_front(LocalEvent::Jump(
|
.is_some()
|
||||||
data.entity,
|
|
||||||
data.body.jump_impulse().unwrap() * strength,
|
|
||||||
));
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_ability(data: &JoinData, update: &mut StateUpdate, input: InputKind) {
|
fn handle_ability(data: &JoinData, update: &mut StateUpdate, input: InputKind) {
|
||||||
@ -666,7 +731,12 @@ impl MovementDirection {
|
|||||||
pub fn get_2d_dir(self, data: &JoinData) -> Vec2<f32> {
|
pub fn get_2d_dir(self, data: &JoinData) -> Vec2<f32> {
|
||||||
use MovementDirection::*;
|
use MovementDirection::*;
|
||||||
match self {
|
match self {
|
||||||
Look => data.inputs.look_dir.xy(),
|
Look => data
|
||||||
|
.inputs
|
||||||
|
.look_dir
|
||||||
|
.to_horizontal()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.xy(),
|
||||||
Move => data.inputs.move_dir,
|
Move => data.inputs.move_dir,
|
||||||
}
|
}
|
||||||
.try_normalized()
|
.try_normalized()
|
||||||
|
@ -112,6 +112,8 @@ impl Dir {
|
|||||||
|
|
||||||
pub fn back() -> Self { -Dir::new(Vec3::<f32>::unit_y()) }
|
pub fn back() -> Self { -Dir::new(Vec3::<f32>::unit_y()) }
|
||||||
|
|
||||||
|
pub fn to_horizontal(self) -> Option<Self> { Self::from_unnormalized(self.xy().into()) }
|
||||||
|
|
||||||
pub fn vec(&self) -> &Vec3<f32> { &self.0 }
|
pub fn vec(&self) -> &Vec3<f32> { &self.0 }
|
||||||
|
|
||||||
pub fn to_vec(self) -> Vec3<f32> { self.0 }
|
pub fn to_vec(self) -> Vec3<f32> { self.0 }
|
||||||
|
@ -199,9 +199,9 @@ impl State {
|
|||||||
ecs.register::<comp::Mounting>();
|
ecs.register::<comp::Mounting>();
|
||||||
ecs.register::<comp::MountState>();
|
ecs.register::<comp::MountState>();
|
||||||
ecs.register::<comp::Mass>();
|
ecs.register::<comp::Mass>();
|
||||||
|
ecs.register::<comp::Density>();
|
||||||
ecs.register::<comp::Collider>();
|
ecs.register::<comp::Collider>();
|
||||||
ecs.register::<comp::Sticky>();
|
ecs.register::<comp::Sticky>();
|
||||||
ecs.register::<comp::Gravity>();
|
|
||||||
ecs.register::<comp::CharacterState>();
|
ecs.register::<comp::CharacterState>();
|
||||||
ecs.register::<comp::Object>();
|
ecs.register::<comp::Object>();
|
||||||
ecs.register::<comp::Group>();
|
ecs.register::<comp::Group>();
|
||||||
|
@ -31,4 +31,4 @@ slab = "0.4.2"
|
|||||||
specs = { git = "https://github.com/amethyst/specs.git", features = ["serde", "storage-event-control", "derive"], rev = "5a9b71035007be0e3574f35184acac1cd4530496" }
|
specs = { git = "https://github.com/amethyst/specs.git", features = ["serde", "storage-event-control", "derive"], rev = "5a9b71035007be0e3574f35184acac1cd4530496" }
|
||||||
|
|
||||||
# Tweak running code
|
# Tweak running code
|
||||||
#inline_tweak = { version = "1.0.8", features = ["release_tweak"] }
|
# inline_tweak = { version = "1.0.8", features = ["release_tweak"] }
|
||||||
|
@ -10,8 +10,9 @@ use common::{
|
|||||||
item::MaterialStatManifest,
|
item::MaterialStatManifest,
|
||||||
slot::{EquipSlot, Slot},
|
slot::{EquipSlot, Slot},
|
||||||
},
|
},
|
||||||
Beam, Body, CharacterState, Combo, Controller, Energy, Health, Inventory, Melee, Mounting,
|
Beam, Body, CharacterState, Combo, Controller, Density, Energy, Health, Inventory, Mass,
|
||||||
Ori, PhysicsState, Poise, PoiseState, Pos, SkillSet, StateUpdate, Stats, Vel,
|
Melee, Mounting, Ori, PhysicsState, Poise, PoiseState, Pos, SkillSet, StateUpdate, Stats,
|
||||||
|
Vel,
|
||||||
},
|
},
|
||||||
event::{EventBus, LocalEvent, ServerEvent},
|
event::{EventBus, LocalEvent, ServerEvent},
|
||||||
resources::DeltaTime,
|
resources::DeltaTime,
|
||||||
@ -32,6 +33,7 @@ fn incorporate_update(join: &mut JoinStruct, mut state_update: StateUpdate) {
|
|||||||
*join.pos = state_update.pos;
|
*join.pos = state_update.pos;
|
||||||
*join.vel = state_update.vel;
|
*join.vel = state_update.vel;
|
||||||
*join.ori = state_update.ori;
|
*join.ori = state_update.ori;
|
||||||
|
*join.density = state_update.density;
|
||||||
// Note: might be changed every tick by timer anyway
|
// Note: might be changed every tick by timer anyway
|
||||||
if join.energy.get_unchecked() != &state_update.energy {
|
if join.energy.get_unchecked() != &state_update.energy {
|
||||||
*join.energy.get_mut_unchecked() = state_update.energy
|
*join.energy.get_mut_unchecked() = state_update.energy
|
||||||
@ -67,6 +69,7 @@ pub struct ReadData<'a> {
|
|||||||
lazy_update: Read<'a, LazyUpdate>,
|
lazy_update: Read<'a, LazyUpdate>,
|
||||||
healths: ReadStorage<'a, Health>,
|
healths: ReadStorage<'a, Health>,
|
||||||
bodies: ReadStorage<'a, Body>,
|
bodies: ReadStorage<'a, Body>,
|
||||||
|
masses: ReadStorage<'a, Mass>,
|
||||||
physics_states: ReadStorage<'a, PhysicsState>,
|
physics_states: ReadStorage<'a, PhysicsState>,
|
||||||
melee_attacks: ReadStorage<'a, Melee>,
|
melee_attacks: ReadStorage<'a, Melee>,
|
||||||
beams: ReadStorage<'a, Beam>,
|
beams: ReadStorage<'a, Beam>,
|
||||||
@ -93,6 +96,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>,
|
||||||
|
WriteStorage<'a, Density>,
|
||||||
WriteStorage<'a, Energy>,
|
WriteStorage<'a, Energy>,
|
||||||
WriteStorage<'a, Inventory>,
|
WriteStorage<'a, Inventory>,
|
||||||
WriteStorage<'a, Controller>,
|
WriteStorage<'a, Controller>,
|
||||||
@ -112,6 +116,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
mut positions,
|
mut positions,
|
||||||
mut velocities,
|
mut velocities,
|
||||||
mut orientations,
|
mut orientations,
|
||||||
|
mut densities,
|
||||||
mut energies,
|
mut energies,
|
||||||
mut inventories,
|
mut inventories,
|
||||||
mut controllers,
|
mut controllers,
|
||||||
@ -128,14 +133,15 @@ impl<'a> System<'a> for Sys {
|
|||||||
mut pos,
|
mut pos,
|
||||||
mut vel,
|
mut vel,
|
||||||
mut ori,
|
mut ori,
|
||||||
|
mass,
|
||||||
|
mut density,
|
||||||
energy,
|
energy,
|
||||||
inventory,
|
inventory,
|
||||||
mut controller,
|
mut controller,
|
||||||
health,
|
health,
|
||||||
body,
|
body,
|
||||||
physics,
|
physics,
|
||||||
stat,
|
(stat, skill_set),
|
||||||
skill_set,
|
|
||||||
combo,
|
combo,
|
||||||
) in (
|
) in (
|
||||||
&read_data.entities,
|
&read_data.entities,
|
||||||
@ -144,14 +150,15 @@ impl<'a> System<'a> for Sys {
|
|||||||
&mut positions,
|
&mut positions,
|
||||||
&mut velocities,
|
&mut velocities,
|
||||||
&mut orientations,
|
&mut orientations,
|
||||||
|
&read_data.masses,
|
||||||
|
&mut densities,
|
||||||
&mut energies.restrict_mut(),
|
&mut energies.restrict_mut(),
|
||||||
&mut inventories.restrict_mut(),
|
&mut inventories.restrict_mut(),
|
||||||
&mut controllers,
|
&mut controllers,
|
||||||
read_data.healths.maybe(),
|
read_data.healths.maybe(),
|
||||||
&read_data.bodies,
|
&read_data.bodies,
|
||||||
&read_data.physics_states,
|
&read_data.physics_states,
|
||||||
&read_data.stats,
|
(&read_data.stats, &read_data.skill_sets),
|
||||||
&read_data.skill_sets,
|
|
||||||
&read_data.combos,
|
&read_data.combos,
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
@ -253,6 +260,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
pos: &mut pos,
|
pos: &mut pos,
|
||||||
vel: &mut vel,
|
vel: &mut vel,
|
||||||
ori: &mut ori,
|
ori: &mut ori,
|
||||||
|
mass: &mass,
|
||||||
|
density: &mut density,
|
||||||
energy,
|
energy,
|
||||||
inventory,
|
inventory,
|
||||||
controller: &mut controller,
|
controller: &mut controller,
|
||||||
|
@ -5,15 +5,17 @@ use spatial_grid::SpatialGrid;
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{
|
comp::{
|
||||||
body::ship::figuredata::{VoxelCollider, VOXEL_COLLIDER_MANIFEST},
|
body::ship::figuredata::{VoxelCollider, VOXEL_COLLIDER_MANIFEST},
|
||||||
BeamSegment, Body, CharacterState, Collider, Gravity, Mass, Mounting, Ori, PhysicsState,
|
BeamSegment, Body, CharacterState, Collider, Density, Fluid, Mass, Mounting, Ori,
|
||||||
Pos, PosVelDefer, PreviousPhysCache, Projectile, Scale, Shockwave, Sticky, Vel,
|
PhysicsState, Pos, PosVelDefer, PreviousPhysCache, Projectile, Scale, Shockwave, Sticky,
|
||||||
|
Vel,
|
||||||
},
|
},
|
||||||
consts::{FRIC_GROUND, GRAVITY},
|
consts::{AIR_DENSITY, FRIC_GROUND, GRAVITY},
|
||||||
event::{EventBus, ServerEvent},
|
event::{EventBus, ServerEvent},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::DeltaTime,
|
resources::DeltaTime,
|
||||||
terrain::{Block, TerrainGrid},
|
terrain::{Block, TerrainGrid},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
|
util::Projection,
|
||||||
vol::{BaseVol, ReadVol},
|
vol::{BaseVol, ReadVol},
|
||||||
};
|
};
|
||||||
use common_base::{prof_span, span};
|
use common_base::{prof_span, span};
|
||||||
@ -24,39 +26,84 @@ use specs::{
|
|||||||
Entities, Entity, Join, ParJoin, Read, ReadExpect, ReadStorage, SystemData, Write, WriteExpect,
|
Entities, Entity, Join, ParJoin, Read, ReadExpect, ReadStorage, SystemData, Write, WriteExpect,
|
||||||
WriteStorage,
|
WriteStorage,
|
||||||
};
|
};
|
||||||
use std::ops::Range;
|
use std::{f32::consts::PI, ops::Range};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
pub const BOUYANCY: f32 = 1.0;
|
/// The density of the fluid as a function of submersion ratio in given fluid
|
||||||
// Friction values used for linear damping. They are unitless quantities. The
|
/// where it is assumed that any unsubmersed part is is air.
|
||||||
// value of these quantities must be between zero and one. They represent the
|
// This is a pretty silly way of doing it as it assumes everything is spherical
|
||||||
// amount an object will slow down within 1/60th of a second. Eg. if the
|
// in shape and uniform in mass distribution, but the result feels good enough.
|
||||||
// friction is 0.01, and the speed is 1.0, then after 1/60th of a second the
|
// TODO: Make the shape a capsule?
|
||||||
// speed will be 0.99. after 1 second the speed will be 0.54, which is 0.99 ^
|
fn fluid_density(height: f32, fluid: &Fluid) -> Density {
|
||||||
// 60.
|
// If depth is less than our height (partial submersion), remove
|
||||||
pub const FRIC_AIR: f32 = 0.0025;
|
// fluid density based on the ratio of displacement to full volume.
|
||||||
pub const FRIC_FLUID: f32 = 0.4;
|
// The displacement is modelled as a gradually submersed sphere.
|
||||||
|
let immersion = fluid.depth().map_or(1.0, |depth| {
|
||||||
|
if height < depth {
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
let hemisphere_filled_vol = |r: f32, h: f32| -> f32 {
|
||||||
|
let r_ = (r.powi(2) - (r - h).powi(2)).sqrt();
|
||||||
|
(1.0 / 6.0) * PI * h * (3.0 * r_.powi(2) + h.powi(2))
|
||||||
|
};
|
||||||
|
let hemisphere_vol = |r: f32| -> f32 { (2.0 / 3.0) * PI * r.powi(3) };
|
||||||
|
let r = height / 2.0;
|
||||||
|
let sphere_vol = 2.0 * hemisphere_vol(r);
|
||||||
|
if depth < r {
|
||||||
|
hemisphere_filled_vol(r, depth) / sphere_vol
|
||||||
|
} else {
|
||||||
|
1.0 - hemisphere_filled_vol(r, height - depth) / sphere_vol
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Integrates forces, calculates the new velocity based off of the old velocity
|
Density(fluid.density().0 * immersion + AIR_DENSITY * (1.0 - immersion))
|
||||||
// dt = delta time
|
}
|
||||||
// lv = linear velocity
|
|
||||||
// damp = linear damping
|
|
||||||
// Friction is a type of damping.
|
|
||||||
fn integrate_forces(dt: f32, mut lv: Vec3<f32>, grav: f32, damp: f32) -> Vec3<f32> {
|
|
||||||
// Clamp dt to an effective 10 TPS, to prevent gravity from slamming the players
|
|
||||||
// into the floor when stationary if other systems cause the server to lag
|
|
||||||
// (as observed in the 0.9 release party).
|
|
||||||
let dt = dt.min(0.1);
|
|
||||||
|
|
||||||
// this is not linear damping, because it is proportional to the original
|
#[allow(clippy::too_many_arguments)]
|
||||||
// velocity this "linear" damping in in fact, quite exponential. and thus
|
fn integrate_forces(
|
||||||
// must be interpolated accordingly
|
dt: &DeltaTime,
|
||||||
let linear_damp = (1.0 - damp.min(1.0)).powf(dt * 60.0);
|
mut vel: Vel,
|
||||||
|
body: &Body,
|
||||||
|
density: &Density,
|
||||||
|
mass: &Mass,
|
||||||
|
fluid: &Fluid,
|
||||||
|
gravity: f32,
|
||||||
|
) -> Vel {
|
||||||
|
let dim = body.dimensions();
|
||||||
|
let height = dim.z;
|
||||||
|
let rel_flow = fluid.relative_flow(&vel);
|
||||||
|
let fluid_density = fluid_density(height, fluid);
|
||||||
|
debug_assert!(mass.0 > 0.0);
|
||||||
|
debug_assert!(density.0 > 0.0);
|
||||||
|
|
||||||
// TODO: investigate if we can have air friction provide the neccessary limits
|
// Aerodynamic/hydrodynamic forces
|
||||||
// here
|
if !rel_flow.0.is_approx_zero() {
|
||||||
lv.z = (lv.z - grav * dt).max(-80.0).min(lv.z);
|
debug_assert!(!rel_flow.0.map(|a| a.is_nan()).reduce_or());
|
||||||
lv * linear_damp
|
let impulse = dt.0 * body.aerodynamic_forces(&rel_flow, fluid_density.0);
|
||||||
|
debug_assert!(!impulse.map(|a| a.is_nan()).reduce_or());
|
||||||
|
if !impulse.is_approx_zero() {
|
||||||
|
let new_v = vel.0 + impulse / mass.0;
|
||||||
|
// If the new velocity is in the opposite direction, it's because the forces
|
||||||
|
// involved are too high for the current tick to handle. We deal with this by
|
||||||
|
// removing the component of our velocity vector along the direction of force.
|
||||||
|
// This way we can only ever lose velocity and will never experience a reverse
|
||||||
|
// in direction from events such as falling into water at high velocities.
|
||||||
|
if new_v.dot(vel.0) < 0.0 {
|
||||||
|
vel.0 -= vel.0.projected(&impulse);
|
||||||
|
} else {
|
||||||
|
vel.0 = new_v;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
debug_assert!(!vel.0.map(|a| a.is_nan()).reduce_or());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Hydrostatic/aerostatic forces
|
||||||
|
// modify gravity to account for the effective density as a result of buoyancy
|
||||||
|
let down_force = dt.0 * gravity * (density.0 - fluid_density.0) / density.0;
|
||||||
|
vel.0.z -= down_force;
|
||||||
|
|
||||||
|
vel
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_z_limit(
|
fn calc_z_limit(
|
||||||
@ -88,7 +135,6 @@ pub struct PhysicsRead<'a> {
|
|||||||
stickies: ReadStorage<'a, Sticky>,
|
stickies: ReadStorage<'a, Sticky>,
|
||||||
masses: ReadStorage<'a, Mass>,
|
masses: ReadStorage<'a, Mass>,
|
||||||
colliders: ReadStorage<'a, Collider>,
|
colliders: ReadStorage<'a, Collider>,
|
||||||
gravities: ReadStorage<'a, Gravity>,
|
|
||||||
mountings: ReadStorage<'a, Mounting>,
|
mountings: ReadStorage<'a, Mounting>,
|
||||||
projectiles: ReadStorage<'a, Projectile>,
|
projectiles: ReadStorage<'a, Projectile>,
|
||||||
beams: ReadStorage<'a, BeamSegment>,
|
beams: ReadStorage<'a, BeamSegment>,
|
||||||
@ -96,6 +142,7 @@ pub struct PhysicsRead<'a> {
|
|||||||
char_states: ReadStorage<'a, CharacterState>,
|
char_states: ReadStorage<'a, CharacterState>,
|
||||||
bodies: ReadStorage<'a, Body>,
|
bodies: ReadStorage<'a, Body>,
|
||||||
character_states: ReadStorage<'a, CharacterState>,
|
character_states: ReadStorage<'a, CharacterState>,
|
||||||
|
densities: ReadStorage<'a, Density>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(SystemData)]
|
#[derive(SystemData)]
|
||||||
@ -255,7 +302,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
positions,
|
positions,
|
||||||
&mut write.velocities,
|
&mut write.velocities,
|
||||||
previous_phys_cache,
|
previous_phys_cache,
|
||||||
read.masses.maybe(),
|
&read.masses,
|
||||||
read.colliders.maybe(),
|
read.colliders.maybe(),
|
||||||
!&read.mountings,
|
!&read.mountings,
|
||||||
read.stickies.maybe(),
|
read.stickies.maybe(),
|
||||||
@ -288,7 +335,6 @@ impl<'a> PhysicsData<'a> {
|
|||||||
char_state_maybe,
|
char_state_maybe,
|
||||||
)| {
|
)| {
|
||||||
let z_limits = calc_z_limit(char_state_maybe, collider);
|
let z_limits = calc_z_limit(char_state_maybe, collider);
|
||||||
let mass = mass.map(|m| m.0).unwrap_or(previous_cache.scale);
|
|
||||||
|
|
||||||
// Resets touch_entities in physics
|
// Resets touch_entities in physics
|
||||||
physics.touch_entities.clear();
|
physics.touch_entities.clear();
|
||||||
@ -320,13 +366,14 @@ impl<'a> PhysicsData<'a> {
|
|||||||
.get(entity)
|
.get(entity)
|
||||||
.zip(positions.get(entity))
|
.zip(positions.get(entity))
|
||||||
.zip(previous_phys_cache.get(entity))
|
.zip(previous_phys_cache.get(entity))
|
||||||
.map(|((uid, pos), previous_cache)| {
|
.zip(read.masses.get(entity))
|
||||||
|
.map(|(((uid, pos), previous_cache), mass)| {
|
||||||
(
|
(
|
||||||
entity,
|
entity,
|
||||||
uid,
|
uid,
|
||||||
pos,
|
pos,
|
||||||
previous_cache,
|
previous_cache,
|
||||||
read.masses.get(entity),
|
mass,
|
||||||
read.colliders.get(entity),
|
read.colliders.get(entity),
|
||||||
read.char_states.get(entity),
|
read.char_states.get(entity),
|
||||||
)
|
)
|
||||||
@ -358,16 +405,6 @@ impl<'a> PhysicsData<'a> {
|
|||||||
let z_limits_other =
|
let z_limits_other =
|
||||||
calc_z_limit(char_state_other_maybe, collider_other);
|
calc_z_limit(char_state_other_maybe, collider_other);
|
||||||
|
|
||||||
let mass_other = mass_other
|
|
||||||
.map(|m| m.0)
|
|
||||||
.unwrap_or(previous_cache_other.scale);
|
|
||||||
// This check after the pos check, as we currently don't have
|
|
||||||
// that many
|
|
||||||
// massless entites [citation needed]
|
|
||||||
if mass_other == 0.0 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity_entity_collision_checks += 1;
|
entity_entity_collision_checks += 1;
|
||||||
|
|
||||||
const MIN_COLLISION_DIST: f32 = 0.3;
|
const MIN_COLLISION_DIST: f32 = 0.3;
|
||||||
@ -418,8 +455,8 @@ impl<'a> PhysicsData<'a> {
|
|||||||
{
|
{
|
||||||
let force = 400.0
|
let force = 400.0
|
||||||
* (collision_dist - diff.magnitude())
|
* (collision_dist - diff.magnitude())
|
||||||
* mass_other
|
* mass_other.0
|
||||||
/ (mass + mass_other);
|
/ (mass.0 + mass_other.0);
|
||||||
|
|
||||||
vel_delta +=
|
vel_delta +=
|
||||||
Vec3::from(diff.normalized()) * force * step_delta;
|
Vec3::from(diff.normalized()) * force * step_delta;
|
||||||
@ -542,10 +579,12 @@ impl<'a> PhysicsData<'a> {
|
|||||||
// We do this in a first pass because it helps keep things more stable for
|
// We do this in a first pass because it helps keep things more stable for
|
||||||
// entities that are anchored to other entities (such as airships).
|
// entities that are anchored to other entities (such as airships).
|
||||||
(
|
(
|
||||||
&read.entities,
|
|
||||||
positions,
|
positions,
|
||||||
velocities,
|
velocities,
|
||||||
|
&read.bodies,
|
||||||
&write.physics_states,
|
&write.physics_states,
|
||||||
|
&read.masses,
|
||||||
|
&read.densities,
|
||||||
!&read.mountings,
|
!&read.mountings,
|
||||||
)
|
)
|
||||||
.par_join()
|
.par_join()
|
||||||
@ -554,38 +593,31 @@ impl<'a> PhysicsData<'a> {
|
|||||||
prof_span!(guard, "velocity update rayon job");
|
prof_span!(guard, "velocity update rayon job");
|
||||||
guard
|
guard
|
||||||
},
|
},
|
||||||
|_guard, (entity, pos, vel, physics_state, _)| {
|
|_guard, (pos, vel, body, physics_state, mass, density, _)| {
|
||||||
let in_loaded_chunk = read
|
let in_loaded_chunk = read
|
||||||
.terrain
|
.terrain
|
||||||
.get_key(read.terrain.pos_key(pos.0.map(|e| e.floor() as i32)))
|
.get_key(read.terrain.pos_key(pos.0.map(|e| e.floor() as i32)))
|
||||||
.is_some();
|
.is_some();
|
||||||
// Integrate forces
|
|
||||||
// Friction is assumed to be a constant dependent on location
|
|
||||||
let friction = if physics_state.on_ground { 0.0 } else { FRIC_AIR }
|
|
||||||
// .max(if physics_state.on_ground {
|
|
||||||
// FRIC_GROUND
|
|
||||||
// } else {
|
|
||||||
// 0.0
|
|
||||||
// })
|
|
||||||
.max(if physics_state.in_liquid.is_some() {
|
|
||||||
FRIC_FLUID
|
|
||||||
} else {
|
|
||||||
0.0
|
|
||||||
});
|
|
||||||
let downward_force =
|
|
||||||
if !in_loaded_chunk {
|
|
||||||
0.0 // No gravity in unloaded chunks
|
|
||||||
} else if physics_state
|
|
||||||
.in_liquid
|
|
||||||
.map(|depth| depth > 0.75)
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
(1.0 - BOUYANCY) * GRAVITY
|
|
||||||
} else {
|
|
||||||
GRAVITY
|
|
||||||
} * read.gravities.get(entity).map(|g| g.0).unwrap_or_default();
|
|
||||||
|
|
||||||
vel.0 = integrate_forces(read.dt.0, vel.0, downward_force, friction);
|
// Apply physics only if in a loaded chunk
|
||||||
|
if in_loaded_chunk {
|
||||||
|
// Clamp dt to an effective 10 TPS, to prevent gravity from slamming the
|
||||||
|
// players into the floor when stationary if other systems cause the server
|
||||||
|
// to lag (as observed in the 0.9 release party).
|
||||||
|
let dt = DeltaTime(read.dt.0.min(0.1));
|
||||||
|
|
||||||
|
match physics_state.in_fluid {
|
||||||
|
None => {
|
||||||
|
vel.0.z -= dt.0 * GRAVITY;
|
||||||
|
},
|
||||||
|
Some(fluid) => {
|
||||||
|
vel.0 = integrate_forces(
|
||||||
|
&dt, *vel, body, density, mass, &fluid, GRAVITY,
|
||||||
|
)
|
||||||
|
.0
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -671,8 +703,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
let mut tgt_pos = pos.0 + pos_delta;
|
let mut tgt_pos = pos.0 + pos_delta;
|
||||||
|
|
||||||
let was_on_ground = physics_state.on_ground;
|
let was_on_ground = physics_state.on_ground;
|
||||||
|
let block_snap = body.map_or(false, |b| !matches!(b, Body::Ship(_)));
|
||||||
let block_snap = body.map_or(false, |body| body.jump_impulse().is_some());
|
|
||||||
let climbing =
|
let climbing =
|
||||||
character_state.map_or(false, |cs| matches!(cs, CharacterState::Climb(_)));
|
character_state.map_or(false, |cs| matches!(cs, CharacterState::Climb(_)));
|
||||||
|
|
||||||
@ -816,11 +847,21 @@ impl<'a> PhysicsData<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
physics_state.in_liquid = read
|
physics_state.in_fluid = read
|
||||||
.terrain
|
.terrain
|
||||||
.get(pos.0.map(|e| e.floor() as i32))
|
.get(pos.0.map(|e| e.floor() as i32))
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|vox| vox.is_liquid().then_some(1.0));
|
.and_then(|vox| vox.is_liquid().then_some(1.0))
|
||||||
|
.map(|depth| Fluid::Water {
|
||||||
|
depth,
|
||||||
|
vel: Vel::zero(),
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
Some(Fluid::Air {
|
||||||
|
elevation: pos.0.z,
|
||||||
|
vel: Vel::zero(),
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
tgt_pos = pos.0;
|
tgt_pos = pos.0;
|
||||||
},
|
},
|
||||||
@ -989,12 +1030,19 @@ impl<'a> PhysicsData<'a> {
|
|||||||
.on_wall
|
.on_wall
|
||||||
.map(|dir| ori_from.mul_direction(dir))
|
.map(|dir| ori_from.mul_direction(dir))
|
||||||
});
|
});
|
||||||
physics_state.in_liquid = match (
|
physics_state.in_fluid = match (
|
||||||
physics_state.in_liquid,
|
physics_state.in_fluid,
|
||||||
physics_state_delta.in_liquid,
|
physics_state_delta.in_fluid,
|
||||||
) {
|
) {
|
||||||
// this match computes `x <|> y <|> liftA2 max x y`
|
(Some(x), Some(y)) => x
|
||||||
(Some(x), Some(y)) => Some(x.max(y)),
|
.depth()
|
||||||
|
.and_then(|xh| {
|
||||||
|
y.depth()
|
||||||
|
.map(|yh| xh > yh)
|
||||||
|
.unwrap_or(true)
|
||||||
|
.then_some(x)
|
||||||
|
})
|
||||||
|
.or(Some(y)),
|
||||||
(x @ Some(_), _) => x,
|
(x @ Some(_), _) => x,
|
||||||
(_, y @ Some(_)) => y,
|
(_, y @ Some(_)) => y,
|
||||||
_ => None,
|
_ => None,
|
||||||
@ -1108,7 +1156,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
||||||
cylinder: (f32, f32, f32),
|
cylinder: (f32, f32, f32), // effective collision cylinder
|
||||||
terrain: &'a T,
|
terrain: &'a T,
|
||||||
entity: Entity,
|
entity: Entity,
|
||||||
pos: &mut Pos,
|
pos: &mut Pos,
|
||||||
@ -1450,8 +1498,27 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
|||||||
physics_state.ground_vel = ground_vel;
|
physics_state.ground_vel = ground_vel;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set in_liquid state
|
physics_state.in_fluid = max_liquid_z
|
||||||
physics_state.in_liquid = max_liquid_z.map(|max_z| max_z - pos.0.z);
|
.map(|max_z| max_z - pos.0.z) // NOTE: assumes min_z == 0.0
|
||||||
|
.map(|depth| {
|
||||||
|
physics_state
|
||||||
|
.in_liquid()
|
||||||
|
// This is suboptimal because it doesn't check for true depth,
|
||||||
|
// so it can cause problems for situations like swimming down
|
||||||
|
// a river and spawning or teleporting in(/to) water
|
||||||
|
.map(|old_depth| (old_depth + old_pos.z - pos.0.z).max(depth))
|
||||||
|
.unwrap_or(depth)
|
||||||
|
})
|
||||||
|
.map(|depth| Fluid::Water {
|
||||||
|
depth,
|
||||||
|
vel: Vel::zero(),
|
||||||
|
})
|
||||||
|
.or_else(|| {
|
||||||
|
Some(Fluid::Air {
|
||||||
|
elevation: pos.0.z,
|
||||||
|
vel: Vel::zero(),
|
||||||
|
})
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn voxel_collider_bounding_sphere(
|
fn voxel_collider_bounding_sphere(
|
||||||
|
@ -7,7 +7,7 @@ use common::{
|
|||||||
beam,
|
beam,
|
||||||
buff::{BuffCategory, BuffData, BuffKind, BuffSource},
|
buff::{BuffCategory, BuffData, BuffKind, BuffSource},
|
||||||
inventory::loadout::Loadout,
|
inventory::loadout::Loadout,
|
||||||
shockwave, Agent, Alignment, Body, Gravity, Health, HomeChunk, Inventory, Item, ItemDrop,
|
shockwave, Agent, Alignment, Body, Health, HomeChunk, Inventory, Item, ItemDrop,
|
||||||
LightEmitter, Object, Ori, Poise, Pos, Projectile, Scale, SkillSet, Stats, Vel,
|
LightEmitter, Object, Ori, Poise, Pos, Projectile, Scale, SkillSet, Stats, Vel,
|
||||||
WaypointArea,
|
WaypointArea,
|
||||||
},
|
},
|
||||||
@ -163,7 +163,6 @@ pub fn handle_shoot(
|
|||||||
body: Body,
|
body: Body,
|
||||||
light: Option<LightEmitter>,
|
light: Option<LightEmitter>,
|
||||||
projectile: Projectile,
|
projectile: Projectile,
|
||||||
gravity: Option<Gravity>,
|
|
||||||
speed: f32,
|
speed: f32,
|
||||||
object: Option<Object>,
|
object: Option<Object>,
|
||||||
) {
|
) {
|
||||||
@ -200,9 +199,6 @@ pub fn handle_shoot(
|
|||||||
if let Some(light) = light {
|
if let Some(light) = light {
|
||||||
builder = builder.with(light)
|
builder = builder.with(light)
|
||||||
}
|
}
|
||||||
if let Some(gravity) = gravity {
|
|
||||||
builder = builder.with(gravity)
|
|
||||||
}
|
|
||||||
if let Some(object) = object {
|
if let Some(object) = object {
|
||||||
builder = builder.with(object)
|
builder = builder.with(object)
|
||||||
}
|
}
|
||||||
|
@ -478,8 +478,14 @@ pub fn handle_delete(server: &mut Server, entity: EcsEntity) {
|
|||||||
pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>) {
|
pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>) {
|
||||||
let state = &server.state;
|
let state = &server.state;
|
||||||
if vel.z <= -30.0 {
|
if vel.z <= -30.0 {
|
||||||
let falldmg = (vel.z.powi(2) / 20.0 - 40.0) * 7.5;
|
let mass = state
|
||||||
|
.ecs()
|
||||||
|
.read_storage::<comp::Mass>()
|
||||||
|
.get(entity)
|
||||||
|
.copied()
|
||||||
|
.unwrap_or_default();
|
||||||
let inventories = state.ecs().read_storage::<Inventory>();
|
let inventories = state.ecs().read_storage::<Inventory>();
|
||||||
|
let falldmg = mass.0 * vel.z.powi(2) / 200.0;
|
||||||
let stats = state.ecs().read_storage::<Stats>();
|
let stats = state.ecs().read_storage::<Stats>();
|
||||||
// Handle health change
|
// Handle health change
|
||||||
if let Some(mut health) = state.ecs().write_storage::<comp::Health>().get_mut(entity) {
|
if let Some(mut health) = state.ecs().write_storage::<comp::Health>().get_mut(entity) {
|
||||||
@ -495,7 +501,7 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>)
|
|||||||
// Handle poise change
|
// Handle poise change
|
||||||
if let Some(mut poise) = state.ecs().write_storage::<comp::Poise>().get_mut(entity) {
|
if let Some(mut poise) = state.ecs().write_storage::<comp::Poise>().get_mut(entity) {
|
||||||
let poise_damage = PoiseChange {
|
let poise_damage = PoiseChange {
|
||||||
amount: -(falldmg / 2.0) as i32,
|
amount: -(mass.0 * vel.magnitude_squared() / 1500.0) as i32,
|
||||||
source: PoiseSource::Falling,
|
source: PoiseSource::Falling,
|
||||||
};
|
};
|
||||||
let poise_change = poise_damage.modify_poise_damage(inventories.get(entity));
|
let poise_change = poise_damage.modify_poise_damage(inventories.get(entity));
|
||||||
|
@ -73,12 +73,9 @@ impl Server {
|
|||||||
body,
|
body,
|
||||||
light,
|
light,
|
||||||
projectile,
|
projectile,
|
||||||
gravity,
|
|
||||||
speed,
|
speed,
|
||||||
object,
|
object,
|
||||||
} => handle_shoot(
|
} => handle_shoot(self, entity, dir, body, light, projectile, speed, object),
|
||||||
self, entity, dir, body, light, projectile, gravity, speed, object,
|
|
||||||
),
|
|
||||||
ServerEvent::Shockwave {
|
ServerEvent::Shockwave {
|
||||||
properties,
|
properties,
|
||||||
pos,
|
pos,
|
||||||
|
@ -184,6 +184,8 @@ impl StateExt for State {
|
|||||||
))
|
))
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
)
|
)
|
||||||
|
.with(body.mass())
|
||||||
|
.with(body.density())
|
||||||
.with(match body {
|
.with(match body {
|
||||||
comp::Body::Ship(ship) => comp::Collider::Voxel {
|
comp::Body::Ship(ship) => comp::Collider::Voxel {
|
||||||
id: ship.manifest_entry().to_string(),
|
id: ship.manifest_entry().to_string(),
|
||||||
@ -208,7 +210,6 @@ impl StateExt for State {
|
|||||||
.with(health)
|
.with(health)
|
||||||
.with(poise)
|
.with(poise)
|
||||||
.with(comp::Alignment::Npc)
|
.with(comp::Alignment::Npc)
|
||||||
.with(comp::Gravity(1.0))
|
|
||||||
.with(comp::CharacterState::default())
|
.with(comp::CharacterState::default())
|
||||||
.with(inventory)
|
.with(inventory)
|
||||||
.with(comp::Buffs::default())
|
.with(comp::Buffs::default())
|
||||||
@ -217,19 +218,20 @@ impl StateExt for State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
|
let body = comp::Body::Object(object);
|
||||||
self.ecs_mut()
|
self.ecs_mut()
|
||||||
.create_entity_synced()
|
.create_entity_synced()
|
||||||
.with(pos)
|
.with(pos)
|
||||||
.with(comp::Vel(Vec3::zero()))
|
.with(comp::Vel(Vec3::zero()))
|
||||||
.with(comp::Ori::default())
|
.with(comp::Ori::default())
|
||||||
.with(comp::Mass(5.0))
|
.with(body.mass())
|
||||||
|
.with(body.density())
|
||||||
.with(comp::Collider::Box {
|
.with(comp::Collider::Box {
|
||||||
radius: comp::Body::Object(object).radius(),
|
radius: body.radius(),
|
||||||
z_min: 0.0,
|
z_min: 0.0,
|
||||||
z_max: comp::Body::Object(object).height(),
|
z_max: body.height(),
|
||||||
})
|
})
|
||||||
.with(comp::Body::Object(object))
|
.with(body)
|
||||||
.with(comp::Gravity(1.0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_ship(
|
fn create_ship(
|
||||||
@ -238,18 +240,19 @@ impl StateExt for State {
|
|||||||
ship: comp::ship::Body,
|
ship: comp::ship::Body,
|
||||||
mountable: bool,
|
mountable: bool,
|
||||||
) -> EcsEntityBuilder {
|
) -> EcsEntityBuilder {
|
||||||
|
let body = comp::Body::Ship(ship);
|
||||||
let mut builder = self
|
let mut builder = self
|
||||||
.ecs_mut()
|
.ecs_mut()
|
||||||
.create_entity_synced()
|
.create_entity_synced()
|
||||||
.with(pos)
|
.with(pos)
|
||||||
.with(comp::Vel(Vec3::zero()))
|
.with(comp::Vel(Vec3::zero()))
|
||||||
.with(comp::Ori::default())
|
.with(comp::Ori::default())
|
||||||
.with(comp::Mass(50.0))
|
.with(body.mass())
|
||||||
|
.with(body.density())
|
||||||
.with(comp::Collider::Voxel {
|
.with(comp::Collider::Voxel {
|
||||||
id: ship.manifest_entry().to_string(),
|
id: ship.manifest_entry().to_string(),
|
||||||
})
|
})
|
||||||
.with(comp::Body::Ship(ship))
|
.with(body)
|
||||||
.with(comp::Gravity(1.0))
|
|
||||||
.with(comp::Scale(comp::ship::AIRSHIP_SCALE))
|
.with(comp::Scale(comp::ship::AIRSHIP_SCALE))
|
||||||
.with(comp::Controller::default())
|
.with(comp::Controller::default())
|
||||||
.with(comp::inventory::Inventory::new_empty())
|
.with(comp::inventory::Inventory::new_empty())
|
||||||
@ -279,7 +282,8 @@ impl StateExt for State {
|
|||||||
.with(pos)
|
.with(pos)
|
||||||
.with(vel)
|
.with(vel)
|
||||||
.with(comp::Ori::from_unnormalized_vec(vel.0).unwrap_or_default())
|
.with(comp::Ori::from_unnormalized_vec(vel.0).unwrap_or_default())
|
||||||
.with(comp::Mass(0.0))
|
.with(body.mass())
|
||||||
|
.with(body.density())
|
||||||
.with(comp::Collider::Point)
|
.with(comp::Collider::Point)
|
||||||
.with(body)
|
.with(body)
|
||||||
.with(projectile)
|
.with(projectile)
|
||||||
@ -406,7 +410,6 @@ impl StateExt for State {
|
|||||||
z_min: 0.0,
|
z_min: 0.0,
|
||||||
z_max: 1.75,
|
z_max: 1.75,
|
||||||
});
|
});
|
||||||
self.write_component_ignore_entity_dead(entity, comp::Gravity(1.0));
|
|
||||||
self.write_component_ignore_entity_dead(entity, comp::CharacterState::default());
|
self.write_component_ignore_entity_dead(entity, comp::CharacterState::default());
|
||||||
self.write_component_ignore_entity_dead(entity, comp::Alignment::Owned(player_uid));
|
self.write_component_ignore_entity_dead(entity, comp::Alignment::Owned(player_uid));
|
||||||
self.write_component_ignore_entity_dead(entity, comp::Buffs::default());
|
self.write_component_ignore_entity_dead(entity, comp::Buffs::default());
|
||||||
@ -450,6 +453,8 @@ impl StateExt for State {
|
|||||||
z_max: body.height(),
|
z_max: body.height(),
|
||||||
});
|
});
|
||||||
self.write_component_ignore_entity_dead(entity, body);
|
self.write_component_ignore_entity_dead(entity, body);
|
||||||
|
self.write_component_ignore_entity_dead(entity, body.mass());
|
||||||
|
self.write_component_ignore_entity_dead(entity, body.density());
|
||||||
let (health_level, energy_level) = (
|
let (health_level, energy_level) = (
|
||||||
skill_set
|
skill_set
|
||||||
.skill_level(Skill::General(GeneralSkill::HealthIncrease))
|
.skill_level(Skill::General(GeneralSkill::HealthIncrease))
|
||||||
|
@ -19,6 +19,7 @@ use common::{
|
|||||||
InputKind, Inventory, InventoryAction, LightEmitter, MountState, Ori, PhysicsState, Pos,
|
InputKind, Inventory, InventoryAction, LightEmitter, MountState, Ori, PhysicsState, Pos,
|
||||||
Scale, SkillSet, Stats, UnresolvedChatMsg, Vel,
|
Scale, SkillSet, Stats, UnresolvedChatMsg, Vel,
|
||||||
},
|
},
|
||||||
|
consts::GRAVITY,
|
||||||
effect::{BuffEffect, Effect},
|
effect::{BuffEffect, Effect},
|
||||||
event::{Emitter, EventBus, ServerEvent},
|
event::{Emitter, EventBus, ServerEvent},
|
||||||
path::TraversalConfig,
|
path::TraversalConfig,
|
||||||
@ -233,10 +234,10 @@ impl<'a> System<'a> for Sys {
|
|||||||
node_tolerance,
|
node_tolerance,
|
||||||
slow_factor,
|
slow_factor,
|
||||||
on_ground: physics_state.on_ground,
|
on_ground: physics_state.on_ground,
|
||||||
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().is_some()).unwrap_or(false),
|
can_fly: body.map(|b| b.fly_thrust().is_some()).unwrap_or(false),
|
||||||
};
|
};
|
||||||
|
|
||||||
if traversal_config.can_fly {
|
if traversal_config.can_fly {
|
||||||
@ -743,7 +744,7 @@ impl<'a> AgentData<'a> {
|
|||||||
* speed.min(agent.rtsim_controller.speed_factor);
|
* speed.min(agent.rtsim_controller.speed_factor);
|
||||||
self.jump_if(controller, bearing.z > 1.5 || self.traversal_config.can_fly);
|
self.jump_if(controller, bearing.z > 1.5 || self.traversal_config.can_fly);
|
||||||
controller.inputs.climb = Some(comp::Climb::Up);
|
controller.inputs.climb = Some(comp::Climb::Up);
|
||||||
//.filter(|_| bearing.z > 0.1 || self.physics_state.in_liquid.is_some());
|
//.filter(|_| bearing.z > 0.1 || self.physics_state.in_liquid().is_some());
|
||||||
|
|
||||||
controller.inputs.move_z = bearing.z
|
controller.inputs.move_z = bearing.z
|
||||||
+ if self.traversal_config.can_fly {
|
+ if self.traversal_config.can_fly {
|
||||||
@ -1543,38 +1544,36 @@ impl<'a> AgentData<'a> {
|
|||||||
0.0
|
0.0
|
||||||
};
|
};
|
||||||
|
|
||||||
// Hacky distance offset for ranged weapons. This is
|
let dist_sqrd = self.pos.0.distance_squared(tgt_pos.0);
|
||||||
// intentionally hacky for now before we make ranged
|
|
||||||
// NPCs lead targets and implement varying aiming
|
|
||||||
// skill
|
|
||||||
let distance_offset = match tactic {
|
|
||||||
Tactic::Bow => {
|
|
||||||
0.0004 /* Yay magic numbers */ * self.pos.0.distance_squared(tgt_pos.0)
|
|
||||||
},
|
|
||||||
Tactic::Staff => {
|
|
||||||
0.0015 /* Yay magic numbers */ * self.pos.0.distance_squared(tgt_pos.0)
|
|
||||||
},
|
|
||||||
Tactic::QuadLowRanged => {
|
|
||||||
0.03 /* Yay magic numbers */ * self.pos.0.distance_squared(tgt_pos.0)
|
|
||||||
},
|
|
||||||
_ => 0.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Apply the distance and eye offsets to make the
|
// FIXME: Retrieve actual projectile speed!
|
||||||
// look_dir the vector from projectile launch to
|
// We have to assume projectiles are faster than base speed because there are
|
||||||
// target point
|
// skills that increase it, and in most cases this will cause agents to
|
||||||
if let Some(dir) = Dir::from_unnormalized(
|
// overshoot
|
||||||
Vec3::new(
|
if let Some(dir) = match tactic {
|
||||||
tgt_pos.0.x,
|
Tactic::Bow
|
||||||
tgt_pos.0.y,
|
| Tactic::FixedTurret
|
||||||
tgt_pos.0.z + tgt_eye_offset + distance_offset,
|
| Tactic::QuadLowRanged
|
||||||
) - Vec3::new(self.pos.0.x, self.pos.0.y, self.pos.0.z + eye_offset),
|
| Tactic::QuadMedJump
|
||||||
) {
|
| Tactic::RotatingTurret
|
||||||
|
| Tactic::Staff
|
||||||
|
| Tactic::Turret
|
||||||
|
if dist_sqrd > 0.0 =>
|
||||||
|
{
|
||||||
|
aim_projectile(
|
||||||
|
90.0, // + self.vel.0.magnitude(),
|
||||||
|
Vec3::new(self.pos.0.x, self.pos.0.y, self.pos.0.z + eye_offset),
|
||||||
|
Vec3::new(tgt_pos.0.x, tgt_pos.0.y, tgt_pos.0.z + tgt_eye_offset),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => Dir::from_unnormalized(
|
||||||
|
Vec3::new(tgt_pos.0.x, tgt_pos.0.y, tgt_pos.0.z + tgt_eye_offset)
|
||||||
|
- Vec3::new(self.pos.0.x, self.pos.0.y, self.pos.0.z + eye_offset),
|
||||||
|
),
|
||||||
|
} {
|
||||||
controller.inputs.look_dir = dir;
|
controller.inputs.look_dir = dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
let dist_sqrd = self.pos.0.distance_squared(tgt_pos.0);
|
|
||||||
|
|
||||||
// Match on tactic. Each tactic has different controls
|
// Match on tactic. Each tactic has different controls
|
||||||
// depending on the distance from the agent to the target
|
// depending on the distance from the agent to the target
|
||||||
match tactic {
|
match tactic {
|
||||||
@ -2633,3 +2632,18 @@ fn try_owner_alignment<'a>(
|
|||||||
}
|
}
|
||||||
alignment
|
alignment
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Projectile motion: Returns the direction to aim for the projectile to reach
|
||||||
|
/// target position. Does not take any forces but gravity into account.
|
||||||
|
fn aim_projectile(speed: f32, pos: Vec3<f32>, tgt: Vec3<f32>) -> Option<Dir> {
|
||||||
|
let mut to_tgt = tgt - pos;
|
||||||
|
let dist_sqrd = to_tgt.xy().magnitude_squared();
|
||||||
|
let u_sqrd = speed.powi(2);
|
||||||
|
to_tgt.z = (u_sqrd
|
||||||
|
- (u_sqrd.powi(2) - GRAVITY * (GRAVITY * dist_sqrd + 2.0 * to_tgt.z * u_sqrd))
|
||||||
|
.sqrt()
|
||||||
|
.max(0.0))
|
||||||
|
/ GRAVITY;
|
||||||
|
|
||||||
|
Dir::from_unnormalized(to_tgt)
|
||||||
|
}
|
||||||
|
@ -76,7 +76,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
const ENABLE_RECURSIVE_FIREWORKS: bool = true;
|
const ENABLE_RECURSIVE_FIREWORKS: bool = true;
|
||||||
if ENABLE_RECURSIVE_FIREWORKS {
|
if ENABLE_RECURSIVE_FIREWORKS {
|
||||||
use common::{
|
use common::{
|
||||||
comp::{object, Body, Gravity, LightEmitter, Projectile},
|
comp::{object, Body, LightEmitter, Projectile},
|
||||||
util::Dir,
|
util::Dir,
|
||||||
};
|
};
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
@ -132,7 +132,6 @@ impl<'a> System<'a> for Sys {
|
|||||||
owner: *owner,
|
owner: *owner,
|
||||||
ignore_group: true,
|
ignore_group: true,
|
||||||
},
|
},
|
||||||
gravity: Some(Gravity(1.0)),
|
|
||||||
speed,
|
speed,
|
||||||
object: Some(Object::Firework {
|
object: Some(Object::Firework {
|
||||||
owner: *owner,
|
owner: *owner,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use common::{
|
use common::{
|
||||||
comp::{
|
comp::{
|
||||||
item::MaterialStatManifest, Auras, BeamSegment, Body, Buffs, CanBuild, CharacterState,
|
item::MaterialStatManifest, Auras, BeamSegment, Body, Buffs, CanBuild, CharacterState,
|
||||||
Collider, Combo, Energy, Gravity, Group, Health, Inventory, Item, LightEmitter, Mass,
|
Collider, Combo, Density, Energy, Group, Health, Inventory, Item, LightEmitter, Mass,
|
||||||
MountState, Mounting, Ori, Player, Poise, Pos, Scale, Shockwave, SkillSet, Stats, Sticky,
|
MountState, Mounting, Ori, Player, Poise, Pos, Scale, Shockwave, SkillSet, Stats, Sticky,
|
||||||
Vel,
|
Vel,
|
||||||
},
|
},
|
||||||
@ -58,9 +58,9 @@ pub struct TrackedComps<'a> {
|
|||||||
pub mount_state: ReadStorage<'a, MountState>,
|
pub mount_state: ReadStorage<'a, MountState>,
|
||||||
pub group: ReadStorage<'a, Group>,
|
pub group: ReadStorage<'a, Group>,
|
||||||
pub mass: ReadStorage<'a, Mass>,
|
pub mass: ReadStorage<'a, Mass>,
|
||||||
|
pub density: ReadStorage<'a, Density>,
|
||||||
pub collider: ReadStorage<'a, Collider>,
|
pub collider: ReadStorage<'a, Collider>,
|
||||||
pub sticky: ReadStorage<'a, Sticky>,
|
pub sticky: ReadStorage<'a, Sticky>,
|
||||||
pub gravity: ReadStorage<'a, Gravity>,
|
|
||||||
pub inventory: ReadStorage<'a, Inventory>,
|
pub inventory: ReadStorage<'a, Inventory>,
|
||||||
pub character_state: ReadStorage<'a, CharacterState>,
|
pub character_state: ReadStorage<'a, CharacterState>,
|
||||||
pub shockwave: ReadStorage<'a, Shockwave>,
|
pub shockwave: ReadStorage<'a, Shockwave>,
|
||||||
@ -143,6 +143,10 @@ impl<'a> TrackedComps<'a> {
|
|||||||
.cloned()
|
.cloned()
|
||||||
.map(|c| comps.push(c.into()));
|
.map(|c| comps.push(c.into()));
|
||||||
self.mass.get(entity).copied().map(|c| comps.push(c.into()));
|
self.mass.get(entity).copied().map(|c| comps.push(c.into()));
|
||||||
|
self.density
|
||||||
|
.get(entity)
|
||||||
|
.copied()
|
||||||
|
.map(|c| comps.push(c.into()));
|
||||||
self.collider
|
self.collider
|
||||||
.get(entity)
|
.get(entity)
|
||||||
.cloned()
|
.cloned()
|
||||||
@ -151,10 +155,6 @@ impl<'a> TrackedComps<'a> {
|
|||||||
.get(entity)
|
.get(entity)
|
||||||
.copied()
|
.copied()
|
||||||
.map(|c| comps.push(c.into()));
|
.map(|c| comps.push(c.into()));
|
||||||
self.gravity
|
|
||||||
.get(entity)
|
|
||||||
.copied()
|
|
||||||
.map(|c| comps.push(c.into()));
|
|
||||||
self.inventory
|
self.inventory
|
||||||
.get(entity)
|
.get(entity)
|
||||||
.cloned()
|
.cloned()
|
||||||
@ -201,9 +201,9 @@ pub struct ReadTrackers<'a> {
|
|||||||
pub mount_state: ReadExpect<'a, UpdateTracker<MountState>>,
|
pub mount_state: ReadExpect<'a, UpdateTracker<MountState>>,
|
||||||
pub group: ReadExpect<'a, UpdateTracker<Group>>,
|
pub group: ReadExpect<'a, UpdateTracker<Group>>,
|
||||||
pub mass: ReadExpect<'a, UpdateTracker<Mass>>,
|
pub mass: ReadExpect<'a, UpdateTracker<Mass>>,
|
||||||
|
pub density: ReadExpect<'a, UpdateTracker<Density>>,
|
||||||
pub collider: ReadExpect<'a, UpdateTracker<Collider>>,
|
pub collider: ReadExpect<'a, UpdateTracker<Collider>>,
|
||||||
pub sticky: ReadExpect<'a, UpdateTracker<Sticky>>,
|
pub sticky: ReadExpect<'a, UpdateTracker<Sticky>>,
|
||||||
pub gravity: ReadExpect<'a, UpdateTracker<Gravity>>,
|
|
||||||
pub character_state: ReadExpect<'a, UpdateTracker<CharacterState>>,
|
pub character_state: ReadExpect<'a, UpdateTracker<CharacterState>>,
|
||||||
pub shockwave: ReadExpect<'a, UpdateTracker<Shockwave>>,
|
pub shockwave: ReadExpect<'a, UpdateTracker<Shockwave>>,
|
||||||
pub beam_segment: ReadExpect<'a, UpdateTracker<BeamSegment>>,
|
pub beam_segment: ReadExpect<'a, UpdateTracker<BeamSegment>>,
|
||||||
@ -241,9 +241,9 @@ impl<'a> ReadTrackers<'a> {
|
|||||||
.with_component(&comps.uid, &*self.mount_state, &comps.mount_state, filter)
|
.with_component(&comps.uid, &*self.mount_state, &comps.mount_state, filter)
|
||||||
.with_component(&comps.uid, &*self.group, &comps.group, filter)
|
.with_component(&comps.uid, &*self.group, &comps.group, filter)
|
||||||
.with_component(&comps.uid, &*self.mass, &comps.mass, filter)
|
.with_component(&comps.uid, &*self.mass, &comps.mass, filter)
|
||||||
|
.with_component(&comps.uid, &*self.density, &comps.density, filter)
|
||||||
.with_component(&comps.uid, &*self.collider, &comps.collider, filter)
|
.with_component(&comps.uid, &*self.collider, &comps.collider, filter)
|
||||||
.with_component(&comps.uid, &*self.sticky, &comps.sticky, filter)
|
.with_component(&comps.uid, &*self.sticky, &comps.sticky, filter)
|
||||||
.with_component(&comps.uid, &*self.gravity, &comps.gravity, filter)
|
|
||||||
.with_component(&comps.uid, &*self.inventory, &comps.inventory, filter)
|
.with_component(&comps.uid, &*self.inventory, &comps.inventory, filter)
|
||||||
.with_component(
|
.with_component(
|
||||||
&comps.uid,
|
&comps.uid,
|
||||||
@ -279,9 +279,9 @@ pub struct WriteTrackers<'a> {
|
|||||||
mount_state: WriteExpect<'a, UpdateTracker<MountState>>,
|
mount_state: WriteExpect<'a, UpdateTracker<MountState>>,
|
||||||
group: WriteExpect<'a, UpdateTracker<Group>>,
|
group: WriteExpect<'a, UpdateTracker<Group>>,
|
||||||
mass: WriteExpect<'a, UpdateTracker<Mass>>,
|
mass: WriteExpect<'a, UpdateTracker<Mass>>,
|
||||||
|
density: WriteExpect<'a, UpdateTracker<Density>>,
|
||||||
collider: WriteExpect<'a, UpdateTracker<Collider>>,
|
collider: WriteExpect<'a, UpdateTracker<Collider>>,
|
||||||
sticky: WriteExpect<'a, UpdateTracker<Sticky>>,
|
sticky: WriteExpect<'a, UpdateTracker<Sticky>>,
|
||||||
gravity: WriteExpect<'a, UpdateTracker<Gravity>>,
|
|
||||||
inventory: WriteExpect<'a, UpdateTracker<Inventory>>,
|
inventory: WriteExpect<'a, UpdateTracker<Inventory>>,
|
||||||
character_state: WriteExpect<'a, UpdateTracker<CharacterState>>,
|
character_state: WriteExpect<'a, UpdateTracker<CharacterState>>,
|
||||||
shockwave: WriteExpect<'a, UpdateTracker<Shockwave>>,
|
shockwave: WriteExpect<'a, UpdateTracker<Shockwave>>,
|
||||||
@ -309,9 +309,9 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) {
|
|||||||
trackers.mount_state.record_changes(&comps.mount_state);
|
trackers.mount_state.record_changes(&comps.mount_state);
|
||||||
trackers.group.record_changes(&comps.group);
|
trackers.group.record_changes(&comps.group);
|
||||||
trackers.mass.record_changes(&comps.mass);
|
trackers.mass.record_changes(&comps.mass);
|
||||||
|
trackers.density.record_changes(&comps.density);
|
||||||
trackers.collider.record_changes(&comps.collider);
|
trackers.collider.record_changes(&comps.collider);
|
||||||
trackers.sticky.record_changes(&comps.sticky);
|
trackers.sticky.record_changes(&comps.sticky);
|
||||||
trackers.gravity.record_changes(&comps.gravity);
|
|
||||||
trackers.inventory.record_changes(&comps.inventory);
|
trackers.inventory.record_changes(&comps.inventory);
|
||||||
trackers
|
trackers
|
||||||
.character_state
|
.character_state
|
||||||
@ -350,9 +350,9 @@ fn record_changes(comps: &TrackedComps, trackers: &mut WriteTrackers) {
|
|||||||
log_counts!(mounting, "Mountings");
|
log_counts!(mounting, "Mountings");
|
||||||
log_counts!(mount_state, "Mount States");
|
log_counts!(mount_state, "Mount States");
|
||||||
log_counts!(mass, "Masses");
|
log_counts!(mass, "Masses");
|
||||||
|
log_counts!(mass, "Densities");
|
||||||
log_counts!(collider, "Colliders");
|
log_counts!(collider, "Colliders");
|
||||||
log_counts!(sticky, "Stickies");
|
log_counts!(sticky, "Stickies");
|
||||||
log_counts!(gravity, "Gravitys");
|
|
||||||
log_counts!(loadout, "Loadouts");
|
log_counts!(loadout, "Loadouts");
|
||||||
log_counts!(character_state, "Character States");
|
log_counts!(character_state, "Character States");
|
||||||
log_counts!(shockwave, "Shockwaves");
|
log_counts!(shockwave, "Shockwaves");
|
||||||
@ -380,9 +380,9 @@ pub fn register_trackers(world: &mut World) {
|
|||||||
world.register_tracker::<MountState>();
|
world.register_tracker::<MountState>();
|
||||||
world.register_tracker::<Group>();
|
world.register_tracker::<Group>();
|
||||||
world.register_tracker::<Mass>();
|
world.register_tracker::<Mass>();
|
||||||
|
world.register_tracker::<Density>();
|
||||||
world.register_tracker::<Collider>();
|
world.register_tracker::<Collider>();
|
||||||
world.register_tracker::<Sticky>();
|
world.register_tracker::<Sticky>();
|
||||||
world.register_tracker::<Gravity>();
|
|
||||||
world.register_tracker::<Inventory>();
|
world.register_tracker::<Inventory>();
|
||||||
world.register_tracker::<CharacterState>();
|
world.register_tracker::<CharacterState>();
|
||||||
world.register_tracker::<Shockwave>();
|
world.register_tracker::<Shockwave>();
|
||||||
|
@ -122,11 +122,7 @@ impl EventMapper for MovementEventMapper {
|
|||||||
// it was dispatched
|
// it was dispatched
|
||||||
internal_state.event = mapped_event;
|
internal_state.event = mapped_event;
|
||||||
internal_state.on_ground = physics.on_ground;
|
internal_state.on_ground = physics.on_ground;
|
||||||
if physics.in_liquid.is_some() {
|
internal_state.in_water = physics.in_liquid().is_some();
|
||||||
internal_state.in_water = true;
|
|
||||||
} else {
|
|
||||||
internal_state.in_water = false;
|
|
||||||
}
|
|
||||||
let dt = ecs.fetch::<DeltaTime>().0;
|
let dt = ecs.fetch::<DeltaTime>().0;
|
||||||
internal_state.distance_travelled += vel.0.magnitude() * dt;
|
internal_state.distance_travelled += vel.0.magnitude() * dt;
|
||||||
}
|
}
|
||||||
@ -197,8 +193,8 @@ impl MovementEventMapper {
|
|||||||
underfoot_block_kind: BlockKind,
|
underfoot_block_kind: BlockKind,
|
||||||
) -> SfxEvent {
|
) -> SfxEvent {
|
||||||
// Match run / roll / swim state
|
// Match run / roll / swim state
|
||||||
if physics_state.in_liquid.is_some() && vel.magnitude() > 0.1
|
if physics_state.in_liquid().is_some() && vel.magnitude() > 0.1
|
||||||
|| !previous_state.in_water && physics_state.in_liquid.is_some()
|
|| !previous_state.in_water && physics_state.in_liquid().is_some()
|
||||||
{
|
{
|
||||||
return SfxEvent::Swim;
|
return SfxEvent::Swim;
|
||||||
} else if physics_state.on_ground && vel.magnitude() > 0.1
|
} else if physics_state.on_ground && vel.magnitude() > 0.1
|
||||||
@ -240,7 +236,7 @@ impl MovementEventMapper {
|
|||||||
vel: Vec3<f32>,
|
vel: Vec3<f32>,
|
||||||
underfoot_block_kind: BlockKind,
|
underfoot_block_kind: BlockKind,
|
||||||
) -> SfxEvent {
|
) -> SfxEvent {
|
||||||
if physics_state.in_liquid.is_some() && vel.magnitude() > 0.1 {
|
if physics_state.in_liquid().is_some() && vel.magnitude() > 0.1 {
|
||||||
SfxEvent::Swim
|
SfxEvent::Swim
|
||||||
} else if physics_state.on_ground && vel.magnitude() > 0.1 {
|
} else if physics_state.on_ground && vel.magnitude() > 0.1 {
|
||||||
match underfoot_block_kind {
|
match underfoot_block_kind {
|
||||||
@ -261,7 +257,7 @@ impl MovementEventMapper {
|
|||||||
vel: Vec3<f32>,
|
vel: Vec3<f32>,
|
||||||
underfoot_block_kind: BlockKind,
|
underfoot_block_kind: BlockKind,
|
||||||
) -> SfxEvent {
|
) -> SfxEvent {
|
||||||
if physics_state.in_liquid.is_some() && vel.magnitude() > 0.1 {
|
if physics_state.in_liquid().is_some() && vel.magnitude() > 0.1 {
|
||||||
SfxEvent::Swim
|
SfxEvent::Swim
|
||||||
} else if physics_state.on_ground && vel.magnitude() > 0.1 {
|
} else if physics_state.on_ground && vel.magnitude() > 0.1 {
|
||||||
match underfoot_block_kind {
|
match underfoot_block_kind {
|
||||||
|
@ -783,7 +783,7 @@ impl FigureMgr {
|
|||||||
let target_base = match (
|
let target_base = match (
|
||||||
physics.on_ground,
|
physics.on_ground,
|
||||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||||
physics.in_liquid.is_some(), // In water
|
physics.in_liquid().is_some(), // In water
|
||||||
) {
|
) {
|
||||||
// Standing
|
// Standing
|
||||||
(true, false, false) => anim::character::StandAnimation::update_skeleton(
|
(true, false, false) => anim::character::StandAnimation::update_skeleton(
|
||||||
@ -1411,7 +1411,7 @@ impl FigureMgr {
|
|||||||
skeleton_attr,
|
skeleton_attr,
|
||||||
),
|
),
|
||||||
CharacterState::Wielding { .. } => {
|
CharacterState::Wielding { .. } => {
|
||||||
if physics.in_liquid.is_some() {
|
if physics.in_liquid().is_some() {
|
||||||
anim::character::SwimWieldAnimation::update_skeleton(
|
anim::character::SwimWieldAnimation::update_skeleton(
|
||||||
&target_base,
|
&target_base,
|
||||||
(
|
(
|
||||||
@ -1571,7 +1571,7 @@ impl FigureMgr {
|
|||||||
let target_base = match (
|
let target_base = match (
|
||||||
physics.on_ground,
|
physics.on_ground,
|
||||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||||
physics.in_liquid.is_some(), // In water
|
physics.in_liquid().is_some(), // In water
|
||||||
) {
|
) {
|
||||||
// Standing
|
// Standing
|
||||||
(true, false, false) => {
|
(true, false, false) => {
|
||||||
@ -1773,7 +1773,7 @@ impl FigureMgr {
|
|||||||
let target_base = match (
|
let target_base = match (
|
||||||
physics.on_ground,
|
physics.on_ground,
|
||||||
rel_vel.magnitude_squared() > 0.25, // Moving
|
rel_vel.magnitude_squared() > 0.25, // Moving
|
||||||
physics.in_liquid.is_some(), // In water
|
physics.in_liquid().is_some(), // In water
|
||||||
) {
|
) {
|
||||||
// Standing
|
// Standing
|
||||||
(true, false, false) => {
|
(true, false, false) => {
|
||||||
@ -2100,7 +2100,7 @@ impl FigureMgr {
|
|||||||
let target_base = match (
|
let target_base = match (
|
||||||
physics.on_ground,
|
physics.on_ground,
|
||||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||||
physics.in_liquid.is_some(), // In water
|
physics.in_liquid().is_some(), // In water
|
||||||
) {
|
) {
|
||||||
// Standing
|
// Standing
|
||||||
(true, false, false) => {
|
(true, false, false) => {
|
||||||
@ -2459,7 +2459,7 @@ impl FigureMgr {
|
|||||||
let target_base = match (
|
let target_base = match (
|
||||||
physics.on_ground,
|
physics.on_ground,
|
||||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||||
physics.in_liquid.is_some(), // In water
|
physics.in_liquid().is_some(), // In water
|
||||||
) {
|
) {
|
||||||
// Standing
|
// Standing
|
||||||
(true, false, false) => anim::bird_medium::IdleAnimation::update_skeleton(
|
(true, false, false) => anim::bird_medium::IdleAnimation::update_skeleton(
|
||||||
@ -2569,7 +2569,7 @@ impl FigureMgr {
|
|||||||
let target_base = match (
|
let target_base = match (
|
||||||
physics.on_ground,
|
physics.on_ground,
|
||||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||||
physics.in_liquid.is_some(), // In water
|
physics.in_liquid().is_some(), // In water
|
||||||
) {
|
) {
|
||||||
// Idle
|
// Idle
|
||||||
(_, false, _) => anim::fish_medium::IdleAnimation::update_skeleton(
|
(_, false, _) => anim::fish_medium::IdleAnimation::update_skeleton(
|
||||||
@ -2658,7 +2658,7 @@ impl FigureMgr {
|
|||||||
let target_base = match (
|
let target_base = match (
|
||||||
physics.on_ground,
|
physics.on_ground,
|
||||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||||
physics.in_liquid.is_some(), // In water
|
physics.in_liquid().is_some(), // In water
|
||||||
) {
|
) {
|
||||||
// Idle
|
// Idle
|
||||||
(true, false, false) => anim::biped_small::IdleAnimation::update_skeleton(
|
(true, false, false) => anim::biped_small::IdleAnimation::update_skeleton(
|
||||||
@ -3003,7 +3003,7 @@ impl FigureMgr {
|
|||||||
let target_base = match (
|
let target_base = match (
|
||||||
physics.on_ground,
|
physics.on_ground,
|
||||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||||
physics.in_liquid.is_some(), // In water
|
physics.in_liquid().is_some(), // In water
|
||||||
) {
|
) {
|
||||||
// Standing
|
// Standing
|
||||||
(true, false, false) => anim::dragon::IdleAnimation::update_skeleton(
|
(true, false, false) => anim::dragon::IdleAnimation::update_skeleton(
|
||||||
@ -3098,7 +3098,7 @@ impl FigureMgr {
|
|||||||
let target_base = match (
|
let target_base = match (
|
||||||
physics.on_ground,
|
physics.on_ground,
|
||||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||||
physics.in_liquid.is_some(), // In water
|
physics.in_liquid().is_some(), // In water
|
||||||
) {
|
) {
|
||||||
// Standing
|
// Standing
|
||||||
(true, false, false) => anim::theropod::IdleAnimation::update_skeleton(
|
(true, false, false) => anim::theropod::IdleAnimation::update_skeleton(
|
||||||
@ -3287,7 +3287,7 @@ impl FigureMgr {
|
|||||||
let target_base = match (
|
let target_base = match (
|
||||||
physics.on_ground,
|
physics.on_ground,
|
||||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||||
physics.in_liquid.is_some(), // In water
|
physics.in_liquid().is_some(), // In water
|
||||||
) {
|
) {
|
||||||
// Standing
|
// Standing
|
||||||
(true, false, false) => anim::bird_small::IdleAnimation::update_skeleton(
|
(true, false, false) => anim::bird_small::IdleAnimation::update_skeleton(
|
||||||
@ -3378,7 +3378,7 @@ impl FigureMgr {
|
|||||||
let target_base = match (
|
let target_base = match (
|
||||||
physics.on_ground,
|
physics.on_ground,
|
||||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||||
physics.in_liquid.is_some(), // In water
|
physics.in_liquid().is_some(), // In water
|
||||||
) {
|
) {
|
||||||
// Idle
|
// Idle
|
||||||
(_, false, _) => anim::fish_small::IdleAnimation::update_skeleton(
|
(_, false, _) => anim::fish_small::IdleAnimation::update_skeleton(
|
||||||
@ -3467,7 +3467,7 @@ impl FigureMgr {
|
|||||||
let target_base = match (
|
let target_base = match (
|
||||||
physics.on_ground,
|
physics.on_ground,
|
||||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||||
physics.in_liquid.is_some(), // In water
|
physics.in_liquid().is_some(), // In water
|
||||||
) {
|
) {
|
||||||
// Running
|
// Running
|
||||||
(true, true, false) => anim::biped_large::RunAnimation::update_skeleton(
|
(true, true, false) => anim::biped_large::RunAnimation::update_skeleton(
|
||||||
@ -3988,7 +3988,7 @@ impl FigureMgr {
|
|||||||
let target_base = match (
|
let target_base = match (
|
||||||
physics.on_ground,
|
physics.on_ground,
|
||||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||||
physics.in_liquid.is_some(), // In water
|
physics.in_liquid().is_some(), // In water
|
||||||
) {
|
) {
|
||||||
// Standing
|
// Standing
|
||||||
(true, false, false) => anim::golem::IdleAnimation::update_skeleton(
|
(true, false, false) => anim::golem::IdleAnimation::update_skeleton(
|
||||||
@ -4174,7 +4174,7 @@ impl FigureMgr {
|
|||||||
let target_base = match (
|
let target_base = match (
|
||||||
physics.on_ground,
|
physics.on_ground,
|
||||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||||
physics.in_liquid.is_some(), // In water
|
physics.in_liquid().is_some(), // In water
|
||||||
) {
|
) {
|
||||||
// Standing
|
// Standing
|
||||||
(true, false, false) => anim::object::IdleAnimation::update_skeleton(
|
(true, false, false) => anim::object::IdleAnimation::update_skeleton(
|
||||||
@ -4303,7 +4303,7 @@ impl FigureMgr {
|
|||||||
let target_base = match (
|
let target_base = match (
|
||||||
physics.on_ground,
|
physics.on_ground,
|
||||||
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
rel_vel.magnitude_squared() > MOVING_THRESHOLD_SQR, // Moving
|
||||||
physics.in_liquid.is_some(), // In water
|
physics.in_liquid().is_some(), // In water
|
||||||
) {
|
) {
|
||||||
// Standing
|
// Standing
|
||||||
(true, false, false) => anim::ship::IdleAnimation::update_skeleton(
|
(true, false, false) => anim::ship::IdleAnimation::update_skeleton(
|
||||||
|
Loading…
Reference in New Issue
Block a user