mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Skiing and ice skating
This commit is contained in:
parent
ca1a27bd11
commit
2bf8e1865f
@ -25,6 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Water caves
|
- Water caves
|
||||||
- Modular weapons
|
- Modular weapons
|
||||||
- Added Thai translation
|
- Added Thai translation
|
||||||
|
- Skiing and ice skating
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
15
assets/common/items/armor/misc/foot/iceskate.ron
Normal file
15
assets/common/items/armor/misc/foot/iceskate.ron
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
ItemDef(
|
||||||
|
name: "Ice Skates",
|
||||||
|
description: "Best used on a frozen lake.",
|
||||||
|
kind: Armor((
|
||||||
|
kind: Foot,
|
||||||
|
stats: (
|
||||||
|
protection: Some(Normal(3.0)),
|
||||||
|
ground_contact: Skate,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
quality: Moderate,
|
||||||
|
tags: [
|
||||||
|
Material(Steel),
|
||||||
|
],
|
||||||
|
)
|
16
assets/common/items/armor/misc/foot/ski.ron
Normal file
16
assets/common/items/armor/misc/foot/ski.ron
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
ItemDef(
|
||||||
|
name: "Wooden skis",
|
||||||
|
description: "Best used downhill on snow.",
|
||||||
|
kind: Armor((
|
||||||
|
kind: Foot,
|
||||||
|
stats: (
|
||||||
|
protection: Some(Normal(3.0)),
|
||||||
|
ground_contact: Ski,
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
quality: Moderate,
|
||||||
|
tags: [
|
||||||
|
Material(Wood),
|
||||||
|
// SalvageInto(Twigs),
|
||||||
|
],
|
||||||
|
)
|
@ -2699,6 +2699,14 @@
|
|||||||
"voxel.armor.misc.pants.grayscale",
|
"voxel.armor.misc.pants.grayscale",
|
||||||
(0.0, 1.0, 0.0), (-120.0, 210.0,15.0), 0.9,
|
(0.0, 1.0, 0.0), (-120.0, 210.0,15.0), 0.9,
|
||||||
),
|
),
|
||||||
|
Simple("common.items.armor.misc.foot.ski"): VoxTrans(
|
||||||
|
"voxel.armor.misc.foot.ski",
|
||||||
|
(0.0, 0.0, 0.0), (-120.0, 210.0,15.0), 0.9,
|
||||||
|
),
|
||||||
|
Simple("common.items.armor.misc.foot.iceskate"): VoxTrans(
|
||||||
|
"voxel.armor.misc.foot.iceskate",
|
||||||
|
(0.0, 0.0, 0.0), (-120.0, 210.0,15.0), 0.9,
|
||||||
|
),
|
||||||
// Backs
|
// Backs
|
||||||
Simple("common.items.armor.misc.back.short_0"): VoxTrans(
|
Simple("common.items.armor.misc.back.short_0"): VoxTrans(
|
||||||
"voxel.armor.misc.back.short-0",
|
"voxel.armor.misc.back.short-0",
|
||||||
|
BIN
assets/voxygen/voxel/armor/misc/foot/iceskate.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/armor/misc/foot/iceskate.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/voxel/armor/misc/foot/ski.vox
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/voxel/armor/misc/foot/ski.vox
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -164,5 +164,13 @@
|
|||||||
vox_spec: ("armor.merchant.foot", (-2.5, -3.5, -2.0)),
|
vox_spec: ("armor.merchant.foot", (-2.5, -3.5, -2.0)),
|
||||||
color: None
|
color: None
|
||||||
),
|
),
|
||||||
|
"common.items.armor.misc.foot.ski": (
|
||||||
|
vox_spec: ("armor.misc.foot.ski", (-2.5, -15.5, -2.0)),
|
||||||
|
color: None
|
||||||
|
),
|
||||||
|
"common.items.armor.misc.foot.iceskate": (
|
||||||
|
vox_spec: ("armor.misc.foot.iceskate", (-2.5, -3.5, -2.0)),
|
||||||
|
color: None
|
||||||
|
),
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
|
@ -402,6 +402,7 @@ impl From<&CharacterState> for CharacterAbilityType {
|
|||||||
| CharacterState::SpriteSummon(_)
|
| CharacterState::SpriteSummon(_)
|
||||||
| CharacterState::UseItem(_)
|
| CharacterState::UseItem(_)
|
||||||
| CharacterState::SpriteInteract(_)
|
| CharacterState::SpriteInteract(_)
|
||||||
|
| CharacterState::Skate(_)
|
||||||
| CharacterState::Wallrun(_) => Self::Other,
|
| CharacterState::Wallrun(_) => Self::Other,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::{
|
comp::{
|
||||||
item::ConsumableKind, ControlAction, Density, Energy, InputAttr, InputKind, Ori, Pos, Vel,
|
inventory::item::armor::Friction, item::ConsumableKind, ControlAction, Density, Energy,
|
||||||
|
InputAttr, InputKind, Ori, Pos, Vel,
|
||||||
},
|
},
|
||||||
event::{LocalEvent, ServerEvent},
|
event::{LocalEvent, ServerEvent},
|
||||||
states::{
|
states::{
|
||||||
@ -123,6 +124,8 @@ pub enum CharacterState {
|
|||||||
SpriteInteract(sprite_interact::Data),
|
SpriteInteract(sprite_interact::Data),
|
||||||
/// Runs on the wall
|
/// Runs on the wall
|
||||||
Wallrun(wallrun::Data),
|
Wallrun(wallrun::Data),
|
||||||
|
/// Ice skating or skiing
|
||||||
|
Skate(skate::Data),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CharacterState {
|
impl CharacterState {
|
||||||
@ -153,15 +156,16 @@ impl CharacterState {
|
|||||||
pub fn is_stealthy(&self) -> bool {
|
pub fn is_stealthy(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self,
|
self,
|
||||||
CharacterState::Idle(idle::Data { is_sneaking: true })
|
CharacterState::Idle(idle::Data {
|
||||||
| CharacterState::Wielding(wielding::Data {
|
is_sneaking: true,
|
||||||
is_sneaking: true,
|
footwear: _
|
||||||
..
|
}) | CharacterState::Wielding(wielding::Data {
|
||||||
})
|
is_sneaking: true,
|
||||||
| CharacterState::Roll(roll::Data {
|
..
|
||||||
is_sneaking: true,
|
}) | CharacterState::Roll(roll::Data {
|
||||||
..
|
is_sneaking: true,
|
||||||
})
|
..
|
||||||
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,6 +231,8 @@ impl CharacterState {
|
|||||||
|
|
||||||
pub fn is_glide(&self) -> bool { matches!(self, CharacterState::Glide(_)) }
|
pub fn is_glide(&self) -> bool { matches!(self, CharacterState::Glide(_)) }
|
||||||
|
|
||||||
|
pub fn is_skate(&self) -> bool { matches!(self, CharacterState::Skate(_)) }
|
||||||
|
|
||||||
pub fn is_melee_dodge(&self) -> bool {
|
pub fn is_melee_dodge(&self) -> bool {
|
||||||
matches!(self, CharacterState::Roll(d) if d.static_data.immune_melee)
|
matches!(self, CharacterState::Roll(d) if d.static_data.immune_melee)
|
||||||
}
|
}
|
||||||
@ -329,6 +335,7 @@ impl CharacterState {
|
|||||||
CharacterState::SpriteSummon(data) => data.behavior(j, output_events),
|
CharacterState::SpriteSummon(data) => data.behavior(j, output_events),
|
||||||
CharacterState::UseItem(data) => data.behavior(j, output_events),
|
CharacterState::UseItem(data) => data.behavior(j, output_events),
|
||||||
CharacterState::SpriteInteract(data) => data.behavior(j, output_events),
|
CharacterState::SpriteInteract(data) => data.behavior(j, output_events),
|
||||||
|
CharacterState::Skate(data) => data.behavior(j, output_events),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,12 +382,26 @@ impl CharacterState {
|
|||||||
CharacterState::SpriteSummon(data) => data.handle_event(j, output_events, action),
|
CharacterState::SpriteSummon(data) => data.handle_event(j, output_events, action),
|
||||||
CharacterState::UseItem(data) => data.handle_event(j, output_events, action),
|
CharacterState::UseItem(data) => data.handle_event(j, output_events, action),
|
||||||
CharacterState::SpriteInteract(data) => data.handle_event(j, output_events, action),
|
CharacterState::SpriteInteract(data) => data.handle_event(j, output_events, action),
|
||||||
|
CharacterState::Skate(data) => data.handle_event(j, output_events, action),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn footwear(&self) -> Option<Friction> {
|
||||||
|
match &self {
|
||||||
|
CharacterState::Idle(data) => data.footwear,
|
||||||
|
CharacterState::Skate(data) => Some(data.footwear),
|
||||||
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CharacterState {
|
impl Default for CharacterState {
|
||||||
fn default() -> Self { Self::Idle(idle::Data { is_sneaking: false }) }
|
fn default() -> Self {
|
||||||
|
Self::Idle(idle::Data {
|
||||||
|
is_sneaking: false,
|
||||||
|
footwear: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Component for CharacterState {
|
impl Component for CharacterState {
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
use crate::{
|
||||||
|
comp::item::Rgb,
|
||||||
|
terrain::{Block, BlockKind},
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{cmp::Ordering, ops::Sub};
|
use std::{cmp::Ordering, ops::Sub};
|
||||||
|
|
||||||
@ -26,6 +30,45 @@ impl Armor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// longitudinal and lateral friction, only meaningful for footwear
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub enum Friction {
|
||||||
|
Normal,
|
||||||
|
Ski,
|
||||||
|
Skate,
|
||||||
|
// Snowshoe,
|
||||||
|
// Spikes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Friction {
|
||||||
|
fn default() -> Self { Self::Normal }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Friction {
|
||||||
|
pub fn can_skate_on(&self, b: BlockKind) -> bool {
|
||||||
|
match self {
|
||||||
|
Friction::Ski => matches!(b, BlockKind::Snow | BlockKind::Ice | BlockKind::Air),
|
||||||
|
Friction::Skate => b == BlockKind::Ice,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// longitudinal (forward) and lateral (side) friction
|
||||||
|
pub fn get_friction(&self, b: BlockKind) -> (f32, f32) {
|
||||||
|
match (self, b) {
|
||||||
|
(Friction::Ski, BlockKind::Snow) => (0.01, 0.95),
|
||||||
|
(Friction::Ski, BlockKind::Ice) => (0.001, 0.5),
|
||||||
|
(Friction::Ski, BlockKind::Water) => (0.1, 0.7),
|
||||||
|
(Friction::Ski, BlockKind::Air) => (0.0, 0.0),
|
||||||
|
(Friction::Skate, BlockKind::Ice) => (0.001, 0.99),
|
||||||
|
_ => {
|
||||||
|
let non_directional_friction = Block::new(b, Rgb::new(0, 0, 0)).get_friction();
|
||||||
|
(non_directional_friction, non_directional_friction)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Stats {
|
pub struct Stats {
|
||||||
/// Protection is non-linearly transformed (following summation) to a damage
|
/// Protection is non-linearly transformed (following summation) to a damage
|
||||||
@ -46,6 +89,9 @@ pub struct Stats {
|
|||||||
/// Stealth is summed along with the base stealth bonus (2.0), and then
|
/// Stealth is summed along with the base stealth bonus (2.0), and then
|
||||||
/// the agent's perception distance is divided by this value
|
/// the agent's perception distance is divided by this value
|
||||||
stealth: Option<f32>,
|
stealth: Option<f32>,
|
||||||
|
/// Ground contact type, mostly for shoes
|
||||||
|
#[serde(default)]
|
||||||
|
ground_contact: Friction,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stats {
|
impl Stats {
|
||||||
@ -66,6 +112,7 @@ impl Stats {
|
|||||||
energy_reward,
|
energy_reward,
|
||||||
crit_power,
|
crit_power,
|
||||||
stealth,
|
stealth,
|
||||||
|
ground_contact: Friction::Normal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,6 +127,8 @@ impl Stats {
|
|||||||
pub fn crit_power(&self) -> Option<f32> { self.crit_power }
|
pub fn crit_power(&self) -> Option<f32> { self.crit_power }
|
||||||
|
|
||||||
pub fn stealth(&self) -> Option<f32> { self.stealth }
|
pub fn stealth(&self) -> Option<f32> { self.stealth }
|
||||||
|
|
||||||
|
pub fn ground_contact(&self) -> Friction { self.ground_contact }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Sub<Stats> for Stats {
|
impl Sub<Stats> for Stats {
|
||||||
@ -99,6 +148,7 @@ impl Sub<Stats> for Stats {
|
|||||||
.map(|(a, b)| a - b),
|
.map(|(a, b)| a - b),
|
||||||
crit_power: self.crit_power.zip(other.crit_power).map(|(a, b)| a - b),
|
crit_power: self.crit_power.zip(other.crit_power).map(|(a, b)| a - b),
|
||||||
stealth: self.stealth.zip(other.stealth).map(|(a, b)| a - b),
|
stealth: self.stealth.zip(other.stealth).map(|(a, b)| a - b),
|
||||||
|
ground_contact: Friction::Normal,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,6 +209,8 @@ impl Armor {
|
|||||||
|
|
||||||
pub fn stealth(&self) -> Option<f32> { self.stats.stealth }
|
pub fn stealth(&self) -> Option<f32> { self.stats.stealth }
|
||||||
|
|
||||||
|
pub fn ground_contact(&self) -> Friction { self.stats.ground_contact }
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn test_armor(
|
pub fn test_armor(
|
||||||
kind: ArmorKind,
|
kind: ArmorKind,
|
||||||
@ -174,6 +226,7 @@ impl Armor {
|
|||||||
energy_reward: None,
|
energy_reward: None,
|
||||||
crit_power: None,
|
crit_power: None,
|
||||||
stealth: None,
|
stealth: None,
|
||||||
|
ground_contact: Friction::Normal,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
use super::{Fluid, Ori};
|
use super::{Fluid, Ori};
|
||||||
use crate::{
|
use crate::{
|
||||||
comp::body::ship::figuredata::VoxelCollider, consts::WATER_DENSITY, terrain::Block, uid::Uid,
|
comp::{body::ship::figuredata::VoxelCollider, inventory::item::armor::Friction},
|
||||||
|
consts::WATER_DENSITY,
|
||||||
|
terrain::Block,
|
||||||
|
uid::Uid,
|
||||||
};
|
};
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -182,6 +185,9 @@ pub struct PhysicsState {
|
|||||||
pub touch_entities: HashSet<Uid>,
|
pub touch_entities: HashSet<Uid>,
|
||||||
pub in_fluid: Option<Fluid>,
|
pub in_fluid: Option<Fluid>,
|
||||||
pub ground_vel: Vec3<f32>,
|
pub ground_vel: Vec3<f32>,
|
||||||
|
pub footwear: Friction,
|
||||||
|
pub skating_last_height: f32,
|
||||||
|
pub skating_active: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PhysicsState {
|
impl PhysicsState {
|
||||||
|
@ -79,7 +79,7 @@ impl CharacterBehavior for Data {
|
|||||||
CLIMB_BOOST_JUMP_FACTOR * impulse / data.mass.0,
|
CLIMB_BOOST_JUMP_FACTOR * impulse / data.mass.0,
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: false });
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
return update;
|
return update;
|
||||||
};
|
};
|
||||||
// Move player
|
// Move player
|
||||||
@ -103,7 +103,7 @@ impl CharacterBehavior for Data {
|
|||||||
.try_change_by(-energy_use * data.dt.0)
|
.try_change_by(-energy_use * data.dt.0)
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: false });
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set orientation direction based on wall direction
|
// Set orientation direction based on wall direction
|
||||||
|
@ -20,7 +20,7 @@ impl CharacterBehavior for Data {
|
|||||||
|
|
||||||
// Try to Fall/Stand up/Move
|
// Try to Fall/Stand up/Move
|
||||||
if data.physics.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 {
|
if data.physics.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 {
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: false });
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
update
|
update
|
||||||
@ -52,7 +52,7 @@ impl CharacterBehavior for Data {
|
|||||||
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
// Try to Fall/Stand up/Move
|
// Try to Fall/Stand up/Move
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: false });
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
update
|
update
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ impl CharacterBehavior for Data {
|
|||||||
.and_then(|inv| inv.equipped(EquipSlot::Glider))
|
.and_then(|inv| inv.equipped(EquipSlot::Glider))
|
||||||
.is_none()
|
.is_none()
|
||||||
{
|
{
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: false });
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
} else if !handle_climb(data, &mut update) {
|
} else if !handle_climb(data, &mut update) {
|
||||||
let air_flow = data
|
let air_flow = data
|
||||||
.physics
|
.physics
|
||||||
@ -207,7 +207,7 @@ impl CharacterBehavior for Data {
|
|||||||
pos: data.pos.0,
|
pos: data.pos.0,
|
||||||
wielded: false,
|
wielded: false,
|
||||||
}));
|
}));
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: false });
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
update
|
update
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ impl CharacterBehavior for Data {
|
|||||||
..*self
|
..*self
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
CharacterState::Idle(idle::Data { is_sneaking: false })
|
CharacterState::Idle(idle::Data::default())
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ impl CharacterBehavior for Data {
|
|||||||
pos: data.pos.0,
|
pos: data.pos.0,
|
||||||
wielded: false,
|
wielded: false,
|
||||||
}));
|
}));
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: false });
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
update
|
update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,19 +1,25 @@
|
|||||||
use super::utils::*;
|
use super::utils::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
comp::{character_state::OutputEvents, CharacterState, InventoryAction, StateUpdate},
|
comp::{
|
||||||
|
character_state::OutputEvents, inventory::item::armor::Friction, CharacterState,
|
||||||
|
InventoryAction, StateUpdate,
|
||||||
|
},
|
||||||
states::behavior::{CharacterBehavior, JoinData},
|
states::behavior::{CharacterBehavior, JoinData},
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Default)]
|
||||||
pub struct Data {
|
pub struct Data {
|
||||||
pub is_sneaking: bool,
|
pub is_sneaking: bool,
|
||||||
|
// None means unknown
|
||||||
|
pub(crate) footwear: Option<Friction>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CharacterBehavior for Data {
|
impl CharacterBehavior for Data {
|
||||||
fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
|
fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
|
|
||||||
|
handle_skating(data, &mut update);
|
||||||
handle_orientation(data, &mut update, 1.0, None);
|
handle_orientation(data, &mut update, 1.0, None);
|
||||||
handle_move(data, &mut update, if self.is_sneaking { 0.4 } else { 1.0 });
|
handle_move(data, &mut update, if self.is_sneaking { 0.4 } else { 1.0 });
|
||||||
handle_jump(data, output_events, &mut update, 1.0);
|
handle_jump(data, output_events, &mut update, 1.0);
|
||||||
@ -26,7 +32,10 @@ impl CharacterBehavior for Data {
|
|||||||
if self.is_sneaking
|
if self.is_sneaking
|
||||||
&& (data.physics.on_ground.is_none() || data.physics.in_liquid().is_some())
|
&& (data.physics.on_ground.is_none() || data.physics.in_liquid().is_some())
|
||||||
{
|
{
|
||||||
update.character = CharacterState::Idle(Data { is_sneaking: false });
|
update.character = CharacterState::Idle(Data {
|
||||||
|
is_sneaking: false,
|
||||||
|
footwear: self.footwear,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
update
|
update
|
||||||
@ -75,13 +84,16 @@ impl CharacterBehavior for Data {
|
|||||||
|
|
||||||
fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
fn sneak(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
update.character = CharacterState::Idle(Data { is_sneaking: true });
|
update.character = CharacterState::Idle(Data {
|
||||||
|
is_sneaking: true,
|
||||||
|
footwear: self.footwear,
|
||||||
|
});
|
||||||
update
|
update
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
update.character = CharacterState::Idle(Data { is_sneaking: false });
|
update.character = CharacterState::Idle(Data::default());
|
||||||
update
|
update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ pub mod roll;
|
|||||||
pub mod self_buff;
|
pub mod self_buff;
|
||||||
pub mod shockwave;
|
pub mod shockwave;
|
||||||
pub mod sit;
|
pub mod sit;
|
||||||
|
pub mod skate;
|
||||||
pub mod spin_melee;
|
pub mod spin_melee;
|
||||||
pub mod sprite_interact;
|
pub mod sprite_interact;
|
||||||
pub mod sprite_summon;
|
pub mod sprite_summon;
|
||||||
|
@ -142,6 +142,7 @@ impl CharacterBehavior for Data {
|
|||||||
} else {
|
} else {
|
||||||
CharacterState::Idle(idle::Data {
|
CharacterState::Idle(idle::Data {
|
||||||
is_sneaking: self.is_sneaking,
|
is_sneaking: self.is_sneaking,
|
||||||
|
footwear: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,6 +152,7 @@ impl CharacterBehavior for Data {
|
|||||||
// If it somehow ends up in an incorrect stage section
|
// If it somehow ends up in an incorrect stage section
|
||||||
update.character = CharacterState::Idle(idle::Data {
|
update.character = CharacterState::Idle(idle::Data {
|
||||||
is_sneaking: self.is_sneaking,
|
is_sneaking: self.is_sneaking,
|
||||||
|
footwear: None,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ impl CharacterBehavior for Data {
|
|||||||
|
|
||||||
// Try to Fall/Stand up/Move
|
// Try to Fall/Stand up/Move
|
||||||
if data.physics.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 {
|
if data.physics.on_ground.is_none() || data.inputs.move_dir.magnitude_squared() > 0.0 {
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: false });
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
update
|
update
|
||||||
@ -52,7 +52,7 @@ impl CharacterBehavior for Data {
|
|||||||
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
// Try to Fall/Stand up/Move
|
// Try to Fall/Stand up/Move
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: false });
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
update
|
update
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
118
common/src/states/skate.rs
Normal file
118
common/src/states/skate.rs
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
use super::utils::*;
|
||||||
|
use crate::{
|
||||||
|
comp::{
|
||||||
|
character_state::OutputEvents, item::armor::Friction, CharacterState, InventoryAction,
|
||||||
|
StateUpdate,
|
||||||
|
},
|
||||||
|
states::{
|
||||||
|
behavior::{CharacterBehavior, JoinData},
|
||||||
|
idle,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct Data {
|
||||||
|
pub(crate) footwear: Friction,
|
||||||
|
// hints for animation
|
||||||
|
pub turn: f32, // negative to left, positive to right, 1.0=45°
|
||||||
|
pub accelerate: f32, // negative to brake
|
||||||
|
pub sidewalk: f32, // negative to left
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Data {
|
||||||
|
pub fn new(_: &JoinData, footwear: Friction) -> Self {
|
||||||
|
Self {
|
||||||
|
footwear,
|
||||||
|
turn: Default::default(),
|
||||||
|
accelerate: Default::default(),
|
||||||
|
sidewalk: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CharacterBehavior for Data {
|
||||||
|
fn behavior(&self, data: &JoinData, output_events: &mut OutputEvents) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
|
||||||
|
handle_wield(data, &mut update);
|
||||||
|
handle_jump(data, output_events, &mut update, 1.0);
|
||||||
|
|
||||||
|
if !data.physics.skating_active {
|
||||||
|
update.character = CharacterState::Idle(idle::Data {
|
||||||
|
is_sneaking: false,
|
||||||
|
footwear: Some(self.footwear),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
let plane_ori = data.inputs.look_dir.xy();
|
||||||
|
let orthogonal = vek::Vec2::new(plane_ori.y, -plane_ori.x);
|
||||||
|
update.ori = vek::Vec3::new(plane_ori.x, plane_ori.y, 0.0).into();
|
||||||
|
let current_planar_velocity = data.vel.0.xy().magnitude();
|
||||||
|
let long_input = data.inputs.move_dir.dot(plane_ori);
|
||||||
|
let lat_input = data.inputs.move_dir.dot(orthogonal);
|
||||||
|
let acceleration = if long_input.abs() > lat_input.abs() {
|
||||||
|
if long_input > 0.0 {
|
||||||
|
if let CharacterState::Skate(data) = &mut update.character {
|
||||||
|
data.accelerate = 1.0;
|
||||||
|
data.sidewalk = 0.0;
|
||||||
|
}
|
||||||
|
// forward, max at 8u/s
|
||||||
|
(data.dt.0 * 3.0)
|
||||||
|
.min(8.0 - current_planar_velocity)
|
||||||
|
.max(0.0)
|
||||||
|
} else {
|
||||||
|
if let CharacterState::Skate(data) = &mut update.character {
|
||||||
|
data.accelerate = -1.0;
|
||||||
|
data.sidewalk = 0.0;
|
||||||
|
}
|
||||||
|
//brake up to 4u/s², but never backwards
|
||||||
|
(data.dt.0 * 4.0).min(current_planar_velocity)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let CharacterState::Skate(data) = &mut update.character {
|
||||||
|
data.accelerate = 0.0;
|
||||||
|
data.sidewalk = lat_input;
|
||||||
|
}
|
||||||
|
// sideways: constant speed
|
||||||
|
(0.5 - current_planar_velocity).max(0.0)
|
||||||
|
};
|
||||||
|
if let CharacterState::Skate(skate_data) = &mut update.character {
|
||||||
|
skate_data.turn = orthogonal.dot(data.vel.0.xy());
|
||||||
|
}
|
||||||
|
let delta_vel = acceleration * data.inputs.move_dir;
|
||||||
|
update.vel.0 += vek::Vec3::new(delta_vel.x, delta_vel.y, 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
|
fn manipulate_loadout(
|
||||||
|
&self,
|
||||||
|
data: &JoinData,
|
||||||
|
output_events: &mut OutputEvents,
|
||||||
|
inv_action: InventoryAction,
|
||||||
|
) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
handle_manipulate_loadout(data, output_events, &mut update, inv_action);
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wield(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
attempt_wield(data, &mut update);
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sit(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
attempt_sit(data, &mut update);
|
||||||
|
update
|
||||||
|
}
|
||||||
|
|
||||||
|
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
|
let mut update = StateUpdate::from(data);
|
||||||
|
// Try to Fall/Stand up/Move
|
||||||
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
|
update
|
||||||
|
}
|
||||||
|
}
|
@ -106,13 +106,14 @@ impl CharacterBehavior for Data {
|
|||||||
} else {
|
} else {
|
||||||
CharacterState::Idle(idle::Data {
|
CharacterState::Idle(idle::Data {
|
||||||
is_sneaking: self.static_data.was_sneak,
|
is_sneaking: self.static_data.was_sneak,
|
||||||
|
footwear: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
// If it somehow ends up in an incorrect stage section
|
// If it somehow ends up in an incorrect stage section
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: false });
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ impl CharacterBehavior for Data {
|
|||||||
update.character =
|
update.character =
|
||||||
CharacterState::Wielding(wielding::Data { is_sneaking: false });
|
CharacterState::Wielding(wielding::Data { is_sneaking: false });
|
||||||
} else {
|
} else {
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: false });
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -82,7 +82,7 @@ impl CharacterBehavior for Data {
|
|||||||
update.character =
|
update.character =
|
||||||
CharacterState::Wielding(wielding::Data { is_sneaking: false });
|
CharacterState::Wielding(wielding::Data { is_sneaking: false });
|
||||||
} else {
|
} else {
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: false });
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -42,14 +42,14 @@ impl CharacterBehavior for Data {
|
|||||||
|
|
||||||
fn sit(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
fn sit(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: false });
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
attempt_sit(data, &mut update);
|
attempt_sit(data, &mut update);
|
||||||
update
|
update
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
fn dance(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: false });
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
attempt_dance(data, &mut update);
|
attempt_dance(data, &mut update);
|
||||||
update
|
update
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ impl CharacterBehavior for Data {
|
|||||||
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
fn stand(&self, data: &JoinData, _: &mut OutputEvents) -> StateUpdate {
|
||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
// Try to Fall/Stand up/Move
|
// Try to Fall/Stand up/Move
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: false });
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
update
|
update
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,13 +132,14 @@ impl CharacterBehavior for Data {
|
|||||||
} else {
|
} else {
|
||||||
CharacterState::Idle(idle::Data {
|
CharacterState::Idle(idle::Data {
|
||||||
is_sneaking: self.static_data.was_sneak,
|
is_sneaking: self.static_data.was_sneak,
|
||||||
|
footwear: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
// If it somehow ends up in an incorrect stage section
|
// If it somehow ends up in an incorrect stage section
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: false });
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@ use crate::{
|
|||||||
comp::{
|
comp::{
|
||||||
arthropod, biped_large, biped_small,
|
arthropod, biped_large, biped_small,
|
||||||
character_state::OutputEvents,
|
character_state::OutputEvents,
|
||||||
inventory::slot::{EquipSlot, Slot},
|
inventory::slot::{ArmorSlot, EquipSlot, Slot},
|
||||||
item::{Hands, ItemKind, ToolKind},
|
item::{armor::Friction, Hands, ItemKind, ToolKind},
|
||||||
quadruped_low, quadruped_medium, quadruped_small,
|
quadruped_low, quadruped_medium, quadruped_small,
|
||||||
skills::{Skill, SwimSkill, SKILL_MODIFIERS},
|
skills::{Skill, SwimSkill, SKILL_MODIFIERS},
|
||||||
theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind,
|
theropod, Body, CharacterAbility, CharacterState, Density, InputAttr, InputKind,
|
||||||
@ -14,7 +14,7 @@ use crate::{
|
|||||||
consts::{FRIC_GROUND, GRAVITY, MAX_PICKUP_RANGE},
|
consts::{FRIC_GROUND, GRAVITY, MAX_PICKUP_RANGE},
|
||||||
event::{LocalEvent, ServerEvent},
|
event::{LocalEvent, ServerEvent},
|
||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
states::{behavior::JoinData, *},
|
states::{behavior::JoinData, utils::CharacterState::Idle, *},
|
||||||
util::Dir,
|
util::Dir,
|
||||||
vol::ReadVol,
|
vol::ReadVol,
|
||||||
};
|
};
|
||||||
@ -301,6 +301,35 @@ impl Body {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// set footwear in idle data and potential state change to Skate
|
||||||
|
pub fn handle_skating(data: &JoinData, update: &mut StateUpdate) {
|
||||||
|
if let Idle(crate::states::idle::Data {
|
||||||
|
is_sneaking,
|
||||||
|
mut footwear,
|
||||||
|
}) = data.character
|
||||||
|
{
|
||||||
|
if footwear.is_none() {
|
||||||
|
footwear = data.inventory.and_then(|inv| {
|
||||||
|
inv.equipped(EquipSlot::Armor(ArmorSlot::Feet))
|
||||||
|
.map(|armor| match armor.kind().as_ref() {
|
||||||
|
ItemKind::Armor(a) => a.ground_contact(),
|
||||||
|
_ => crate::comp::inventory::item::armor::Friction::Normal,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
update.character = Idle(crate::states::idle::Data {
|
||||||
|
is_sneaking: *is_sneaking,
|
||||||
|
footwear,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if data.physics.skating_active {
|
||||||
|
update.character = CharacterState::Skate(crate::states::skate::Data::new(
|
||||||
|
data,
|
||||||
|
footwear.unwrap_or(Friction::Normal),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 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) {
|
||||||
let submersion = data
|
let submersion = data
|
||||||
@ -635,7 +664,10 @@ pub fn attempt_talk(data: &JoinData<'_>, update: &mut StateUpdate) {
|
|||||||
|
|
||||||
pub fn attempt_sneak(data: &JoinData<'_>, update: &mut StateUpdate) {
|
pub fn attempt_sneak(data: &JoinData<'_>, update: &mut StateUpdate) {
|
||||||
if data.physics.on_ground.is_some() && data.body.is_humanoid() {
|
if data.physics.on_ground.is_some() && data.body.is_humanoid() {
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: true });
|
update.character = CharacterState::Idle(idle::Data {
|
||||||
|
is_sneaking: true,
|
||||||
|
footwear: data.character.footwear(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ impl CharacterBehavior for Data {
|
|||||||
|| data.physics.on_ground.is_some()
|
|| data.physics.on_ground.is_some()
|
||||||
|| data.physics.in_liquid().is_some()
|
|| data.physics.in_liquid().is_some()
|
||||||
{
|
{
|
||||||
update.character = CharacterState::Idle(idle::Data { is_sneaking: false });
|
update.character = CharacterState::Idle(idle::Data::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
update
|
update
|
||||||
|
@ -58,6 +58,7 @@ impl CharacterBehavior for Data {
|
|||||||
) => {
|
) => {
|
||||||
update.character = CharacterState::Idle(idle::Data {
|
update.character = CharacterState::Idle(idle::Data {
|
||||||
is_sneaking: self.is_sneaking,
|
is_sneaking: self.is_sneaking,
|
||||||
|
footwear: None,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
_ => (),
|
_ => (),
|
||||||
@ -76,6 +77,7 @@ impl CharacterBehavior for Data {
|
|||||||
let mut update = StateUpdate::from(data);
|
let mut update = StateUpdate::from(data);
|
||||||
update.character = CharacterState::Idle(idle::Data {
|
update.character = CharacterState::Idle(idle::Data {
|
||||||
is_sneaking: self.is_sneaking,
|
is_sneaking: self.is_sneaking,
|
||||||
|
footwear: None,
|
||||||
});
|
});
|
||||||
update
|
update
|
||||||
}
|
}
|
||||||
|
@ -213,7 +213,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
// If mounted, character state is controlled by mount
|
// If mounted, character state is controlled by mount
|
||||||
if is_rider.is_some() && !join_struct.char_state.can_perform_mounted() {
|
if is_rider.is_some() && !join_struct.char_state.can_perform_mounted() {
|
||||||
// TODO: A better way to swap between mount inputs and rider inputs
|
// TODO: A better way to swap between mount inputs and rider inputs
|
||||||
*join_struct.char_state = CharacterState::Idle(idle::Data { is_sneaking: false });
|
*join_struct.char_state = CharacterState::Idle(idle::Data::default());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ use common::{
|
|||||||
comp::{
|
comp::{
|
||||||
body::ship::figuredata::{VoxelCollider, VOXEL_COLLIDER_MANIFEST},
|
body::ship::figuredata::{VoxelCollider, VOXEL_COLLIDER_MANIFEST},
|
||||||
fluid_dynamics::{Fluid, LiquidKind, Wings},
|
fluid_dynamics::{Fluid, LiquidKind, Wings},
|
||||||
|
inventory::item::armor::Friction,
|
||||||
Body, CharacterState, Collider, Density, Immovable, Mass, Ori, PhysicsState, Pos,
|
Body, CharacterState, Collider, Density, Immovable, Mass, Ori, PhysicsState, Pos,
|
||||||
PosVelOriDefer, PreviousPhysCache, Projectile, Scale, Stats, Sticky, Vel,
|
PosVelOriDefer, PreviousPhysCache, Projectile, Scale, Stats, Sticky, Vel,
|
||||||
},
|
},
|
||||||
@ -12,7 +13,7 @@ use common::{
|
|||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
resources::DeltaTime,
|
resources::DeltaTime,
|
||||||
states,
|
states,
|
||||||
terrain::{Block, TerrainGrid},
|
terrain::{Block, BlockKind, TerrainGrid},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
util::{Projection, SpatialGrid},
|
util::{Projection, SpatialGrid},
|
||||||
vol::{BaseVol, ReadVol},
|
vol::{BaseVol, ReadVol},
|
||||||
@ -780,6 +781,13 @@ impl<'a> PhysicsData<'a> {
|
|||||||
1.0
|
1.0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if let Some(state) = character_state {
|
||||||
|
let footwear = state.footwear().unwrap_or(Friction::Normal);
|
||||||
|
if footwear != physics_state.footwear {
|
||||||
|
physics_state.footwear = footwear;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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)))
|
||||||
@ -846,6 +854,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
climbing,
|
climbing,
|
||||||
|entity, vel| land_on_ground = Some((entity, vel)),
|
|entity, vel| land_on_ground = Some((entity, vel)),
|
||||||
read,
|
read,
|
||||||
|
&ori,
|
||||||
);
|
);
|
||||||
tgt_pos = cpos.0;
|
tgt_pos = cpos.0;
|
||||||
},
|
},
|
||||||
@ -878,6 +887,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
climbing,
|
climbing,
|
||||||
|entity, vel| land_on_ground = Some((entity, vel)),
|
|entity, vel| land_on_ground = Some((entity, vel)),
|
||||||
read,
|
read,
|
||||||
|
&ori,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Sticky things shouldn't move when on a surface
|
// Sticky things shouldn't move when on a surface
|
||||||
@ -1142,6 +1152,7 @@ impl<'a> PhysicsData<'a> {
|
|||||||
Some((entity, Vel(ori_from.mul_direction(vel.0))));
|
Some((entity, Vel(ori_from.mul_direction(vel.0))));
|
||||||
},
|
},
|
||||||
read,
|
read,
|
||||||
|
&ori,
|
||||||
);
|
);
|
||||||
|
|
||||||
cpos.0 = transform_from.mul_point(cpos.0) + wpos;
|
cpos.0 = transform_from.mul_point(cpos.0) + wpos;
|
||||||
@ -1339,6 +1350,7 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
|||||||
climbing: bool,
|
climbing: bool,
|
||||||
mut land_on_ground: impl FnMut(Entity, Vel),
|
mut land_on_ground: impl FnMut(Entity, Vel),
|
||||||
read: &PhysicsRead,
|
read: &PhysicsRead,
|
||||||
|
ori: &Ori,
|
||||||
) {
|
) {
|
||||||
// FIXME: Review these
|
// FIXME: Review these
|
||||||
#![allow(
|
#![allow(
|
||||||
@ -1700,19 +1712,84 @@ fn box_voxel_collision<'a, T: BaseVol<Vox = Block> + ReadVol>(
|
|||||||
physics_state.on_wall = on_wall;
|
physics_state.on_wall = on_wall;
|
||||||
let fric_mod = read.stats.get(entity).map_or(1.0, |s| s.friction_modifier);
|
let fric_mod = read.stats.get(entity).map_or(1.0, |s| s.friction_modifier);
|
||||||
|
|
||||||
let ground_fric = physics_state
|
// skating (ski)
|
||||||
.on_ground
|
if !vel.0.xy().is_approx_zero()
|
||||||
.map(|b| b.get_friction())
|
&& physics_state
|
||||||
.unwrap_or(0.0);
|
.on_ground
|
||||||
let wall_fric = if physics_state.on_wall.is_some() && climbing {
|
.map_or(false, |g| physics_state.footwear.can_skate_on(g.kind()))
|
||||||
FRIC_GROUND
|
{
|
||||||
|
const DT_SCALE: f32 = 1.0; // other areas use 60.0???
|
||||||
|
const POTENTIAL_TO_KINETIC: f32 = 8.0; // * 2.0 * GRAVITY;
|
||||||
|
|
||||||
|
let kind = physics_state.on_ground.map_or(BlockKind::Air, |g| g.kind());
|
||||||
|
let (longitudinal_friction, lateral_friction) = physics_state.footwear.get_friction(kind);
|
||||||
|
// the amount of longitudinal speed preserved
|
||||||
|
let longitudinal_friction_factor_squared =
|
||||||
|
(1.0 - longitudinal_friction).powf(dt.0 * DT_SCALE * 2.0);
|
||||||
|
let lateral_friction_factor = (1.0 - lateral_friction).powf(dt.0 * DT_SCALE);
|
||||||
|
let groundplane_velocity = vel.0.xy();
|
||||||
|
let mut longitudinal_dir = ori.look_vec().xy();
|
||||||
|
if longitudinal_dir.is_approx_zero() {
|
||||||
|
// fall back to travelling dir (in case we look up)
|
||||||
|
longitudinal_dir = groundplane_velocity;
|
||||||
|
}
|
||||||
|
let longitudinal_dir = longitudinal_dir.normalized();
|
||||||
|
let lateral_dir = Vec2::new(longitudinal_dir.y, -longitudinal_dir.x);
|
||||||
|
let squared_velocity = groundplane_velocity.magnitude_squared();
|
||||||
|
// if we crossed an edge up or down accelerate in travelling direction,
|
||||||
|
// as potential energy is converted into kinetic energy we compare it with the
|
||||||
|
// square of velocity
|
||||||
|
let vertical_difference = physics_state.skating_last_height - pos.0.z;
|
||||||
|
// might become negative when skating slowly uphill
|
||||||
|
let height_factor_squared = if vertical_difference != 0.0 {
|
||||||
|
// E=½mv², we scale both energies by ½m
|
||||||
|
let kinetic = squared_velocity;
|
||||||
|
// positive accelerate, negative decelerate, ΔE=mgΔh
|
||||||
|
let delta_potential = vertical_difference.max(-1.0).min(2.0) * POTENTIAL_TO_KINETIC;
|
||||||
|
let new_energy = kinetic + delta_potential;
|
||||||
|
physics_state.skating_last_height = pos.0.z;
|
||||||
|
new_energy / kinetic
|
||||||
|
} else {
|
||||||
|
1.0
|
||||||
|
};
|
||||||
|
|
||||||
|
// we calculate these squared as we need to combined them Euclidianly anyway,
|
||||||
|
// skiing: separate speed into longitudinal and lateral component
|
||||||
|
let long_speed = groundplane_velocity.dot(longitudinal_dir);
|
||||||
|
let lat_speed = groundplane_velocity.dot(lateral_dir);
|
||||||
|
let long_speed_squared = long_speed.powi(2);
|
||||||
|
|
||||||
|
// lateral speed is reduced by lateral_friction,
|
||||||
|
let new_lateral = lat_speed * lateral_friction_factor;
|
||||||
|
let lateral_speed_reduction = lat_speed - new_lateral;
|
||||||
|
// we convert this reduction partically (by the cosine of the angle) into
|
||||||
|
// longitudinal (elastic collision) and the remainder into heat
|
||||||
|
let cosine_squared_aoa = long_speed_squared / squared_velocity;
|
||||||
|
let converted_lateral_squared = cosine_squared_aoa * lateral_speed_reduction.powi(2);
|
||||||
|
let new_longitudinal_squared = longitudinal_friction_factor_squared
|
||||||
|
* (long_speed_squared + converted_lateral_squared)
|
||||||
|
* height_factor_squared;
|
||||||
|
let new_longitudinal =
|
||||||
|
new_longitudinal_squared.signum() * new_longitudinal_squared.abs().sqrt();
|
||||||
|
let new_ground_speed = new_longitudinal * longitudinal_dir + new_lateral * lateral_dir;
|
||||||
|
physics_state.skating_active = true;
|
||||||
|
vel.0 = Vec3::new(new_ground_speed.x, new_ground_speed.y, 0.0);
|
||||||
} else {
|
} else {
|
||||||
0.0
|
let ground_fric = physics_state
|
||||||
};
|
.on_ground
|
||||||
let fric = ground_fric.max(wall_fric);
|
.map(|b| b.get_friction())
|
||||||
if fric > 0.0 {
|
.unwrap_or(0.0);
|
||||||
vel.0 *= (1.0 - fric.min(1.0) * fric_mod).powf(dt.0 * 60.0);
|
let wall_fric = if physics_state.on_wall.is_some() && climbing {
|
||||||
physics_state.ground_vel = ground_vel;
|
FRIC_GROUND
|
||||||
|
} else {
|
||||||
|
0.0
|
||||||
|
};
|
||||||
|
let fric = ground_fric.max(wall_fric);
|
||||||
|
if fric > 0.0 {
|
||||||
|
vel.0 *= (1.0 - fric.min(1.0) * fric_mod).powf(dt.0 * 60.0);
|
||||||
|
physics_state.ground_vel = ground_vel;
|
||||||
|
}
|
||||||
|
physics_state.skating_active = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
physics_state.in_fluid = liquid
|
physics_state.in_fluid = liquid
|
||||||
|
@ -147,6 +147,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
| CharacterState::Sit { .. }
|
| CharacterState::Sit { .. }
|
||||||
| CharacterState::Dance { .. }
|
| CharacterState::Dance { .. }
|
||||||
| CharacterState::Glide { .. }
|
| CharacterState::Glide { .. }
|
||||||
|
| CharacterState::Skate { .. }
|
||||||
| CharacterState::GlideWield { .. }
|
| CharacterState::GlideWield { .. }
|
||||||
| CharacterState::Wielding { .. }
|
| CharacterState::Wielding { .. }
|
||||||
| CharacterState::Equipping { .. }
|
| CharacterState::Equipping { .. }
|
||||||
|
@ -11,7 +11,7 @@ mod widgets;
|
|||||||
use client::{Client, Join, World, WorldExt};
|
use client::{Client, Join, World, WorldExt};
|
||||||
use common::{
|
use common::{
|
||||||
comp,
|
comp,
|
||||||
comp::{Poise, PoiseState},
|
comp::{inventory::item::armor::Friction, Poise, PoiseState},
|
||||||
};
|
};
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use egui::{
|
use egui::{
|
||||||
@ -708,6 +708,7 @@ fn selected_entity_window(
|
|||||||
Some(Fluid::Liquid { depth, kind, .. }) => format!("{:?} (Depth: {:.1})", kind, depth),
|
Some(Fluid::Liquid { depth, kind, .. }) => format!("{:?} (Depth: {:.1})", kind, depth),
|
||||||
_ => "None".to_owned() });
|
_ => "None".to_owned() });
|
||||||
});
|
});
|
||||||
|
two_col_row(ui, "Footwear", match physics_state.footwear{ Friction::Ski => "Ski", Friction::Skate => "Skate", /* Friction::Snowshoe => "Snowshoe", Friction::Spikes => "Spikes", */ Friction::Normal=>"Normal",}.to_string());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -93,7 +93,7 @@ fn same_previous_event_elapsed_emits() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn maps_idle() {
|
fn maps_idle() {
|
||||||
let result = MovementEventMapper::map_movement_event(
|
let result = MovementEventMapper::map_movement_event(
|
||||||
&CharacterState::Idle(common::states::idle::Data { is_sneaking: false }),
|
&CharacterState::Idle(common::states::idle::Data::default()),
|
||||||
&PhysicsState {
|
&PhysicsState {
|
||||||
on_ground: Some(Block::empty()),
|
on_ground: Some(Block::empty()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -115,7 +115,7 @@ fn maps_idle() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn maps_run_with_sufficient_velocity() {
|
fn maps_run_with_sufficient_velocity() {
|
||||||
let result = MovementEventMapper::map_movement_event(
|
let result = MovementEventMapper::map_movement_event(
|
||||||
&CharacterState::Idle(common::states::idle::Data { is_sneaking: false }),
|
&CharacterState::Idle(common::states::idle::Data::default()),
|
||||||
&PhysicsState {
|
&PhysicsState {
|
||||||
on_ground: Some(Block::empty()),
|
on_ground: Some(Block::empty()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -137,7 +137,7 @@ fn maps_run_with_sufficient_velocity() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn does_not_map_run_with_insufficient_velocity() {
|
fn does_not_map_run_with_insufficient_velocity() {
|
||||||
let result = MovementEventMapper::map_movement_event(
|
let result = MovementEventMapper::map_movement_event(
|
||||||
&CharacterState::Idle(common::states::idle::Data { is_sneaking: false }),
|
&CharacterState::Idle(common::states::idle::Data::default()),
|
||||||
&PhysicsState {
|
&PhysicsState {
|
||||||
on_ground: Some(Block::empty()),
|
on_ground: Some(Block::empty()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -159,7 +159,7 @@ fn does_not_map_run_with_insufficient_velocity() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn does_not_map_run_with_sufficient_velocity_but_not_on_ground() {
|
fn does_not_map_run_with_sufficient_velocity_but_not_on_ground() {
|
||||||
let result = MovementEventMapper::map_movement_event(
|
let result = MovementEventMapper::map_movement_event(
|
||||||
&CharacterState::Idle(common::states::idle::Data { is_sneaking: false }),
|
&CharacterState::Idle(common::states::idle::Data::default()),
|
||||||
&Default::default(),
|
&Default::default(),
|
||||||
&PreviousEntityState {
|
&PreviousEntityState {
|
||||||
event: SfxEvent::Idle,
|
event: SfxEvent::Idle,
|
||||||
@ -214,7 +214,7 @@ fn maps_roll() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn maps_land_on_ground_to_run() {
|
fn maps_land_on_ground_to_run() {
|
||||||
let result = MovementEventMapper::map_movement_event(
|
let result = MovementEventMapper::map_movement_event(
|
||||||
&CharacterState::Idle(common::states::idle::Data { is_sneaking: false }),
|
&CharacterState::Idle(common::states::idle::Data::default()),
|
||||||
&PhysicsState {
|
&PhysicsState {
|
||||||
on_ground: Some(Block::empty()),
|
on_ground: Some(Block::empty()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -976,9 +976,10 @@ impl FigureMgr {
|
|||||||
rel_vel.magnitude_squared() > 0.01, // Moving
|
rel_vel.magnitude_squared() > 0.01, // Moving
|
||||||
physics.in_liquid().is_some(), // In water
|
physics.in_liquid().is_some(), // In water
|
||||||
is_rider.is_some(),
|
is_rider.is_some(),
|
||||||
|
physics.skating_active,
|
||||||
) {
|
) {
|
||||||
// Standing
|
// Standing or Skating
|
||||||
(true, false, false, false) => {
|
(true, false, false, false, _) | (_, _, false, false, true) => {
|
||||||
anim::character::StandAnimation::update_skeleton(
|
anim::character::StandAnimation::update_skeleton(
|
||||||
&CharacterSkeleton::new(holding_lantern),
|
&CharacterSkeleton::new(holding_lantern),
|
||||||
(
|
(
|
||||||
@ -997,7 +998,7 @@ impl FigureMgr {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
// Running
|
// Running
|
||||||
(true, true, false, false) => {
|
(true, true, false, false, _) => {
|
||||||
anim::character::RunAnimation::update_skeleton(
|
anim::character::RunAnimation::update_skeleton(
|
||||||
&CharacterSkeleton::new(holding_lantern),
|
&CharacterSkeleton::new(holding_lantern),
|
||||||
(
|
(
|
||||||
@ -1019,7 +1020,7 @@ impl FigureMgr {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
// In air
|
// In air
|
||||||
(false, _, false, false) => {
|
(false, _, false, false, _) => {
|
||||||
anim::character::JumpAnimation::update_skeleton(
|
anim::character::JumpAnimation::update_skeleton(
|
||||||
&CharacterSkeleton::new(holding_lantern),
|
&CharacterSkeleton::new(holding_lantern),
|
||||||
(
|
(
|
||||||
@ -1038,7 +1039,7 @@ impl FigureMgr {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
// Swim
|
// Swim
|
||||||
(_, _, true, false) => anim::character::SwimAnimation::update_skeleton(
|
(_, _, true, false, _) => anim::character::SwimAnimation::update_skeleton(
|
||||||
&CharacterSkeleton::new(holding_lantern),
|
&CharacterSkeleton::new(holding_lantern),
|
||||||
(
|
(
|
||||||
active_tool_kind,
|
active_tool_kind,
|
||||||
@ -1056,7 +1057,7 @@ impl FigureMgr {
|
|||||||
skeleton_attr,
|
skeleton_attr,
|
||||||
),
|
),
|
||||||
// Mount
|
// Mount
|
||||||
(_, _, _, true) => anim::character::MountAnimation::update_skeleton(
|
(_, _, _, true, _) => anim::character::MountAnimation::update_skeleton(
|
||||||
&CharacterSkeleton::new(holding_lantern),
|
&CharacterSkeleton::new(holding_lantern),
|
||||||
(
|
(
|
||||||
active_tool_kind,
|
active_tool_kind,
|
||||||
@ -1260,7 +1261,9 @@ impl FigureMgr {
|
|||||||
skeleton_attr,
|
skeleton_attr,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
CharacterState::Idle(idle::Data { is_sneaking: true }) => {
|
CharacterState::Idle(idle::Data {
|
||||||
|
is_sneaking: true, ..
|
||||||
|
}) => {
|
||||||
anim::character::SneakAnimation::update_skeleton(
|
anim::character::SneakAnimation::update_skeleton(
|
||||||
&target_base,
|
&target_base,
|
||||||
(
|
(
|
||||||
@ -5257,16 +5260,11 @@ impl FigureMgr {
|
|||||||
// Average velocity relative to the current ground
|
// Average velocity relative to the current ground
|
||||||
let _rel_avg_vel = state.avg_vel - physics.ground_vel;
|
let _rel_avg_vel = state.avg_vel - physics.ground_vel;
|
||||||
|
|
||||||
|
let idlestate = CharacterState::Idle(common::states::idle::Data::default());
|
||||||
|
let last = Last(idlestate.clone());
|
||||||
let (character, last_character) = match (character, last_character) {
|
let (character, last_character) = match (character, last_character) {
|
||||||
(Some(c), Some(l)) => (c, l),
|
(Some(c), Some(l)) => (c, l),
|
||||||
_ => (
|
_ => (&idlestate, &last),
|
||||||
&CharacterState::Idle(common::states::idle::Data {
|
|
||||||
is_sneaking: false,
|
|
||||||
}),
|
|
||||||
&Last(CharacterState::Idle(common::states::idle::Data {
|
|
||||||
is_sneaking: false,
|
|
||||||
})),
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if !character.same_variant(&last_character.0) {
|
if !character.same_variant(&last_character.0) {
|
||||||
@ -5388,16 +5386,11 @@ impl FigureMgr {
|
|||||||
// Average velocity relative to the current ground
|
// Average velocity relative to the current ground
|
||||||
let _rel_avg_vel = state.avg_vel - physics.ground_vel;
|
let _rel_avg_vel = state.avg_vel - physics.ground_vel;
|
||||||
|
|
||||||
|
let idle_state = CharacterState::Idle(common::states::idle::Data::default());
|
||||||
|
let last = Last(idle_state.clone());
|
||||||
let (character, last_character) = match (character, last_character) {
|
let (character, last_character) = match (character, last_character) {
|
||||||
(Some(c), Some(l)) => (c, l),
|
(Some(c), Some(l)) => (c, l),
|
||||||
_ => (
|
_ => (&idle_state, &last),
|
||||||
&CharacterState::Idle(common::states::idle::Data {
|
|
||||||
is_sneaking: false,
|
|
||||||
}),
|
|
||||||
&Last(CharacterState::Idle(common::states::idle::Data {
|
|
||||||
is_sneaking: false,
|
|
||||||
})),
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if !character.same_variant(&last_character.0) {
|
if !character.same_variant(&last_character.0) {
|
||||||
@ -5486,16 +5479,11 @@ impl FigureMgr {
|
|||||||
// Average velocity relative to the current ground
|
// Average velocity relative to the current ground
|
||||||
let _rel_avg_vel = state.avg_vel - physics.ground_vel;
|
let _rel_avg_vel = state.avg_vel - physics.ground_vel;
|
||||||
|
|
||||||
|
let idlestate = CharacterState::Idle(common::states::idle::Data::default());
|
||||||
|
let last = Last(idlestate.clone());
|
||||||
let (character, last_character) = match (character, last_character) {
|
let (character, last_character) = match (character, last_character) {
|
||||||
(Some(c), Some(l)) => (c, l),
|
(Some(c), Some(l)) => (c, l),
|
||||||
_ => (
|
_ => (&idlestate, &last),
|
||||||
&CharacterState::Idle(common::states::idle::Data {
|
|
||||||
is_sneaking: false,
|
|
||||||
}),
|
|
||||||
&Last(CharacterState::Idle(common::states::idle::Data {
|
|
||||||
is_sneaking: false,
|
|
||||||
})),
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if !character.same_variant(&last_character.0) {
|
if !character.same_variant(&last_character.0) {
|
||||||
|
Loading…
Reference in New Issue
Block a user