Merge branch 'timo-rebase-slipped-on-midge' into 'master'

ECS organization + Rolling animation

See merge request veloren/veloren!235
This commit is contained in:
Joshua Barretto 2019-06-16 19:56:44 +00:00
commit ceff450233
38 changed files with 990 additions and 558 deletions

View File

@ -29,7 +29,7 @@ fn main() {
client.send_chat("Hello!".to_string());
loop {
let events = match client.tick(comp::Control::default(), clock.get_last_delta()) {
let events = match client.tick(comp::Controller::default(), clock.get_last_delta()) {
Ok(events) => events,
Err(err) => {
error!("Error: {:?}", err);

View File

@ -153,7 +153,7 @@ impl Client {
pub fn current_chunk(&self) -> Option<Arc<TerrainChunk>> {
let chunk_pos = Vec2::from(
self.state
.read_storage::<comp::phys::Pos>()
.read_storage::<comp::Pos>()
.get(self.entity)
.cloned()?
.0,
@ -171,52 +171,6 @@ impl Client {
self.postbox.send_message(ClientMsg::Chat(msg))
}
/// Jump locally, the new positions will be synced to the server
#[allow(dead_code)]
pub fn jump(&mut self) {
if self.client_state != ClientState::Character {
return;
}
self.state.write_component(self.entity, comp::Jumping);
}
/// Start to glide locally, animation will be synced
#[allow(dead_code)]
pub fn glide(&mut self, state: bool) {
if self.client_state != ClientState::Character {
return;
}
if state {
self.state.write_component(self.entity, comp::Gliding);
} else {
self.state
.ecs_mut()
.write_storage::<comp::Gliding>()
.remove(self.entity);
}
}
/// Start to attack
#[allow(dead_code)]
pub fn attack(&mut self) {
if self.client_state != ClientState::Character {
return;
}
// TODO: Test if attack is possible using timeout
self.state
.write_component(self.entity, comp::Attacking::start());
self.postbox.send_message(ClientMsg::Attack);
}
/// Tell the server the client wants to respawn.
#[allow(dead_code)]
pub fn respawn(&mut self) {
if self.client_state != ClientState::Dead {
return;
}
self.postbox.send_message(ClientMsg::Respawn)
}
/// Remove all cached terrain
#[allow(dead_code)]
pub fn clear_terrain(&mut self) {
@ -226,7 +180,11 @@ impl Client {
/// Execute a single client tick, handle input and update the game state by the given duration.
#[allow(dead_code)]
pub fn tick(&mut self, control: comp::Control, dt: Duration) -> Result<Vec<Event>, Error> {
pub fn tick(
&mut self,
controller: comp::Controller,
dt: Duration,
) -> Result<Vec<Event>, Error> {
// This tick function is the centre of the Veloren universe. Most client-side things are
// managed from here, and as such it's important that it stays organised. Please consult
// the core developers before making significant changes to this code. Here is the
@ -243,10 +201,8 @@ impl Client {
// 1) Handle input from frontend.
// Pass character actions from frontend input to the player's entity.
// TODO: Only do this if the entity already has a Inputs component!
if self.client_state == ClientState::Character {
self.state.write_component(self.entity, control.clone());
}
self.state.write_component(self.entity, controller.clone());
self.postbox.send_message(ClientMsg::Controller(controller));
// 2) Build up a list of events for this frame, to be passed to the frontend.
let mut frontend_events = Vec::new();
@ -262,7 +218,7 @@ impl Client {
// 5) Terrain
let pos = self
.state
.read_storage::<comp::phys::Pos>()
.read_storage::<comp::Pos>()
.get(self.entity)
.cloned();
if let (Some(pos), Some(view_distance)) = (pos, self.view_distance) {
@ -336,19 +292,6 @@ impl Client {
_ => {}
}
// Update the server about the player's current animation.
if let Some(animation_info) = self
.state
.ecs_mut()
.write_storage::<comp::AnimationInfo>()
.get_mut(self.entity)
{
if animation_info.changed {
self.postbox
.send_message(ClientMsg::PlayerAnimation(animation_info.clone()));
}
}
// Output debug metrics
if log_enabled!(log::Level::Info) && self.tick % 600 == 0 {
let metrics = self

View File

@ -7,6 +7,9 @@ pub enum Animation {
Jump,
Gliding,
Attack,
Roll,
Crun,
Cidle,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]

View File

@ -0,0 +1,16 @@
use specs::{Component, FlaggedStorage, VecStorage};
use vek::*;
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Controller {
pub move_dir: Vec2<f32>,
pub jump: bool,
pub attack: bool,
pub roll: bool,
pub glide: bool,
pub respawn: bool,
}
impl Component for Controller {
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}

View File

@ -1,29 +1,33 @@
use specs::{Component, FlaggedStorage, NullStorage, VecStorage};
use vek::*;
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Control {
pub move_dir: Vec2<f32>,
}
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Respawning;
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct MoveDir(pub Vec2<f32>);
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Attacking {
pub time: f32,
pub applied: bool,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Rolling {
pub time: f32,
pub applied: bool,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct OnGround;
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Jumping;
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct Gliding;
impl Component for Control {
type Storage = VecStorage<Self>;
}
impl Component for Respawning {
type Storage = NullStorage<Self>;
}
@ -36,14 +40,36 @@ impl Attacking {
}
}
}
impl Rolling {
pub fn start() -> Self {
Self {
time: 0.0,
applied: false,
}
}
}
impl Component for MoveDir {
type Storage = VecStorage<Self>;
}
impl Component for Attacking {
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}
impl Component for Rolling {
type Storage = FlaggedStorage<Self, VecStorage<Self>>;
}
impl Component for OnGround {
type Storage = NullStorage<Self>;
}
impl Component for Jumping {
type Storage = NullStorage<Self>;
}
impl Component for Gliding {
type Storage = NullStorage<Self>;
type Storage = FlaggedStorage<Self, NullStorage<Self>>;
}

View File

@ -1,26 +1,18 @@
pub mod actor;
pub mod agent;
pub mod animation;
pub mod inputs;
pub mod phys;
pub mod player;
pub mod stats;
mod agent;
mod animation;
mod controller;
mod inputs;
mod phys;
mod player;
mod stats;
// Reexports
pub use actor::Actor;
pub use actor::Body;
pub use actor::HumanoidBody;
pub use actor::QuadrupedBody;
pub use actor::QuadrupedMediumBody;
pub use actor::{Actor, Body, HumanoidBody, QuadrupedBody, QuadrupedMediumBody};
pub use agent::Agent;
pub use animation::Animation;
pub use animation::AnimationInfo;
pub use inputs::Attacking;
pub use inputs::Control;
pub use inputs::Gliding;
pub use inputs::Jumping;
pub use inputs::Respawning;
pub use animation::{Animation, AnimationInfo};
pub use controller::Controller;
pub use inputs::{Attacking, Gliding, Jumping, MoveDir, OnGround, Respawning, Rolling};
pub use phys::{ForceUpdate, Ori, Pos, Vel};
pub use player::Player;
pub use stats::Dying;
pub use stats::HealthSource;
pub use stats::Stats;
pub use stats::{Dying, HealthSource, Stats};

View File

@ -2,7 +2,6 @@ use specs::{Component, NullStorage, VecStorage};
use vek::*;
// Position
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct Pos(pub Vec3<f32>);
@ -11,7 +10,6 @@ impl Component for Pos {
}
// Velocity
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct Vel(pub Vec3<f32>);
@ -20,7 +18,6 @@ impl Component for Vel {
}
// Orientation
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct Ori(pub Vec3<f32>);
@ -29,7 +26,6 @@ impl Component for Ori {
}
// ForceUpdate
#[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)]
pub struct ForceUpdate;

View File

@ -11,18 +11,16 @@ pub enum ClientMsg {
name: String,
body: comp::Body,
},
Attack,
Respawn,
Controller(comp::Controller),
RequestState(ClientState),
SetViewDistance(u32),
Ping,
Pong,
Chat(String),
PlayerAnimation(comp::AnimationInfo),
PlayerPhysics {
pos: comp::phys::Pos,
vel: comp::phys::Vel,
ori: comp::phys::Ori,
pos: comp::Pos,
vel: comp::Vel,
ori: comp::Ori,
},
TerrainChunkRequest {
key: Vec2<i32>,

View File

@ -17,13 +17,15 @@ impl sphynx::ResPacket for EcsResPacket {}
sphynx::sum_type! {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum EcsCompPacket {
Pos(comp::phys::Pos),
Vel(comp::phys::Vel),
Ori(comp::phys::Ori),
Pos(comp::Pos),
Vel(comp::Vel),
Ori(comp::Ori),
Actor(comp::Actor),
Player(comp::Player),
Stats(comp::Stats),
Attacking(comp::Attacking),
Rolling(comp::Rolling),
Gliding(comp::Gliding),
}
}
// Automatically derive From<T> for EcsCompPhantom
@ -31,13 +33,15 @@ sphynx::sum_type! {
sphynx::sum_type! {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum EcsCompPhantom {
Pos(PhantomData<comp::phys::Pos>),
Vel(PhantomData<comp::phys::Vel>),
Ori(PhantomData<comp::phys::Ori>),
Pos(PhantomData<comp::Pos>),
Vel(PhantomData<comp::Vel>),
Ori(PhantomData<comp::Ori>),
Actor(PhantomData<comp::Actor>),
Player(PhantomData<comp::Player>),
Stats(PhantomData<comp::Stats>),
Attacking(PhantomData<comp::Attacking>),
Rolling(PhantomData<comp::Rolling>),
Gliding(PhantomData<comp::Gliding>),
}
}
impl sphynx::CompPacket for EcsCompPacket {

View File

@ -32,9 +32,9 @@ pub enum ServerMsg {
EcsSync(sphynx::SyncPackage<EcsCompPacket, EcsResPacket>),
EntityPhysics {
entity: u64,
pos: comp::phys::Pos,
vel: comp::phys::Vel,
ori: comp::phys::Ori,
pos: comp::Pos,
vel: comp::Vel,
ori: comp::Ori,
},
EntityAnimation {
entity: u64,

View File

@ -98,28 +98,31 @@ impl State {
// Create a new Sphynx ECS world.
fn setup_sphynx_world(ecs: &mut sphynx::World<EcsCompPacket, EcsResPacket>) {
// Register synced components.
// Register server->client synced components.
ecs.register_synced::<comp::Actor>();
ecs.register_synced::<comp::Player>();
ecs.register_synced::<comp::Stats>();
ecs.register_synced::<comp::Attacking>(); // TODO: Don't send this to the client?
ecs.register::<comp::phys::ForceUpdate>();
ecs.register_synced::<comp::Attacking>();
ecs.register_synced::<comp::Rolling>();
ecs.register_synced::<comp::Gliding>();
// Register components synced by other means
ecs.register::<comp::phys::Pos>();
ecs.register::<comp::phys::Vel>();
ecs.register::<comp::phys::Ori>();
ecs.register::<comp::Pos>();
ecs.register::<comp::Vel>();
ecs.register::<comp::Ori>();
ecs.register::<comp::MoveDir>();
ecs.register::<comp::OnGround>();
ecs.register::<comp::AnimationInfo>();
ecs.register::<comp::Controller>();
// Register client-local components
ecs.register::<comp::Control>();
ecs.register::<comp::Jumping>();
// Register server-local components
ecs.register::<comp::Agent>();
ecs.register::<comp::Respawning>();
ecs.register::<comp::Gliding>();
ecs.register::<comp::Dying>();
ecs.register::<comp::ForceUpdate>();
ecs.register::<inventory::Inventory>();
// Register synced resources used by the ECS.

View File

@ -1,29 +0,0 @@
use crate::{comp::Attacking, state::DeltaTime};
use specs::{Entities, Join, Read, System, WriteStorage};
// Basic ECS AI agent system
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
Read<'a, DeltaTime>,
WriteStorage<'a, Attacking>,
);
fn run(&mut self, (entities, dt, mut attacks): Self::SystemData) {
for attack in (&mut attacks).join() {
attack.time += dt.0;
}
let finished_attacks = (&entities, &mut attacks)
.join()
.filter(|(_, a)| a.time > 0.25) // TODO: constant
.map(|(e, _)| e)
.collect::<Vec<_>>();
for entity in finished_attacks {
attacks.remove(entity);
}
}
}

View File

@ -1,28 +1,27 @@
use crate::comp::{phys::Pos, Agent, Attacking, Control, Jumping};
use crate::comp::{Agent, Attacking, Controller, Jumping, Pos};
use log::warn;
use rand::{seq::SliceRandom, thread_rng};
use specs::{Entities, Join, ReadStorage, System, WriteStorage};
use vek::*;
// Basic ECS AI agent system
/// This system will allow NPCs to modify their controller
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
WriteStorage<'a, Agent>,
ReadStorage<'a, Pos>,
WriteStorage<'a, Control>,
WriteStorage<'a, Controller>,
WriteStorage<'a, Jumping>,
WriteStorage<'a, Attacking>,
);
fn run(
&mut self,
(entities, mut agents, positions, mut controls, mut jumps, mut attacks): Self::SystemData,
(entities, mut agents, positions, mut controllers, mut jumps, mut attacks): Self::SystemData,
) {
for (entity, agent, pos, control) in
(&entities, &mut agents, &positions, &mut controls).join()
for (entity, agent, pos, controller) in
(&entities, &mut agents, &positions, &mut controllers).join()
{
match agent {
Agent::Wanderer(bearing) => {
@ -32,7 +31,7 @@ impl<'a> System<'a> for Sys {
- pos.0 * 0.0002;
if bearing.magnitude_squared() != 0.0 {
control.move_dir = bearing.normalized();
controller.move_dir = bearing.normalized();
}
}
Agent::Pet { target, offset } => {
@ -49,7 +48,7 @@ impl<'a> System<'a> for Sys {
// Move towards the target.
let dist: f32 = Vec2::from(tgt_pos - pos.0).magnitude();
control.move_dir = if dist > 5.0 {
controller.move_dir = if dist > 5.0 {
Vec2::from(tgt_pos - pos.0).normalized()
} else if dist < 1.5 && dist > 0.0 {
Vec2::from(pos.0 - tgt_pos).normalized()
@ -57,7 +56,7 @@ impl<'a> System<'a> for Sys {
Vec2::zero()
};
}
_ => control.move_dir = Vec2::zero(),
_ => controller.move_dir = Vec2::zero(),
}
// Change offset occasionally.
@ -72,7 +71,7 @@ impl<'a> System<'a> for Sys {
Some(tgt_pos) => {
let dist = Vec2::<f32>::from(tgt_pos.0 - pos.0).magnitude();
if dist < 2.0 {
control.move_dir = Vec2::zero();
controller.move_dir = Vec2::zero();
if rand::random::<f32>() < 0.2 {
attacks
@ -82,7 +81,7 @@ impl<'a> System<'a> for Sys {
false
} else if dist < 60.0 {
control.move_dir =
controller.move_dir =
Vec2::<f32>::from(tgt_pos.0 - pos.0).normalized() * 0.96;
false
@ -91,7 +90,7 @@ impl<'a> System<'a> for Sys {
}
}
None => {
control.move_dir = Vec2::one();
controller.move_dir = Vec2::one();
true
}
};

View File

@ -1,15 +1,87 @@
use crate::{comp::AnimationInfo, state::DeltaTime};
use specs::{Join, Read, System, WriteStorage};
use crate::{
comp::{
Animation, AnimationInfo, Attacking, ForceUpdate, Gliding, Jumping, OnGround, Ori, Pos,
Rolling, Vel,
},
state::DeltaTime,
};
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
// Basic ECS AI agent system
/// This system will apply the animation that fits best to the users actions
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (Read<'a, DeltaTime>, WriteStorage<'a, AnimationInfo>);
type SystemData = (
Entities<'a>,
Read<'a, DeltaTime>,
ReadStorage<'a, Vel>,
ReadStorage<'a, OnGround>,
ReadStorage<'a, Jumping>,
ReadStorage<'a, Gliding>,
ReadStorage<'a, Attacking>,
ReadStorage<'a, Rolling>,
WriteStorage<'a, AnimationInfo>,
);
fn run(&mut self, (dt, mut animation_infos): Self::SystemData) {
for mut animation_info in (&mut animation_infos).join() {
fn run(
&mut self,
(
entities,
dt,
velocities,
on_grounds,
jumpings,
glidings,
attackings,
rollings,
mut animation_infos,
): Self::SystemData,
) {
for (entity, vel, on_ground, jumping, gliding, attacking, rolling, mut animation_info) in (
&entities,
&velocities,
on_grounds.maybe(),
jumpings.maybe(),
glidings.maybe(),
attackings.maybe(),
rollings.maybe(),
&mut animation_infos,
)
.join()
{
animation_info.time += dt.0 as f64;
fn impossible_animation(message: &str) -> Animation {
warn!("{}", message);
Animation::Idle
}
let animation = match (
on_ground.is_some(),
vel.0.magnitude() > 3.0, // Moving
attacking.is_some(),
gliding.is_some(),
rolling.is_some(),
) {
(_, _, true, true, _) => impossible_animation("Attack while gliding"),
(_, _, true, _, true) => impossible_animation("Roll while attacking"),
(_, _, _, true, true) => impossible_animation("Roll while gliding"),
(_, false, _, _, true) => impossible_animation("Roll without moving"),
(_, true, false, false, true) => Animation::Roll,
(true, false, false, false, false) => Animation::Idle,
(true, true, false, false, false) => Animation::Run,
(false, _, false, false, false) => Animation::Jump,
(_, _, false, true, false) => Animation::Gliding,
(_, _, true, false, false) => Animation::Attack,
};
let last = animation_info.clone();
let changed = last.animation != animation;
*animation_info = AnimationInfo {
animation,
time: if changed { 0.0 } else { last.time },
changed,
};
}
}
}

77
common/src/sys/combat.rs Normal file
View File

@ -0,0 +1,77 @@
use crate::{
comp::{
Attacking, HealthSource, Stats, {ForceUpdate, Ori, Pos, Vel},
},
state::{DeltaTime, Uid},
};
use log::warn;
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
/// This system is responsible for handling accepted inputs like moving or attacking
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
ReadStorage<'a, Uid>,
Read<'a, DeltaTime>,
ReadStorage<'a, Pos>,
ReadStorage<'a, Ori>,
WriteStorage<'a, Vel>,
WriteStorage<'a, Attacking>,
WriteStorage<'a, Stats>,
WriteStorage<'a, ForceUpdate>,
);
fn run(
&mut self,
(
entities,
uids,
dt,
positions,
orientations,
mut velocities,
mut attackings,
mut stats,
mut force_updates,
): Self::SystemData,
) {
// Attacks
(&entities, &uids, &positions, &orientations, &mut attackings)
.join()
.filter_map(|(entity, uid, pos, ori, mut attacking)| {
if !attacking.applied {
// Go through all other entities
for (b, pos_b, mut vel_b, mut stat_b) in
(&entities, &positions, &mut velocities, &mut stats).join()
{
// Check if it is a hit
if entity != b
&& !stat_b.is_dead
&& pos.0.distance_squared(pos_b.0) < 50.0
&& ori.0.angle_between(pos_b.0 - pos.0).to_degrees() < 70.0
{
// Deal damage
stat_b.hp.change_by(-10, HealthSource::Attack { by: *uid }); // TODO: variable damage and weapon
vel_b.0 += (pos_b.0 - pos.0).normalized() * 10.0;
vel_b.0.z = 15.0;
let _ = force_updates.insert(b, ForceUpdate);
}
}
attacking.applied = true;
}
if attacking.time > 0.5 {
Some(entity)
} else {
attacking.time += dt.0;
None
}
})
.collect::<Vec<_>>()
.into_iter()
.for_each(|e| {
attackings.remove(e);
});
}
}

View File

@ -0,0 +1,117 @@
use crate::{
comp::{
Animation, AnimationInfo, Attacking, Controller, Gliding, HealthSource, Jumping, MoveDir,
OnGround, Respawning, Rolling, Stats, {ForceUpdate, Ori, Pos, Vel},
},
state::DeltaTime,
};
use specs::{Entities, Join, Read, ReadStorage, System, WriteStorage};
/// This system is responsible for validating controller inputs
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
Read<'a, DeltaTime>,
ReadStorage<'a, Controller>,
ReadStorage<'a, Stats>,
ReadStorage<'a, Pos>,
ReadStorage<'a, Vel>,
ReadStorage<'a, Ori>,
ReadStorage<'a, OnGround>,
WriteStorage<'a, MoveDir>,
WriteStorage<'a, Jumping>,
WriteStorage<'a, Attacking>,
WriteStorage<'a, Rolling>,
WriteStorage<'a, Respawning>,
WriteStorage<'a, Gliding>,
);
fn run(
&mut self,
(
entities,
dt,
controllers,
stats,
positions,
velocities,
orientations,
on_grounds,
mut move_dirs,
mut jumpings,
mut attackings,
mut rollings,
mut respawns,
mut glidings,
): Self::SystemData,
) {
for (entity, controller, stats, pos, vel, ori, on_ground) in (
&entities,
&controllers,
&stats,
&positions,
&velocities,
&orientations,
on_grounds.maybe(),
)
.join()
{
if stats.is_dead {
// Respawn
if controller.respawn {
respawns.insert(entity, Respawning);
}
continue;
}
// Move dir
if rollings.get(entity).is_none() {
move_dirs.insert(
entity,
MoveDir(if controller.move_dir.magnitude() > 1.0 {
controller.move_dir.normalized()
} else {
controller.move_dir
}),
);
}
// Glide
if controller.glide
&& on_ground.is_none()
&& attackings.get(entity).is_none()
&& rollings.get(entity).is_none()
{
glidings.insert(entity, Gliding);
} else {
glidings.remove(entity);
}
// Attack
if controller.attack
&& attackings.get(entity).is_none()
&& glidings.get(entity).is_none()
&& rollings.get(entity).is_none()
{
attackings.insert(entity, Attacking::start());
}
// Jump
if controller.jump && on_ground.is_some() && vel.0.z <= 0.0 {
jumpings.insert(entity, Jumping);
}
// Roll
if controller.roll
&& rollings.get(entity).is_none()
&& attackings.get(entity).is_none()
&& glidings.get(entity).is_none()
&& on_ground.is_some()
&& vel.0.magnitude() > 5.0
{
rollings.insert(entity, Rolling::start());
}
}
}
}

View File

@ -1,174 +0,0 @@
use crate::{
comp::{
phys::{ForceUpdate, Ori, Pos, Vel},
Animation, AnimationInfo, Attacking, Control, Gliding, HealthSource, Jumping, Stats,
},
state::{DeltaTime, Uid},
terrain::TerrainMap,
vol::{ReadVol, Vox},
};
use log::warn;
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
use vek::*;
// Basic ECS AI agent system
pub struct Sys;
const HUMANOID_ACCEL: f32 = 100.0;
const HUMANOID_SPEED: f32 = 500.0;
const HUMANOID_AIR_ACCEL: f32 = 10.0;
const HUMANOID_AIR_SPEED: f32 = 100.0;
const HUMANOID_JUMP_ACCEL: f32 = 16.0;
const GLIDE_ACCEL: f32 = 15.0;
const GLIDE_SPEED: f32 = 45.0;
// Gravity is 9.81 * 4, so this makes gravity equal to .15
const GLIDE_ANTIGRAV: f32 = 9.81 * 3.95;
impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
ReadStorage<'a, Uid>,
Read<'a, DeltaTime>,
ReadExpect<'a, TerrainMap>,
ReadStorage<'a, Pos>,
WriteStorage<'a, Vel>,
WriteStorage<'a, Ori>,
WriteStorage<'a, AnimationInfo>,
WriteStorage<'a, Stats>,
ReadStorage<'a, Control>,
WriteStorage<'a, Jumping>,
WriteStorage<'a, Gliding>,
WriteStorage<'a, Attacking>,
WriteStorage<'a, ForceUpdate>,
);
fn run(
&mut self,
(
entities,
uids,
dt,
terrain,
positions,
mut velocities,
mut orientations,
mut animation_infos,
mut stats,
controls,
mut jumps,
glides,
mut attacks,
mut force_updates,
): Self::SystemData,
) {
for (entity, pos, control, stats, mut ori, mut vel) in (
&entities,
&positions,
&controls,
&stats,
&mut orientations,
&mut velocities,
)
.join()
{
// Disable while dead TODO: Replace with client states
if stats.is_dead {
continue;
}
let on_ground = terrain
.get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32))
.map(|vox| !vox.is_empty())
.unwrap_or(false)
&& vel.0.z <= 0.0;
let gliding = glides.get(entity).is_some() && vel.0.z < 0.0;
let move_dir = if control.move_dir.magnitude() > 1.0 {
control.move_dir.normalized()
} else {
control.move_dir
};
if on_ground {
// Move player according to move_dir
if vel.0.magnitude() < HUMANOID_SPEED {
vel.0 += Vec2::broadcast(dt.0) * move_dir * HUMANOID_ACCEL;
}
// Jump
if jumps.get(entity).is_some() && vel.0.z <= 0.0 {
vel.0.z = HUMANOID_JUMP_ACCEL;
jumps.remove(entity);
}
} else if gliding && vel.0.magnitude() < GLIDE_SPEED {
let anti_grav = GLIDE_ANTIGRAV + vel.0.z.powf(2.0) * 0.2;
vel.0.z += dt.0 * anti_grav * Vec2::<f32>::from(vel.0 * 0.15).magnitude().min(1.0);
vel.0 += Vec2::broadcast(dt.0) * move_dir * GLIDE_ACCEL;
} else if vel.0.magnitude() < HUMANOID_AIR_SPEED {
vel.0 += Vec2::broadcast(dt.0) * move_dir * HUMANOID_AIR_ACCEL;
}
// Set direction based on velocity
if vel.0.magnitude_squared() != 0.0 {
ori.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0);
}
let animation = if on_ground {
if control.move_dir.magnitude() > 0.01 {
Animation::Run
} else if attacks.get(entity).is_some() {
Animation::Attack
} else {
Animation::Idle
}
} else if glides.get(entity).is_some() {
Animation::Gliding
} else {
Animation::Jump
};
let last = animation_infos
.get_mut(entity)
.cloned()
.unwrap_or(AnimationInfo::default());
let changed = last.animation != animation;
if let Err(err) = animation_infos.insert(
entity,
AnimationInfo {
animation,
time: if changed { 0.0 } else { last.time },
changed,
},
) {
warn!("Inserting AnimationInfo for an entity failed: {:?}", err);
}
}
for (entity, &uid, pos, ori, attacking) in
(&entities, &uids, &positions, &orientations, &mut attacks).join()
{
if !attacking.applied {
for (b, pos_b, stat_b, mut vel_b) in
(&entities, &positions, &mut stats, &mut velocities).join()
{
// Check if it is a hit
if entity != b
&& !stat_b.is_dead
&& pos.0.distance_squared(pos_b.0) < 50.0
&& ori.0.angle_between(pos_b.0 - pos.0).to_degrees() < 70.0
{
// Deal damage
stat_b.hp.change_by(-10, HealthSource::Attack { by: uid }); // TODO: variable damage and weapon
vel_b.0 += (pos_b.0 - pos.0).normalized() * 10.0;
vel_b.0.z = 15.0;
if let Err(err) = force_updates.insert(b, ForceUpdate) {
warn!("Inserting ForceUpdate for an entity failed: {:?}", err);
}
}
}
attacking.applied = true;
}
}
}
}

View File

@ -1,7 +1,7 @@
pub mod actions;
pub mod agent;
pub mod animation;
pub mod inputs;
pub mod combat;
pub mod controller;
pub mod phys;
mod stats;
@ -10,17 +10,17 @@ use specs::DispatcherBuilder;
// System names
const AGENT_SYS: &str = "agent_sys";
const INPUTS_SYS: &str = "inputs_sys";
const ACTIONS_SYS: &str = "actions_sys";
const CONTROLLER_SYS: &str = "controller_sys";
const PHYS_SYS: &str = "phys_sys";
const COMBAT_SYS: &str = "combat_sys";
const ANIMATION_SYS: &str = "animation_sys";
const STATS_SYS: &str = "stats_sys";
pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
dispatch_builder.add(agent::Sys, AGENT_SYS, &[]);
dispatch_builder.add(controller::Sys, CONTROLLER_SYS, &[]);
dispatch_builder.add(phys::Sys, PHYS_SYS, &[]);
dispatch_builder.add(actions::Sys, ACTIONS_SYS, &[]);
dispatch_builder.add(inputs::Sys, INPUTS_SYS, &[]);
dispatch_builder.add(combat::Sys, COMBAT_SYS, &[]);
dispatch_builder.add(animation::Sys, ANIMATION_SYS, &[]);
dispatch_builder.add(stats::Sys, STATS_SYS, &[INPUTS_SYS]);
dispatch_builder.add(stats::Sys, STATS_SYS, &[COMBAT_SYS]);
}

View File

@ -1,21 +1,26 @@
use crate::{
comp::{
phys::{Pos, Vel},
Stats,
},
comp::{Gliding, Jumping, MoveDir, OnGround, Ori, Pos, Rolling, Stats, Vel},
state::DeltaTime,
terrain::TerrainMap,
vol::{ReadVol, Vox},
};
use specs::{Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
use specs::{Entities, Join, Read, ReadExpect, ReadStorage, System, WriteStorage};
use vek::*;
// Basic ECS physics system
pub struct Sys;
const GRAVITY: f32 = 9.81 * 4.0;
const FRIC_GROUND: f32 = 0.15;
const FRIC_AIR: f32 = 0.015;
const HUMANOID_ACCEL: f32 = 70.0;
const HUMANOID_SPEED: f32 = 120.0;
const HUMANOID_AIR_ACCEL: f32 = 10.0;
const HUMANOID_AIR_SPEED: f32 = 100.0;
const HUMANOID_JUMP_ACCEL: f32 = 16.0;
const ROLL_ACCEL: f32 = 160.0;
const ROLL_SPEED: f32 = 550.0;
const GLIDE_ACCEL: f32 = 15.0;
const GLIDE_SPEED: f32 = 45.0;
// Gravity is 9.81 * 4, so this makes gravity equal to .15
const GLIDE_ANTIGRAV: f32 = 9.81 * 3.95;
// Integrates forces, calculates the new velocity based off of the old velocity
// dt = delta time
@ -38,34 +43,128 @@ fn integrate_forces(dt: f32, mut lv: Vec3<f32>, damp: f32) -> Vec3<f32> {
lv
}
/// This system applies forces and calculates new positions and velocities.
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,
ReadExpect<'a, TerrainMap>,
Read<'a, DeltaTime>,
ReadStorage<'a, MoveDir>,
ReadStorage<'a, Gliding>,
ReadStorage<'a, Stats>,
WriteStorage<'a, Jumping>,
WriteStorage<'a, Rolling>,
WriteStorage<'a, OnGround>,
WriteStorage<'a, Pos>,
WriteStorage<'a, Vel>,
WriteStorage<'a, Ori>,
);
fn run(&mut self, (terrain, dt, stats, mut positions, mut velocities): Self::SystemData) {
for (stats, pos, vel) in (&stats, &mut positions, &mut velocities).join() {
// Disable while dead TODO: Replace with client states
fn run(
&mut self,
(
entities,
terrain,
dt,
move_dirs,
glidings,
stats,
mut jumpings,
mut rollings,
mut on_grounds,
mut positions,
mut velocities,
mut orientations,
): Self::SystemData,
) {
// Apply movement inputs
for (entity, stats, move_dir, gliding, mut pos, mut vel, mut ori) in (
&entities,
&stats,
move_dirs.maybe(),
glidings.maybe(),
&mut positions,
&mut velocities,
&mut orientations,
)
.join()
{
// Disable while dead TODO: Replace with client states?
if stats.is_dead {
continue;
}
let on_ground = terrain
.get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32))
.map(|vox| !vox.is_empty())
.unwrap_or(false)
&& vel.0.z <= 0.0;
// Move player according to move_dir
if let Some(move_dir) = move_dir {
vel.0 += Vec2::broadcast(dt.0)
* move_dir.0
* match (
on_grounds.get(entity).is_some(),
glidings.get(entity).is_some(),
rollings.get(entity).is_some(),
) {
(true, false, false) if vel.0.magnitude() < HUMANOID_SPEED => {
HUMANOID_ACCEL
}
(false, true, false) if vel.0.magnitude() < GLIDE_SPEED => GLIDE_ACCEL,
(false, false, false) if vel.0.magnitude() < HUMANOID_AIR_SPEED => {
HUMANOID_AIR_ACCEL
}
(true, false, true) if vel.0.magnitude() < ROLL_SPEED => ROLL_ACCEL,
_ => 0.0,
};
}
// Jump
if jumpings.get(entity).is_some() {
vel.0.z = HUMANOID_JUMP_ACCEL;
jumpings.remove(entity);
}
// Glide
if gliding.is_some() && vel.0.magnitude() < GLIDE_SPEED && vel.0.z < 0.0 {
let lift = GLIDE_ANTIGRAV + vel.0.z.powf(2.0) * 0.2;
vel.0.z += dt.0 * lift * Vec2::<f32>::from(vel.0 * 0.15).magnitude().min(1.0);
}
// Roll
if let Some(time) = rollings.get_mut(entity).map(|r| &mut r.time) {
*time += dt.0;
if *time > 0.55 {
rollings.remove(entity);
}
}
// Set direction based on velocity
if vel.0.magnitude_squared() != 0.0 {
ori.0 = vel.0.normalized() * Vec3::new(1.0, 1.0, 0.0);
}
// Movement
pos.0 += vel.0 * dt.0;
// Update OnGround component
if terrain
.get((pos.0 - Vec3::unit_z() * 0.1).map(|e| e.floor() as i32))
.map(|vox| !vox.is_empty())
.unwrap_or(false)
&& vel.0.z <= 0.0
{
on_grounds.insert(entity, OnGround);
} else {
on_grounds.remove(entity);
}
// Integrate forces
// Friction is assumed to be a constant dependent on location
let friction = 50.0 * if on_ground { FRIC_GROUND } else { FRIC_AIR };
let friction = 50.0
* if on_grounds.get(entity).is_some() {
FRIC_GROUND
} else {
FRIC_AIR
};
vel.0 = integrate_forces(dt.0, vel.0, friction);
// Basic collision with terrain

View File

@ -5,9 +5,8 @@ use crate::{
use log::warn;
use specs::{Entities, Join, Read, System, WriteStorage};
// Basic ECS AI agent system
/// This system kills players
pub struct Sys;
impl<'a> System<'a> for Sys {
type SystemData = (
Entities<'a>,

View File

@ -100,18 +100,12 @@ fn handle_jump(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
let (opt_x, opt_y, opt_z) = scan_fmt!(&args, action.arg_fmt, f32, f32, f32);
match (opt_x, opt_y, opt_z) {
(Some(x), Some(y), Some(z)) => {
match server
.state
.read_component_cloned::<comp::phys::Pos>(entity)
{
match server.state.read_component_cloned::<comp::Pos>(entity) {
Some(current_pos) => {
server.state.write_component(
entity,
comp::phys::Pos(current_pos.0 + Vec3::new(x, y, z)),
);
server
.state
.write_component(entity, comp::phys::ForceUpdate);
.write_component(entity, comp::Pos(current_pos.0 + Vec3::new(x, y, z)));
server.state.write_component(entity, comp::ForceUpdate);
}
None => server.clients.notify(
entity,
@ -131,10 +125,8 @@ fn handle_goto(server: &mut Server, entity: EcsEntity, args: String, action: &Ch
(Some(x), Some(y), Some(z)) => {
server
.state
.write_component(entity, comp::phys::Pos(Vec3::new(x, y, z)));
server
.state
.write_component(entity, comp::phys::ForceUpdate);
.write_component(entity, comp::Pos(Vec3::new(x, y, z)));
server.state.write_component(entity, comp::ForceUpdate);
}
_ => server
.clients
@ -173,20 +165,15 @@ fn handle_tp(server: &mut Server, entity: EcsEntity, args: String, action: &Chat
match opt_alias {
Some(alias) => {
let ecs = server.state.ecs();
let opt_player = (&ecs.entities(), &ecs.read_storage::<comp::player::Player>())
let opt_player = (&ecs.entities(), &ecs.read_storage::<comp::Player>())
.join()
.find(|(_, player)| player.alias == alias)
.map(|(entity, _)| entity);
match opt_player {
Some(player) => match server
.state
.read_component_cloned::<comp::phys::Pos>(player)
{
Some(player) => match server.state.read_component_cloned::<comp::Pos>(player) {
Some(pos) => {
server.state.write_component(entity, pos);
server
.state
.write_component(entity, comp::phys::ForceUpdate);
server.state.write_component(entity, comp::ForceUpdate);
}
None => server.clients.notify(
entity,
@ -222,10 +209,7 @@ fn handle_spawn(server: &mut Server, entity: EcsEntity, args: String, action: &C
match (opt_agent, opt_id, opt_amount) {
(Some(agent), Some(id), Some(amount)) => {
match server
.state
.read_component_cloned::<comp::phys::Pos>(entity)
{
match server.state.read_component_cloned::<comp::Pos>(entity) {
Some(mut pos) => {
pos.0.x += 1.0; // Temp fix TODO: Solve NaN issue with positions of pets
let body = kind_to_body(id);

View File

@ -135,7 +135,7 @@ impl Server {
#[allow(dead_code)]
pub fn create_npc(
&mut self,
pos: comp::phys::Pos,
pos: comp::Pos,
name: String,
body: comp::Body,
) -> EcsEntityBuilder {
@ -143,13 +143,13 @@ impl Server {
.ecs_mut()
.create_entity_synced()
.with(pos)
.with(comp::phys::Vel(Vec3::zero()))
.with(comp::phys::Ori(Vec3::unit_y()))
.with(comp::Control::default())
.with(comp::Vel(Vec3::zero()))
.with(comp::Ori(Vec3::unit_y()))
.with(comp::Controller::default())
.with(comp::AnimationInfo::default())
.with(comp::Actor::Character { name, body })
.with(comp::Stats::default())
.with(comp::phys::ForceUpdate)
.with(comp::ForceUpdate)
}
pub fn create_player_character(
@ -164,11 +164,12 @@ impl Server {
state.write_component(entity, comp::Actor::Character { name, body });
state.write_component(entity, comp::Stats::default());
state.write_component(entity, comp::AnimationInfo::default());
state.write_component(entity, comp::phys::Pos(spawn_point));
state.write_component(entity, comp::phys::Vel(Vec3::zero()));
state.write_component(entity, comp::phys::Ori(Vec3::unit_y()));
state.write_component(entity, comp::Controller::default());
state.write_component(entity, comp::Pos(spawn_point));
state.write_component(entity, comp::Vel(Vec3::zero()));
state.write_component(entity, comp::Ori(Vec3::unit_y()));
// Make sure physics are accepted.
state.write_component(entity, comp::phys::ForceUpdate);
state.write_component(entity, comp::ForceUpdate);
// Tell the client its request was successful.
client.allow_state(ClientState::Character);
@ -245,9 +246,8 @@ impl Server {
// Actually kill them
for entity in todo_kill {
if let Some(client) = self.clients.get_mut(&entity) {
self.state
.write_component(entity, comp::phys::Vel(Vec3::zero()));
self.state.write_component(entity, comp::phys::ForceUpdate);
self.state.write_component(entity, comp::Vel(Vec3::zero()));
self.state.write_component(entity, comp::ForceUpdate);
client.force_state(ClientState::Dead);
} else {
if let Err(err) = self.state.ecs_mut().delete_entity_synced(entity) {
@ -272,12 +272,11 @@ impl Server {
self.state.write_component(entity, comp::Stats::default());
self.state
.ecs_mut()
.write_storage::<comp::phys::Pos>()
.write_storage::<comp::Pos>()
.get_mut(entity)
.map(|pos| pos.0.z += 100.0);
self.state
.write_component(entity, comp::phys::Vel(Vec3::zero()));
self.state.write_component(entity, comp::phys::ForceUpdate);
self.state.write_component(entity, comp::Vel(Vec3::zero()));
self.state.write_component(entity, comp::ForceUpdate);
}
}
@ -288,7 +287,7 @@ impl Server {
for (entity, view_distance, pos) in (
&self.state.ecs().entities(),
&self.state.ecs().read_storage::<comp::Player>(),
&self.state.ecs().read_storage::<comp::phys::Pos>(),
&self.state.ecs().read_storage::<comp::Pos>(),
)
.join()
.filter_map(|(entity, player, pos)| {
@ -323,7 +322,7 @@ impl Server {
// For each player with a position, calculate the distance.
for (player, pos) in (
&self.state.ecs().read_storage::<comp::Player>(),
&self.state.ecs().read_storage::<comp::phys::Pos>(),
&self.state.ecs().read_storage::<comp::Pos>(),
)
.join()
{
@ -492,24 +491,16 @@ impl Server {
}
ClientState::Pending => {}
},
ClientMsg::Attack => match client.client_state {
ClientState::Character => {
if state
.ecs()
.read_storage::<comp::Attacking>()
.get(entity)
.is_none()
{
state.write_component(entity, comp::Attacking::start());
}
ClientMsg::Controller(controller) => match client.client_state {
ClientState::Connected
| ClientState::Registered
| ClientState::Spectator => {
client.error_state(RequestStateError::Impossible)
}
_ => client.error_state(RequestStateError::Impossible),
},
ClientMsg::Respawn => match client.client_state {
ClientState::Dead => {
state.write_component(entity, comp::Respawning);
ClientState::Dead | ClientState::Character => {
state.write_component(entity, controller);
}
_ => client.error_state(RequestStateError::Impossible),
ClientState::Pending => {}
},
ClientMsg::Chat(msg) => match client.client_state {
ClientState::Connected => {
@ -521,15 +512,6 @@ impl Server {
| ClientState::Character => new_chat_msgs.push((Some(entity), msg)),
ClientState::Pending => {}
},
ClientMsg::PlayerAnimation(animation_info) => {
match client.client_state {
ClientState::Character => {
state.write_component(entity, animation_info)
}
// Only characters can send animations.
_ => client.error_state(RequestStateError::Impossible),
}
}
ClientMsg::PlayerPhysics { pos, vel, ori } => match client.client_state {
ClientState::Character => {
state.write_component(entity, pos);
@ -639,9 +621,9 @@ impl Server {
// Sync physics
for (&uid, &pos, &vel, &ori) in (
&state.ecs().read_storage::<Uid>(),
&state.ecs().read_storage::<comp::phys::Pos>(),
&state.ecs().read_storage::<comp::phys::Vel>(),
&state.ecs().read_storage::<comp::phys::Ori>(),
&state.ecs().read_storage::<comp::Pos>(),
&state.ecs().read_storage::<comp::Vel>(),
&state.ecs().read_storage::<comp::Ori>(),
)
.join()
{
@ -680,13 +662,10 @@ impl Server {
for (entity, &uid, &pos, &vel, &ori, force_update) in (
&self.state.ecs().entities(),
&self.state.ecs().read_storage::<Uid>(),
&self.state.ecs().read_storage::<comp::phys::Pos>(),
&self.state.ecs().read_storage::<comp::phys::Vel>(),
&self.state.ecs().read_storage::<comp::phys::Ori>(),
self.state
.ecs()
.read_storage::<comp::phys::ForceUpdate>()
.maybe(),
&self.state.ecs().read_storage::<comp::Pos>(),
&self.state.ecs().read_storage::<comp::Vel>(),
&self.state.ecs().read_storage::<comp::Ori>(),
self.state.ecs().read_storage::<comp::ForceUpdate>().maybe(),
)
.join()
{
@ -702,7 +681,7 @@ impl Server {
let in_vd = |entity| {
// Get client position.
let client_pos = match state.ecs().read_storage::<comp::phys::Pos>().get(entity) {
let client_pos = match state.ecs().read_storage::<comp::Pos>().get(entity) {
Some(pos) => pos.0,
None => return false,
};
@ -733,10 +712,7 @@ impl Server {
&self.state.ecs().entities(),
&self.state.ecs().read_storage::<Uid>(),
&self.state.ecs().read_storage::<comp::AnimationInfo>(),
self.state
.ecs()
.read_storage::<comp::phys::ForceUpdate>()
.maybe(),
self.state.ecs().read_storage::<comp::ForceUpdate>().maybe(),
)
.join()
{
@ -755,7 +731,7 @@ impl Server {
// Remove all force flags.
self.state
.ecs_mut()
.write_storage::<comp::phys::ForceUpdate>()
.write_storage::<comp::ForceUpdate>()
.clear();
}

View File

@ -46,11 +46,11 @@ impl Animation for AttackAnimation {
-8.0 + wave_quicken_slow * 10.0,
4.0 + wave_quicken_double * 3.0,
9.0,
) / 11.0;
);
next.l_hand.ori = Quaternion::rotation_z(-0.8)
* Quaternion::rotation_x(0.0 + wave_quicken * -0.8)
* Quaternion::rotation_y(0.0 + wave_quicken * -0.4);
next.l_hand.scale = Vec3::one() / 11.0;
next.l_hand.scale = Vec3::one();
next.r_hand.offset = Vec3::new(0.0, -2.0, 6.5) / 11.0;
next.r_hand.ori = Quaternion::rotation_x(0.0);

View File

@ -0,0 +1,104 @@
use super::{super::Animation, CharacterSkeleton};
use std::{f32::consts::PI, ops::Mul};
use vek::*;
pub struct CidleAnimation;
impl Animation for CidleAnimation {
type Skeleton = CharacterSkeleton;
type Dependency = f64;
fn update_skeleton(
skeleton: &Self::Skeleton,
global_time: f64,
anim_time: f64,
) -> Self::Skeleton {
let mut next = (*skeleton).clone();
let wave_ultra_slow = (anim_time as f32 * 1.0 + PI).sin();
let wave_ultra_slow_cos = (anim_time as f32 * 1.0 + PI).cos();
let head_look = Vec2::new(
((global_time + anim_time) as f32 / 8.0)
.floor()
.mul(7331.0)
.sin()
* 0.5,
((global_time + anim_time) as f32 / 8.0)
.floor()
.mul(1337.0)
.sin()
* 0.25,
);
next.head.offset = Vec3::new(0.0, 2.0, 11.0 + wave_ultra_slow * 0.3);
next.head.ori = Quaternion::rotation_z(head_look.x) * Quaternion::rotation_x(head_look.y);
next.head.scale = Vec3::one();
next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + wave_ultra_slow * 0.3);
next.chest.ori = Quaternion::rotation_x(0.0);
next.chest.scale = Vec3::one();
next.belt.offset = Vec3::new(0.0, 0.0, 5.0 + wave_ultra_slow * 0.3);
next.belt.ori = Quaternion::rotation_x(0.0);
next.belt.scale = Vec3::one();
next.shorts.offset = Vec3::new(0.0, 0.0, 2.0 + wave_ultra_slow * 0.3);
next.shorts.ori = Quaternion::rotation_x(0.0);
next.shorts.scale = Vec3::one();
next.l_hand.offset = Vec3::new(
-7.5,
-2.0 + wave_ultra_slow_cos * 0.15,
8.0 + wave_ultra_slow * 0.5,
) / 11.0;
next.l_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * 0.06);
next.l_hand.scale = Vec3::one() / 11.0;
next.r_hand.offset = Vec3::new(
7.5,
-2.0 + wave_ultra_slow_cos * 0.15,
8.0 + wave_ultra_slow * 0.5,
) / 11.0;
next.r_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * 0.06);
next.r_hand.scale = Vec3::one() / 11.;
next.l_foot.offset = Vec3::new(-3.4, -0.1, 8.0);
next.l_foot.ori = Quaternion::identity();
next.l_foot.scale = Vec3::one();
next.r_foot.offset = Vec3::new(3.4, -0.1, 8.0);
next.r_foot.ori = Quaternion::identity();
next.r_foot.scale = Vec3::one();
next.weapon.offset = Vec3::new(-7.0, -5.0, 15.0);
next.weapon.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57);
next.weapon.scale = Vec3::one() * 0.0;
next.l_shoulder.offset = Vec3::new(-10.0, -3.2, 2.5);
next.l_shoulder.ori = Quaternion::rotation_x(0.0);
next.l_shoulder.scale = Vec3::one() * 1.04;
next.r_shoulder.offset = Vec3::new(0.0, -3.2, 2.5);
next.r_shoulder.ori = Quaternion::rotation_x(0.0);
next.r_shoulder.scale = Vec3::one() * 1.04;
next.draw.offset = Vec3::new(0.0, 5.0, 0.0);
next.draw.ori = Quaternion::rotation_y(0.0);
next.draw.scale = Vec3::one() * 0.0;
next.left_equip.offset = Vec3::new(0.0, 0.0, 5.0) / 11.0;
next.left_equip.ori = Quaternion::rotation_x(0.0);;
next.left_equip.scale = Vec3::one() * 0.0;
next.right_equip.offset = Vec3::new(0.0, 0.0, 5.0) / 11.0;
next.right_equip.ori = Quaternion::rotation_x(0.0);;
next.right_equip.scale = Vec3::one() * 0.0;
next.torso.offset = Vec3::new(0.0, -0.2, 0.1);
next.torso.ori = Quaternion::rotation_x(0.0);
next.torso.scale = Vec3::one() / 11.0;
next
}
}

View File

@ -0,0 +1,97 @@
use super::{super::Animation, CharacterSkeleton};
use std::ops::Mul;
use vek::*;
pub struct CrunAnimation;
impl Animation for CrunAnimation {
type Skeleton = CharacterSkeleton;
type Dependency = (f32, f64);
fn update_skeleton(
skeleton: &Self::Skeleton,
(velocity, global_time): Self::Dependency,
anim_time: f64,
) -> Self::Skeleton {
let mut next = (*skeleton).clone();
let wave = (anim_time as f32 * 14.0).sin();
let wave_cos = (anim_time as f32 * 14.0).cos();
let head_look = Vec2::new(
((global_time + anim_time) as f32 / 2.0)
.floor()
.mul(7331.0)
.sin()
* 0.2,
((global_time + anim_time) as f32 / 2.0)
.floor()
.mul(1337.0)
.sin()
* 0.1,
);
next.head.offset = Vec3::new(0.0, 3.0, 12.0 + wave_cos * 1.3);
next.head.ori = Quaternion::rotation_z(head_look.x + wave * 0.1)
* Quaternion::rotation_x(head_look.y + 0.35);
next.head.scale = Vec3::one();
next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + wave_cos * 1.1);
next.chest.ori = Quaternion::rotation_z(wave * 0.1);
next.chest.scale = Vec3::one();
next.belt.offset = Vec3::new(0.0, 0.0, 5.0 + wave_cos * 1.1);
next.belt.ori = Quaternion::rotation_z(wave * 0.25);
next.belt.scale = Vec3::one();
next.shorts.offset = Vec3::new(0.0, 0.0, 2.0 + wave_cos * 1.1);
next.shorts.ori = Quaternion::rotation_z(wave * 0.6);
next.shorts.scale = Vec3::one();
next.l_hand.offset = Vec3::new(-9.0, 3.0 + wave_cos * 8.0, 12.0 - wave * 1.0) / 11.0;
next.l_hand.ori = Quaternion::rotation_x(wave_cos * 1.1);
next.l_hand.scale = Vec3::one() / 11.0;
next.r_hand.offset = Vec3::new(9.0, 3.0 - wave_cos * 8.0, 12.0 + wave * 1.0) / 11.0;
next.r_hand.ori = Quaternion::rotation_x(wave_cos * -1.1);
next.r_hand.scale = Vec3::one() / 11.0;
next.l_foot.offset = Vec3::new(-3.4, 0.0 + wave_cos * 1.0, 6.0);
next.l_foot.ori = Quaternion::rotation_x(-0.0 - wave_cos * 1.5);
next.l_foot.scale = Vec3::one();
next.r_foot.offset = Vec3::new(3.4, 0.0 - wave_cos * 1.0, 6.0);
next.r_foot.ori = Quaternion::rotation_x(-0.0 + wave_cos * 1.5);
next.r_foot.scale = Vec3::one();
next.weapon.offset = Vec3::new(-7.0, -5.0, 15.0);
next.weapon.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57);
next.weapon.scale = Vec3::one() * 0.0;
next.l_shoulder.offset = Vec3::new(-10.0, -3.2, 2.5);
next.l_shoulder.ori = Quaternion::rotation_x(0.0);
next.l_shoulder.scale = Vec3::one() * 1.04;
next.r_shoulder.offset = Vec3::new(0.0, -3.2, 2.5);
next.r_shoulder.ori = Quaternion::rotation_x(0.0);
next.r_shoulder.scale = Vec3::one() * 1.04;
next.draw.offset = Vec3::new(0.0, 5.0, 0.0);
next.draw.ori = Quaternion::rotation_y(0.0);
next.draw.scale = Vec3::one() * 0.0;
next.left_equip.offset = Vec3::new(0.0, 0.0, 5.0) / 11.0;
next.left_equip.ori = Quaternion::rotation_x(0.0);;
next.left_equip.scale = Vec3::one() * 0.0;
next.right_equip.offset = Vec3::new(0.0, 0.0, 5.0) / 11.0;
next.right_equip.ori = Quaternion::rotation_x(0.0);;
next.right_equip.scale = Vec3::one() * 0.0;
next.torso.offset = Vec3::new(0.0, -0.2, 0.4);
next.torso.ori = Quaternion::rotation_x(-velocity * 0.04 - wave_cos * 0.10);
next.torso.scale = Vec3::one() / 11.0;
next
}
}

View File

@ -6,11 +6,11 @@ pub struct GlidingAnimation;
impl Animation for GlidingAnimation {
type Skeleton = CharacterSkeleton;
type Dependency = f64;
type Dependency = (f32, f64);
fn update_skeleton(
skeleton: &Self::Skeleton,
global_time: f64,
(velocity, global_time): Self::Dependency,
anim_time: f64,
) -> Self::Skeleton {
let mut next = (*skeleton).clone();
@ -35,48 +35,40 @@ impl Animation for GlidingAnimation {
.sin()
* 0.25,
);
next.head.offset = Vec3::new(0.0, 2.0, 12.0);
next.head.offset = Vec3::new(0.0, 2.0, 2.0);
next.head.ori = Quaternion::rotation_x(0.35 - wave_very_slow * 0.10 + head_look.y)
* Quaternion::rotation_z(head_look.x + wave_very_slow_cos * 0.15);
next.head.scale = Vec3::one();
next.chest.offset = Vec3::new(0.0, 0.0, 8.0);
next.chest.offset = Vec3::new(0.0, 0.0, -2.0);
next.chest.ori = Quaternion::rotation_z(wave_very_slow_cos * 0.15);
next.chest.scale = Vec3::one();
next.belt.offset = Vec3::new(0.0, 0.0, 6.0);
next.belt.offset = Vec3::new(0.0, 0.0, -4.0);
next.belt.ori = Quaternion::rotation_z(wave_very_slow_cos * 0.20);
next.belt.scale = Vec3::one();
next.shorts.offset = Vec3::new(0.0, 0.0, 3.0);
next.shorts.offset = Vec3::new(0.0, 0.0, -7.0);
next.shorts.ori = Quaternion::rotation_z(wave_very_slow_cos * 0.25);
next.shorts.scale = Vec3::one();
next.l_hand.offset = Vec3::new(
-10.0,
6.0 - wave_very_slow * 1.50,
15.0 + wave_very_slow * 0.50,
) / 11.0;
next.l_hand.ori = Quaternion::rotation_x(0.2 + wave_very_slow_cos * 0.05);
next.l_hand.scale = Vec3::one() / 11.0;
next.l_hand.offset = Vec3::new(-10.0, -2.0 + wave_very_slow * 0.10, 10.5);
next.l_hand.ori = Quaternion::rotation_x(1.0 + wave_very_slow_cos * -0.10);
next.l_hand.scale = Vec3::one();
next.r_hand.offset = Vec3::new(
10.0,
6.0 - wave_very_slow * 1.50,
14.5 + wave_very_slow * 0.50,
) / 11.0;
next.r_hand.ori = Quaternion::rotation_x(0.1 + wave_very_slow * 0.05);
next.r_hand.scale = Vec3::one() / 11.0;
next.r_hand.offset = Vec3::new(10.0, -2.0 + wave_very_slow * 0.10, 10.5);
next.r_hand.ori = Quaternion::rotation_x(1.0 + wave_very_slow_cos * -0.10);
next.r_hand.scale = Vec3::one();
next.l_foot.offset = Vec3::new(-3.4, 1.0, 8.0);
next.l_foot.offset = Vec3::new(-3.4, 1.0, -2.0);
next.l_foot.ori = Quaternion::rotation_x(
wave_stop * -0.7 - wave_slow_cos * -0.21 + wave_very_slow * 0.19,
(wave_stop * -0.7 - wave_slow_cos * -0.21 + wave_very_slow * 0.19) * velocity * 0.07,
);
next.l_foot.scale = Vec3::one();
next.r_foot.offset = Vec3::new(3.4, 1.0, 8.0);
next.r_foot.offset = Vec3::new(3.4, 1.0, -2.0);
next.r_foot.ori = Quaternion::rotation_x(
wave_stop * -0.8 + wave_slow * -0.25 + wave_very_slow_alt * 0.13,
(wave_stop * -0.8 + wave_slow * -0.25 + wave_very_slow_alt * 0.13) * velocity * 0.07,
);
next.r_foot.scale = Vec3::one();
@ -92,21 +84,21 @@ impl Animation for GlidingAnimation {
next.r_shoulder.ori = Quaternion::rotation_x(0.0);
next.r_shoulder.scale = Vec3::one() * 1.04;
next.draw.offset = Vec3::new(0.0, -9.0 + wave_very_slow * 0.10, 18.0);
next.draw.ori = Quaternion::rotation_x(0.95 - wave_very_slow * 0.15)
next.draw.offset = Vec3::new(0.0, -9.0 + wave_very_slow * 0.10, 8.0);
next.draw.ori = Quaternion::rotation_x(1.0)//0.95 - wave_very_slow * 0.08)
* Quaternion::rotation_y(wave_very_slow_cos * 0.04);
next.draw.scale = Vec3::one();
next.left_equip.offset = Vec3::new(0.0, 0.0, 5.0) / 11.0;
next.left_equip.offset = Vec3::new(0.0, 0.0, -5.0) / 11.0;
next.left_equip.ori = Quaternion::rotation_x(0.0);;
next.left_equip.scale = Vec3::one() * 0.0;
next.right_equip.offset = Vec3::new(0.0, 0.0, 5.0) / 11.0;
next.right_equip.offset = Vec3::new(0.0, 0.0, -5.0) / 11.0;
next.right_equip.ori = Quaternion::rotation_x(0.0);;
next.right_equip.scale = Vec3::one() * 0.0;
next.torso.offset = Vec3::new(0.0, -0.2, 0.0);
next.torso.ori = Quaternion::rotation_x(-0.8 + wave_very_slow * 0.10);
next.torso.offset = Vec3::new(0.0, -0.2, 10.0) / 11.0;
next.torso.ori = Quaternion::rotation_x(-0.05 * velocity + wave_very_slow * 0.10);
next.torso.scale = Vec3::one() / 11.0;
next

View File

@ -51,20 +51,20 @@ impl Animation for IdleAnimation {
next.l_hand.offset = Vec3::new(
-7.5,
-2.0 + wave_ultra_slow_cos * 0.15,
8.0 + wave_ultra_slow * 0.5,
) / 11.0;
0.0 + wave_ultra_slow_cos * 0.15,
7.0 + wave_ultra_slow * 0.5,
);
next.l_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * 0.06);
next.l_hand.scale = Vec3::one() / 11.0;
next.l_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * -0.06);
next.l_hand.scale = Vec3::one();
next.r_hand.offset = Vec3::new(
7.5,
-2.0 + wave_ultra_slow_cos * 0.15,
8.0 + wave_ultra_slow * 0.5,
) / 11.0;
next.r_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * 0.06);
next.r_hand.scale = Vec3::one() / 11.;
0.0 + wave_ultra_slow_cos * 0.15,
7.0 + wave_ultra_slow * 0.5,
);
next.r_hand.ori = Quaternion::rotation_x(0.0 + wave_ultra_slow * -0.06);
next.r_hand.scale = Vec3::one();
next.l_foot.offset = Vec3::new(-3.4, -0.1, 8.0);
next.l_foot.ori = Quaternion::identity();

View File

@ -39,17 +39,17 @@ impl Animation for JumpAnimation {
-8.0,
0.0 + wave_stop * 3.8,
7.0 + wave_stop * 3.2 - wave * 0.4,
) / 11.0;
);
next.l_hand.ori = Quaternion::rotation_x(wave_stop_alt * 0.6);
next.l_hand.scale = Vec3::one() / 11.0;
next.l_hand.scale = Vec3::one();
next.r_hand.offset = Vec3::new(
8.0,
0.0 + wave_stop * -3.8,
7.0 + wave_stop * 3.2 - wave * 0.4,
) / 11.0;
);
next.r_hand.ori = Quaternion::rotation_x(-wave_stop_alt * 0.6);
next.r_hand.scale = Vec3::one() / 11.0;
next.r_hand.scale = Vec3::one();
next.l_foot.offset = Vec3::new(-3.4, 1.0, 6.0);
next.l_foot.ori = Quaternion::rotation_x(wave_stop * -1.2 - wave_slow * 0.2);

View File

@ -1,14 +1,20 @@
pub mod attack;
pub mod cidle;
pub mod crun;
pub mod gliding;
pub mod idle;
pub mod jump;
pub mod roll;
pub mod run;
// Reexports
pub use self::attack::AttackAnimation;
pub use self::cidle::CidleAnimation;
pub use self::crun::CrunAnimation;
pub use self::gliding::GlidingAnimation;
pub use self::idle::IdleAnimation;
pub use self::jump::JumpAnimation;
pub use self::roll::RollAnimation;
pub use self::run::RunAnimation;
use super::{Bone, Skeleton};
@ -68,8 +74,8 @@ impl Skeleton for CharacterSkeleton {
FigureBoneData::new(torso_mat * chest_mat),
FigureBoneData::new(torso_mat * self.belt.compute_base_matrix()),
FigureBoneData::new(torso_mat * self.shorts.compute_base_matrix()),
FigureBoneData::new(l_hand_mat),
FigureBoneData::new(self.r_hand.compute_base_matrix()),
FigureBoneData::new(torso_mat * l_hand_mat),
FigureBoneData::new(torso_mat * self.r_hand.compute_base_matrix()),
FigureBoneData::new(torso_mat * self.l_foot.compute_base_matrix()),
FigureBoneData::new(torso_mat * self.r_foot.compute_base_matrix()),
FigureBoneData::new(torso_mat * chest_mat * weapon_mat),

View File

@ -0,0 +1,98 @@
use super::{super::Animation, CharacterSkeleton};
use std::{f32::consts::PI, ops::Mul};
use vek::*;
pub struct RollAnimation;
impl Animation for RollAnimation {
type Skeleton = CharacterSkeleton;
type Dependency = f64;
fn update_skeleton(
skeleton: &Self::Skeleton,
global_time: f64,
anim_time: f64,
) -> Self::Skeleton {
let mut next = (*skeleton).clone();
let wave = (anim_time as f32 * 5.5).sin();
let wave_quick = (anim_time as f32 * 9.5).sin();
let wave_quick_cos = (anim_time as f32 * 9.5).cos();
let wave_cos = (anim_time as f32 * 5.5).cos();
let wave_slow = (anim_time as f32 * 2.8 + PI).sin();
let wave_dub = (anim_time as f32 * 5.5).sin();
next.head.offset = Vec3::new(0.0, 0.0 + wave_slow * -3.0, 9.0 + wave_dub * -5.0);
next.head.ori = Quaternion::rotation_x(wave_dub * -0.4);
next.head.scale = Vec3::one();
next.chest.offset = Vec3::new(0.0, 0.0, 7.0 + wave_dub * -2.5);
next.chest.ori = Quaternion::rotation_x(wave_dub * -0.5);
next.chest.scale = Vec3::one() * 1.01;
next.belt.offset = Vec3::new(0.0, 0.0, 5.0);
next.belt.ori = Quaternion::rotation_x(0.0);
next.belt.scale = Vec3::one();
next.shorts.offset = Vec3::new(0.0, 0.0, 2.0);
next.shorts.ori = Quaternion::rotation_x(0.0);
next.shorts.scale = Vec3::one();
next.l_hand.offset = Vec3::new(
-5.5 + wave * -0.5,
-2.0 + wave_quick_cos * -5.5,
8.0 + wave_quick * 0.5,
);
next.l_hand.ori =
Quaternion::rotation_x(wave_slow * 6.5) * Quaternion::rotation_y(wave * 0.3);
next.l_hand.scale = Vec3::one();
next.r_hand.offset = Vec3::new(
5.5 + wave * 0.5,
-2.0 + wave_quick_cos * 2.5,
8.0 + wave_quick * 3.0,
);
next.r_hand.ori =
Quaternion::rotation_x(wave_slow * 6.5) * Quaternion::rotation_y(wave * 0.3);
next.r_hand.scale = Vec3::one();
next.l_foot.offset = Vec3::new(-3.4, -0.1, 9.0 - 0.0 + wave_dub * -1.2 + wave_slow * 4.0);
next.l_foot.ori = Quaternion::rotation_x(wave * 0.6);
next.l_foot.scale = Vec3::one();
next.r_foot.offset = Vec3::new(3.4, -0.1, 9.0 - 0.0 + wave_dub * -1.0 + wave_slow * 4.0);
next.r_foot.ori = Quaternion::rotation_x(wave * -0.4);
next.r_foot.scale = Vec3::one();
next.weapon.offset = Vec3::new(-7.0, -7.0, 15.0);
next.weapon.ori =
Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57 + wave_quick * 1.0);
next.weapon.scale = Vec3::one();
next.l_shoulder.offset = Vec3::new(-10.0, -3.2, 2.5);
next.l_shoulder.ori = Quaternion::rotation_x(0.0);
next.l_shoulder.scale = Vec3::one() * 1.04;
next.r_shoulder.offset = Vec3::new(0.0, -3.2, 2.5);
next.r_shoulder.ori = Quaternion::rotation_x(0.0);
next.r_shoulder.scale = Vec3::one() * 1.04;
next.draw.offset = Vec3::new(0.0, 5.0, 0.0);
next.draw.ori = Quaternion::rotation_y(0.0);
next.draw.scale = Vec3::one() * 0.0;
next.left_equip.offset = Vec3::new(0.0, 0.0, 5.0) / 11.0;
next.left_equip.ori = Quaternion::rotation_x(0.0);;
next.left_equip.scale = Vec3::one() * 0.0;
next.right_equip.offset = Vec3::new(0.0, 0.0, 5.0) / 11.0;
next.right_equip.ori = Quaternion::rotation_x(0.0);;
next.right_equip.scale = Vec3::one() * 0.0;
next.torso.offset = Vec3::new(0.0, -2.2, 0.1 + wave_dub * 16.0) / 11.0;
next.torso.ori = Quaternion::rotation_x(wave_slow * 6.0);
next.torso.scale = Vec3::one() / 11.0;
next
}
}

View File

@ -1,4 +1,5 @@
use super::{super::Animation, CharacterSkeleton};
use std::f32::consts::PI;
use std::ops::Mul;
use vek::*;
@ -15,8 +16,11 @@ impl Animation for RunAnimation {
) -> Self::Skeleton {
let mut next = (*skeleton).clone();
let wave = (anim_time as f32 * 14.0).sin();
let wave_cos = (anim_time as f32 * 14.0).cos();
let wave = (anim_time as f32 * 12.0).sin();
let wave_cos = (anim_time as f32 * 12.0).cos();
let wave_diff = (anim_time as f32 * 12.0 + PI / 2.0).sin();
let wave_cos_dub = (anim_time as f32 * 24.0).cos();
let wave_stop = (anim_time as f32 * 2.6).min(PI / 2.0).sin();
let head_look = Vec2::new(
((global_time + anim_time) as f32 / 2.0)
@ -48,24 +52,33 @@ impl Animation for RunAnimation {
next.shorts.ori = Quaternion::rotation_z(wave * 0.6);
next.shorts.scale = Vec3::one();
next.l_hand.offset = Vec3::new(-9.0, 3.0 + wave_cos * 8.0, 12.0 - wave * 1.0) / 11.0;
next.l_hand.ori = Quaternion::rotation_x(wave_cos * 1.1);
next.l_hand.scale = Vec3::one() / 11.0;
next.l_hand.offset = Vec3::new(
-7.5 + wave_cos_dub * 1.0,
2.0 + wave_cos * 5.0,
7.0 - wave * 1.5,
);
next.l_hand.ori = Quaternion::rotation_x(wave_cos * 0.8);
next.l_hand.scale = Vec3::one() * 1.0;
next.r_hand.offset = Vec3::new(9.0, 3.0 - wave_cos * 8.0, 12.0 + wave * 1.0) / 11.0;
next.r_hand.ori = Quaternion::rotation_x(wave_cos * -1.1);
next.r_hand.scale = Vec3::one() / 11.0;
next.r_hand.offset = Vec3::new(
7.5 - wave_cos_dub * 1.0,
2.0 - wave_cos * 5.0,
7.0 + wave * 1.5,
);
next.r_hand.ori = Quaternion::rotation_x(wave_cos * -0.8);
next.r_hand.scale = Vec3::one() * 1.0;
next.l_foot.offset = Vec3::new(-3.4, 0.0 + wave_cos * 1.0, 6.0);
next.l_foot.ori = Quaternion::rotation_x(-0.0 - wave_cos * 1.5);
next.l_foot.offset = Vec3::new(-3.4, 0.0 + wave_cos * 1.0, 7.0);
next.l_foot.ori = Quaternion::rotation_x(-0.0 - wave_cos * 1.3);
next.l_foot.scale = Vec3::one();
next.r_foot.offset = Vec3::new(3.4, 0.0 - wave_cos * 1.0, 6.0);
next.r_foot.ori = Quaternion::rotation_x(-0.0 + wave_cos * 1.5);
next.r_foot.offset = Vec3::new(3.4, 0.0 - wave_cos * 1.0, 7.0);
next.r_foot.ori = Quaternion::rotation_x(-0.0 + wave_cos * 1.3);
next.r_foot.scale = Vec3::one();
next.weapon.offset = Vec3::new(-7.0, -5.0, 15.0);
next.weapon.ori = Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57);
next.weapon.ori =
Quaternion::rotation_y(2.5) * Quaternion::rotation_z(1.57 + wave_cos * 0.25);
next.weapon.scale = Vec3::one();
next.l_shoulder.offset = Vec3::new(-10.0, -3.2, 2.5);
@ -89,7 +102,8 @@ impl Animation for RunAnimation {
next.right_equip.scale = Vec3::one() * 0.0;
next.torso.offset = Vec3::new(0.0, -0.2, 0.4);
next.torso.ori = Quaternion::rotation_x(-velocity * 0.04 - wave_cos * 0.10);
next.torso.ori =
Quaternion::rotation_x(wave_stop * velocity * -0.06 + wave_diff * velocity * -0.005);
next.torso.scale = Vec3::one() / 11.0;
next

View File

@ -103,7 +103,7 @@ font_ids! {
pub struct DebugInfo {
pub tps: f64,
pub ping_ms: f64,
pub coordinates: Option<comp::phys::Pos>,
pub coordinates: Option<comp::Pos>,
}
pub enum Event {
@ -312,7 +312,7 @@ impl Hud {
if self.show.ingame {
let ecs = client.state().ecs();
let actor = ecs.read_storage::<comp::Actor>();
let pos = ecs.read_storage::<comp::phys::Pos>();
let pos = ecs.read_storage::<comp::Pos>();
let stats = ecs.read_storage::<comp::Stats>();
let player = ecs.read_storage::<comp::Player>();
let entities = ecs.entities();
@ -322,7 +322,7 @@ impl Hud {
let player_pos = client
.state()
.ecs()
.read_storage::<comp::phys::Pos>()
.read_storage::<comp::Pos>()
.get(client.entity())
.map_or(Vec3::zero(), |pos| pos.0);
let mut name_id_walker = self.ids.name_tags.walk();

View File

@ -108,7 +108,7 @@ impl PlayState for CharSelectionState {
if let Err(err) = self
.client
.borrow_mut()
.tick(comp::Control::default(), clock.get_last_delta())
.tick(comp::Controller::default(), clock.get_last_delta())
{
error!("Failed to tick the scene: {:?}", err);
return PlayStateResult::Pop;

View File

@ -477,15 +477,15 @@ impl FigureMgr {
let player_pos = client
.state()
.ecs()
.read_storage::<comp::phys::Pos>()
.read_storage::<comp::Pos>()
.get(client.entity())
.map_or(Vec3::zero(), |pos| pos.0);
for (entity, pos, vel, ori, actor, animation_info, stats) in (
&ecs.entities(),
&ecs.read_storage::<comp::phys::Pos>(),
&ecs.read_storage::<comp::phys::Vel>(),
&ecs.read_storage::<comp::phys::Ori>(),
&ecs.read_storage::<comp::Pos>(),
&ecs.read_storage::<comp::Vel>(),
&ecs.read_storage::<comp::Ori>(),
&ecs.read_storage::<comp::Actor>(),
&ecs.read_storage::<comp::AnimationInfo>(),
ecs.read_storage::<comp::Stats>().maybe(),
@ -556,10 +556,25 @@ impl FigureMgr {
time,
animation_info.time,
),
comp::Animation::Roll => character::RollAnimation::update_skeleton(
state.skeleton_mut(),
time,
animation_info.time,
),
comp::Animation::Crun => character::CrunAnimation::update_skeleton(
state.skeleton_mut(),
(vel.0.magnitude(), time),
animation_info.time,
),
comp::Animation::Cidle => character::CidleAnimation::update_skeleton(
state.skeleton_mut(),
time,
animation_info.time,
),
comp::Animation::Gliding => {
character::GlidingAnimation::update_skeleton(
state.skeleton_mut(),
time,
(vel.0.magnitude(), time),
animation_info.time,
)
}
@ -661,15 +676,15 @@ impl FigureMgr {
let player_pos = client
.state()
.ecs()
.read_storage::<comp::phys::Pos>()
.read_storage::<comp::Pos>()
.get(client.entity())
.map_or(Vec3::zero(), |pos| pos.0);
for (entity, _, _, _, actor, _, _) in (
&ecs.entities(),
&ecs.read_storage::<comp::phys::Pos>(),
&ecs.read_storage::<comp::phys::Vel>(),
&ecs.read_storage::<comp::phys::Ori>(),
&ecs.read_storage::<comp::Pos>(),
&ecs.read_storage::<comp::Vel>(),
&ecs.read_storage::<comp::Ori>(),
&ecs.read_storage::<comp::Actor>(),
&ecs.read_storage::<comp::AnimationInfo>(),
ecs.read_storage::<comp::Stats>().maybe(),

View File

@ -110,7 +110,7 @@ impl Scene {
let player_pos = client
.state()
.ecs()
.read_storage::<comp::phys::Pos>()
.read_storage::<comp::Pos>()
.get(client.entity())
.map_or(Vec3::zero(), |pos| pos.0);

View File

@ -8,7 +8,7 @@ use crate::{
Direction, Error, GlobalState, PlayState, PlayStateResult,
};
use client::{self, Client};
use common::{clock::Clock, comp, comp::phys::Pos, msg::ClientState};
use common::{clock::Clock, comp, comp::Pos, msg::ClientState};
use log::{error, warn};
use std::{cell::RefCell, rc::Rc, time::Duration};
use vek::*;
@ -16,8 +16,9 @@ use vek::*;
pub struct SessionState {
scene: Scene,
client: Rc<RefCell<Client>>,
key_state: KeyState,
hud: Hud,
key_state: KeyState,
controller: comp::Controller,
}
/// Represents an active game session (i.e., the one being played).
@ -30,6 +31,7 @@ impl SessionState {
scene,
client,
key_state: KeyState::new(),
controller: comp::Controller::default(),
hud: Hud::new(window),
}
}
@ -45,22 +47,8 @@ const BG_COLOR: Rgba<f32> = Rgba {
impl SessionState {
/// Tick the session (and the client attached to it).
pub fn tick(&mut self, dt: Duration) -> Result<(), Error> {
// Calculate the movement input vector of the player from the current key presses
// and the camera direction.
let ori = self.scene.camera().get_orientation();
let unit_vecs = (
Vec2::new(ori[0].cos(), -ori[0].sin()),
Vec2::new(ori[0].sin(), ori[0].cos()),
);
let dir_vec = self.key_state.dir_vec();
let move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1];
for event in self
.client
.borrow_mut()
.tick(comp::Control { move_dir }, dt)?
{
fn tick(&mut self, dt: Duration) -> Result<(), Error> {
for event in self.client.borrow_mut().tick(self.controller.clone(), dt)? {
match event {
client::Event::Chat(msg) => {
self.hud.new_message(msg);
@ -127,19 +115,22 @@ impl PlayState for SessionState {
Event::Close => {
return PlayStateResult::Shutdown;
}
Event::InputUpdate(GameInput::Attack, true) => {
self.client.borrow_mut().attack();
self.client.borrow_mut().respawn();
Event::InputUpdate(GameInput::Attack, state) => {
self.controller.attack = state;
self.controller.respawn = state; // TODO: Don't do both
}
Event::InputUpdate(GameInput::Jump, true) => {
self.client.borrow_mut().jump();
Event::InputUpdate(GameInput::Roll, state) => {
self.controller.roll = state;
}
Event::InputUpdate(GameInput::Jump, state) => {
self.controller.jump = state;
}
Event::InputUpdate(GameInput::MoveForward, state) => self.key_state.up = state,
Event::InputUpdate(GameInput::MoveBack, state) => self.key_state.down = state,
Event::InputUpdate(GameInput::MoveLeft, state) => self.key_state.left = state,
Event::InputUpdate(GameInput::MoveRight, state) => self.key_state.right = state,
Event::InputUpdate(GameInput::Glide, state) => {
self.client.borrow_mut().glide(state)
self.controller.glide = state;
}
// Pass all other events to the scene
@ -149,6 +140,16 @@ impl PlayState for SessionState {
}
}
// Calculate the movement input vector of the player from the current key presses
// and the camera direction.
let ori = self.scene.camera().get_orientation();
let unit_vecs = (
Vec2::new(ori[0].cos(), -ori[0].sin()),
Vec2::new(ori[0].sin(), ori[0].cos()),
);
let dir_vec = self.key_state.dir_vec();
self.controller.move_dir = unit_vecs.0 * dir_vec[0] + unit_vecs.1 * dir_vec[1];
// Perform an in-game tick.
if let Err(err) = self.tick(clock.get_last_delta()) {
error!("Failed to tick the scene: {:?}", err);

View File

@ -31,6 +31,7 @@ pub struct ControlSettings {
pub screenshot: KeyMouse,
pub toggle_ingame_ui: KeyMouse,
pub attack: KeyMouse,
pub roll: KeyMouse,
}
impl Default for ControlSettings {
@ -59,6 +60,7 @@ impl Default for ControlSettings {
screenshot: KeyMouse::Key(VirtualKeyCode::F4),
toggle_ingame_ui: KeyMouse::Key(VirtualKeyCode::F6),
attack: KeyMouse::Mouse(MouseButton::Left),
roll: KeyMouse::Mouse(MouseButton::Middle),
}
}
}

View File

@ -34,6 +34,7 @@ pub enum GameInput {
Screenshot,
ToggleIngameUi,
Attack,
Roll,
Respawn,
}
@ -134,6 +135,7 @@ impl Window {
GameInput::ToggleIngameUi,
);
key_map.insert(settings.controls.attack, GameInput::Attack);
key_map.insert(settings.controls.roll, GameInput::Roll);
Ok(Self {
events_loop,