starting stun anim

stagger anim, mirroring, bettern walk anim

wielding with stuns/stagger

Knockback fix

Added Poise documentation/comments
This commit is contained in:
jshipsey 2020-12-18 21:05:39 -05:00 committed by jiminycrick
parent 0f244bf84b
commit 29732bb763
15 changed files with 560 additions and 369 deletions

286
4
View File

@ -1,286 +0,0 @@
use crate::{
comp::{Attacking, CharacterState, EnergyChange, EnergySource, StateUpdate},
states::{
behavior::{CharacterBehavior, JoinData},
utils::*,
},
Damage, DamageSource, GroupTarget, Knockback,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct Stage<T> {
/// Specifies which stage the combo attack is in
pub stage: u32,
/// Initial damage of stage
pub base_damage: u32,
/// Max damage of stage
pub max_damage: u32,
/// Damage scaling per combo
pub damage_increase: u32,
/// Initial poise damage of stage
pub base_poise_damage: u32,
/// Knockback of stage
pub knockback: f32,
/// Range of attack
pub range: f32,
/// Angle of attack
pub angle: f32,
/// Initial buildup duration of stage (how long until state can deal damage)
pub base_buildup_duration: T,
/// Duration of stage spent in swing (controls animation stuff, and can also
/// be used to handle movement separately to buildup)
pub base_swing_duration: T,
/// Initial recover duration of stage (how long until character exits state)
pub base_recover_duration: T,
/// How much forward movement there is in the swing portion of the stage
pub forward_movement: f32,
}
impl Stage<u64> {
pub fn to_duration(self) -> Stage<Duration> {
Stage::<Duration> {
stage: self.stage,
base_damage: self.base_damage,
max_damage: self.max_damage,
damage_increase: self.damage_increase,
base_poise_damage: self.base_poise_damage,
knockback: self.knockback,
range: self.range,
angle: self.angle,
base_buildup_duration: Duration::from_millis(self.base_buildup_duration),
base_swing_duration: Duration::from_millis(self.base_swing_duration),
base_recover_duration: Duration::from_millis(self.base_recover_duration),
forward_movement: self.forward_movement,
}
}
pub fn adjusted_by_stats(mut self, power: f32, speed: f32) -> Self {
self.base_damage = (self.base_damage as f32 * power) as u32;
self.max_damage = (self.max_damage as f32 * power) as u32;
self.damage_increase = (self.damage_increase as f32 * power) as u32;
self.base_buildup_duration = (self.base_buildup_duration as f32 / speed) as u64;
self.base_swing_duration = (self.base_swing_duration as f32 / speed) as u64;
self.base_recover_duration = (self.base_recover_duration as f32 / speed) as u64;
self
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
/// Separated out to condense update portions of character state
pub struct StaticData {
/// Indicates number of stages in combo
pub num_stages: u32,
/// Data for each stage
pub stage_data: Vec<Stage<Duration>>,
/// Initial energy gain per strike
pub initial_energy_gain: u32,
/// Max energy gain per strike
pub max_energy_gain: u32,
/// Energy gain increase per combo
pub energy_increase: u32,
/// (100% - speed_increase) is percentage speed increases from current to
/// max when combo increases
pub speed_increase: f32,
/// (100% + max_speed_increase) is the max attack speed
pub max_speed_increase: f32,
/// Whether the state can be interrupted by other abilities
pub is_interruptible: bool,
/// What key is used to press ability
pub ability_key: AbilityKey,
}
/// A sequence of attacks that can incrementally become faster and more
/// damaging.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Data {
/// Struct containing data that does not change over the course of the
/// character state
pub static_data: StaticData,
/// Indicates what stage the combo is in
pub stage: u32,
/// Number of consecutive strikes
pub combo: u32,
/// Timer for each stage
pub timer: Duration,
/// Checks what section a stage is in
pub stage_section: StageSection,
/// Whether the state should go onto the next stage
pub next_stage: bool,
}
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData) -> StateUpdate {
let mut update = StateUpdate::from(data);
handle_orientation(data, &mut update, 1.0);
handle_move(data, &mut update, 0.3);
if !ability_key_is_pressed(data, self.static_data.ability_key) {
handle_interrupt(data, &mut update, self.static_data.is_interruptible);
if let CharacterState::Roll(roll) = &mut update.character {
roll.was_combo = Some((self.stage, self.combo));
}
match update.character {
CharacterState::ComboMelee(_) => {},
_ => {
return update;
},
}
}
let stage_index = (self.stage - 1) as usize;
let speed_modifer = 1.0
+ self.static_data.max_speed_increase
* (1.0 - self.static_data.speed_increase.powi(self.combo as i32));
match self.stage_section {
StageSection::Buildup => {
if self.timer < self.static_data.stage_data[stage_index].base_buildup_duration {
// Build up
update.character = CharacterState::ComboMelee(Data {
static_data: self.static_data.clone(),
timer: self
.timer
.checked_add(Duration::from_secs_f32(data.dt.0 * speed_modifer))
.unwrap_or_default(),
..*self
});
} else {
// Transitions to swing section of stage
update.character = CharacterState::ComboMelee(Data {
static_data: self.static_data.clone(),
timer: Duration::default(),
stage_section: StageSection::Swing,
..*self
});
// Hit attempt
let damage = self.static_data.stage_data[stage_index].max_damage.min(
self.static_data.stage_data[stage_index].base_damage
+ self.combo / self.static_data.num_stages
* self.static_data.stage_data[stage_index].damage_increase,
);
let poise_damage = self.static_data.stage_data[stage_index].base_poise_damage;
data.updater.insert(data.entity, Attacking {
damages: vec![(Some(GroupTarget::OutOfGroup), Damage {
source: DamageSource::Melee,
value: damage as f32,
poise_damage: poise_damage as f32,
})],
range: self.static_data.stage_data[stage_index].range,
max_angle: self.static_data.stage_data[stage_index].angle.to_radians(),
applied: false,
hit_count: 0,
knockback: Knockback::Away(
self.static_data.stage_data[stage_index].knockback,
),
});
}
},
StageSection::Swing => {
if self.timer < self.static_data.stage_data[stage_index].base_swing_duration {
// Forward movement
handle_forced_movement(
data,
&mut update,
ForcedMovement::Forward {
strength: self.static_data.stage_data[stage_index].forward_movement,
},
0.3,
);
// Swings
update.character = CharacterState::ComboMelee(Data {
static_data: self.static_data.clone(),
timer: self
.timer
.checked_add(Duration::from_secs_f32(data.dt.0 * speed_modifer))
.unwrap_or_default(),
..*self
});
} else {
// Transitions to recover section of stage
update.character = CharacterState::ComboMelee(Data {
static_data: self.static_data.clone(),
timer: Duration::default(),
stage_section: StageSection::Recover,
..*self
});
}
},
StageSection::Recover => {
if self.timer < self.static_data.stage_data[stage_index].base_recover_duration {
// Recovers
if ability_key_is_pressed(data, self.static_data.ability_key) {
// Checks if state will transition to next stage after recover
update.character = CharacterState::ComboMelee(Data {
static_data: self.static_data.clone(),
timer: self
.timer
.checked_add(Duration::from_secs_f32(data.dt.0 * speed_modifer))
.unwrap_or_default(),
next_stage: true,
..*self
});
} else {
update.character = CharacterState::ComboMelee(Data {
static_data: self.static_data.clone(),
timer: self
.timer
.checked_add(Duration::from_secs_f32(data.dt.0 * speed_modifer))
.unwrap_or_default(),
..*self
});
}
} else if self.next_stage {
// Transitions to buildup section of next stage
update.character = CharacterState::ComboMelee(Data {
static_data: self.static_data.clone(),
stage: (self.stage % self.static_data.num_stages) + 1,
timer: Duration::default(),
stage_section: StageSection::Buildup,
next_stage: false,
..*self
});
} else {
// Done
update.character = CharacterState::Wielding;
// Make sure attack component is removed
data.updater.remove::<Attacking>(data.entity);
}
},
_ => {
// If it somehow ends up in an incorrect stage section
update.character = CharacterState::Wielding;
// Make sure attack component is removed
data.updater.remove::<Attacking>(data.entity);
},
}
// Grant energy on successful hit
if let Some(attack) = data.attacking {
if attack.applied && attack.hit_count > 0 {
let energy = self.static_data.max_energy_gain.min(
self.static_data.initial_energy_gain
+ self.combo * self.static_data.energy_increase,
) as i32;
update.character = CharacterState::ComboMelee(Data {
static_data: self.static_data.clone(),
stage: self.stage,
combo: self.combo + 1,
timer: self.timer,
stage_section: self.stage_section,
next_stage: self.next_stage,
});
data.updater.remove::<Attacking>(data.entity);
update.energy.change_by(EnergyChange {
amount: energy,
source: EnergySource::HitEnemy,
});
}
}
update
}
}

View File

@ -1,8 +1,4 @@
use crate::{
comp::{PoiseChange, PoiseSource},
uid::Uid,
Damage, GroupTarget,
};
use crate::{comp::PoiseChange, uid::Uid, Damage, GroupTarget};
use serde::{Deserialize, Serialize};
use specs::{Component, DerefFlaggedStorage};
use specs_idvs::IdvStorage;

View File

@ -139,6 +139,8 @@ impl CharacterState {
| CharacterState::RepeaterRanged(_)
| CharacterState::Shockwave(_)
| CharacterState::BasicBeam(_)
| CharacterState::Stunned(_)
| CharacterState::Staggered(_)
| CharacterState::Wielding
)
}

View File

@ -4,20 +4,19 @@ use specs::{Component, FlaggedStorage};
use specs_idvs::IdvStorage;
use vek::*;
/// A change in the poise component. Stores the amount as a signed
/// integer to allow for added or removed poise. Also has a field to
/// label where the poise change came from.
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct PoiseChange {
/// Value of the change in poise
pub amount: i32,
/// Source of change in poise
pub source: PoiseSource,
}
impl PoiseChange {
pub fn set_zero(self) -> Self {
Self {
amount: 0,
source: self.source,
}
}
/// Alters poise damage as a result of armor poise damage reduction
pub fn modify_poise_damage(self, loadout: Option<&Loadout>) -> PoiseChange {
let mut poise_damage = self.amount as f32;
match self.source {
@ -77,6 +76,7 @@ impl PoiseChange {
}
}
/// Sources of poise change
#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
pub enum PoiseSource {
LevelUp,
@ -91,17 +91,20 @@ pub enum PoiseSource {
Other,
}
/// Poise component
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub struct Poise {
/// Base poise amount for this entity
base_max: u32,
/// Poise of entity at any given moment
current: u32,
/// Maximum poise of entity at a given time
maximum: u32,
/// Knockback direction of last change, for use as an effect in sys/stats.rs
knockback: Vec3<f32>,
/// Last poise change, storing time since last change
pub last_change: (f64, PoiseChange),
pub is_interrupted: bool,
pub is_stunned: bool,
pub is_dazed: bool,
pub is_knockeddown: bool,
/// Rate of poise regeneration per tick. Starts at zero and accelerates.
pub regen_rate: f32,
}
@ -116,35 +119,41 @@ impl Default for Poise {
amount: 0,
source: PoiseSource::Revive,
}),
is_interrupted: false,
is_stunned: false,
is_dazed: false,
is_knockeddown: false,
regen_rate: 0.0,
}
}
}
/// States to define effects of a poise change
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub enum PoiseState {
/// No effect applied
Normal,
/// Poise reset, and target briefly stunned
Interrupted,
/// Poise reset, target stunned and knocked back horizontally
Stunned,
/// Poise reset, target staggered
Dazed,
/// Poise reset, target staggered and knocked back further
KnockedDown,
}
impl Poise {
/// Creates a new poise struct based on the body it is being assigned to
pub fn new(body: Body) -> Self {
let mut poise = Poise::default();
poise.update_max_poise(Some(body));
poise.set_to(poise.maximum(), PoiseSource::Revive);
poise.update_base_max(Some(body));
poise.set_maximum(poise.base_max);
poise.set_to(poise.maximum, PoiseSource::Revive);
poise
}
/// Returns knockback as a Vec3
pub fn knockback(&self) -> Vec3<f32> { self.knockback }
/// Defines the poise states based on fraction of maximum poise
pub fn poise_state(&self) -> PoiseState {
if self.current >= 8 * self.maximum / 10 {
PoiseState::Normal
@ -159,10 +168,17 @@ impl Poise {
}
}
/// Gets the current poise value
pub fn current(&self) -> u32 { self.current }
/// Gets the maximum poise value
pub fn maximum(&self) -> u32 { self.maximum }
/// Gets the base_max value
pub fn base_max(&self) -> u32 { self.base_max }
/// Sets the poise value to a provided value. First cuts off the value
/// at the maximum. In most cases change_by() should be used.
pub fn set_to(&mut self, amount: u32, cause: PoiseSource) {
let amount = amount.min(self.maximum);
self.last_change = (0.0, PoiseChange {
@ -172,6 +188,7 @@ impl Poise {
self.current = amount;
}
/// Changes the current poise due to an in-game effect.
pub fn change_by(&mut self, change: PoiseChange, impulse: Vec3<f32>) {
self.current = ((self.current as i32 + change.amount).max(0) as u32).min(self.maximum);
self.knockback = impulse;
@ -181,32 +198,32 @@ impl Poise {
});
}
/// Resets current value to maximum
pub fn reset(&mut self) { self.current = self.maximum; }
/// Sets the maximum and updates the current value to max out at the new
/// maximum
pub fn set_maximum(&mut self, amount: u32) {
self.maximum = amount;
self.current = self.current.min(self.maximum);
}
/// Sets the `Poise` base_max
fn set_base_max(&mut self, amount: u32) {
self.base_max = amount;
self.current = self.current.min(self.maximum);
}
/// Resets the maximum to the base_max. Example use would be a potion
/// wearing off
pub fn reset_max(&mut self) { self.maximum = self.base_max; }
pub fn update_max_poise(&mut self, body: Option<Body>) {
/// Sets the base_max based on the entity `Body`
pub fn update_base_max(&mut self, body: Option<Body>) {
if let Some(body) = body {
self.set_base_max(body.base_poise());
self.set_maximum(body.base_poise());
}
}
pub fn with_max_poise(mut self, amount: u32) -> Self {
self.maximum = amount;
self.current = amount;
self
}
}
impl Component for Poise {

View File

@ -30,7 +30,7 @@ pub struct Data {
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData) -> StateUpdate {
println!("staggered");
//println!("staggered");
let mut update = StateUpdate::from(data);
match self.stage_section {
StageSection::Buildup => {

View File

@ -13,6 +13,8 @@ pub struct StaticData {
pub buildup_duration: Duration,
/// How long the state has until exiting
pub recover_duration: Duration,
/// Fraction of normal movement speed allowed during the state
pub movement_speed: f32,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
@ -30,8 +32,10 @@ pub struct Data {
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData) -> StateUpdate {
println!("stunned");
let mut update = StateUpdate::from(data);
handle_move(data, &mut update, self.static_data.movement_speed);
match self.stage_section {
StageSection::Buildup => {
if self.timer < self.static_data.buildup_duration {

View File

@ -101,9 +101,6 @@ impl<'a> System<'a> for Sys {
// Check if entity is dodging
let is_dodge = char_state_b_maybe.map_or(false, |c_s| c_s.is_melee_dodge());
// Check if entity is stunned
let is_stunned = char_state_b_maybe.map_or(false, |c_s| c_s.is_stunned());
// Check if it is a hit
if entity != b
&& !health_b.is_dead
@ -132,12 +129,8 @@ impl<'a> System<'a> for Sys {
}
}
let change = damage.modify_damage(inventories.get(b), Some(*uid));
let poise_change = if is_stunned {
poise_change.set_zero()
} else {
poise_change.modify_poise_damage(inventories.get(b))
};
let change = damage.modify_damage(loadouts.get(b), Some(*uid));
let poise_change = poise_change.modify_poise_damage(inventories.get(b));
server_emitter.emit(ServerEvent::Damage { entity: b, change });
// Apply bleeding buff on melee hits with 10% chance
@ -166,7 +159,7 @@ impl<'a> System<'a> for Sys {
server_emitter.emit(ServerEvent::PoiseChange {
entity: b,
change: poise_change,
kb_dir: *Dir::slerp(kb_dir, Dir::new(Vec3::unit_z()), 0.5),
kb_dir: *kb_dir,
});
attack.hit_count += 1;

View File

@ -167,8 +167,9 @@ impl<'a> System<'a> for Sys {
poise.reset();
*character_state = CharacterState::Stunned(common::states::stunned::Data {
static_data: common::states::stunned::StaticData {
buildup_duration: Duration::from_millis(100),
recover_duration: Duration::from_millis(100),
buildup_duration: Duration::from_millis(150),
recover_duration: Duration::from_millis(150),
movement_speed: 0.3,
},
timer: Duration::default(),
stage_section: common::states::utils::StageSection::Buildup,
@ -181,6 +182,7 @@ impl<'a> System<'a> for Sys {
static_data: common::states::stunned::StaticData {
buildup_duration: Duration::from_millis(500),
recover_duration: Duration::from_millis(500),
movement_speed: 0.1,
},
timer: Duration::default(),
stage_section: common::states::utils::StageSection::Buildup,
@ -211,8 +213,8 @@ impl<'a> System<'a> for Sys {
poise.reset();
*character_state = CharacterState::Staggered(common::states::staggered::Data {
static_data: common::states::staggered::StaticData {
buildup_duration: Duration::from_millis(5000),
recover_duration: Duration::from_millis(250),
buildup_duration: Duration::from_millis(3000),
recover_duration: Duration::from_millis(500),
},
timer: Duration::default(),
stage_section: common::states::utils::StageSection::Buildup,

View File

@ -12,9 +12,9 @@ use common::{
combat,
comp::{
self, aura, buff,
chat::{KillSource, KillType}, CharacterState,
object, Alignment, Body, Energy, EnergyChange, Group, Health, HealthChange, HealthSource,
Inventory, Item, Player, Poise, PoiseChange, PoiseSource, Pos, Stats,
chat::{KillSource, KillType},
object, Alignment, Body, CharacterState, Energy, EnergyChange, Group, Health, HealthChange,
HealthSource, Inventory, Item, Player, Poise, PoiseChange, PoiseSource, Pos, Stats,
},
effect::Effect,
lottery::Lottery,
@ -42,6 +42,7 @@ pub fn handle_poise(
) {
let ecs = &server.state.ecs();
if let Some(character_state) = ecs.read_storage::<CharacterState>().get(entity) {
// Entity is invincible to poise change during stunned/staggered character state
if !character_state.is_stunned() {
if let Some(poise) = ecs.write_storage::<Poise>().get_mut(entity) {
poise.change_by(change, knockback_dir);
@ -74,7 +75,7 @@ pub fn handle_knockback(server: &Server, entity: EcsEntity, impulse: Vec3<f32>)
}
let mut velocities = ecs.write_storage::<comp::Vel>();
if let Some(vel) = velocities.get_mut(entity) {
vel.0 = impulse;
vel.0 += impulse;
}
if let Some(client) = clients.get(entity) {
client.send_fallible(ServerGeneral::Knockback(impulse));
@ -435,17 +436,26 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, cause: HealthSourc
};
let pos = state.ecs().read_storage::<comp::Pos>().get(entity).cloned();
let vel = state.ecs().read_storage::<comp::Vel>().get(entity).cloned();
if let Some(pos) = pos {
let _ = state
.create_object(comp::Pos(pos.0 + Vec3::unit_z() * 0.25), match old_body {
Some(common::comp::Body::Humanoid(_)) => object::Body::Pouch,
Some(common::comp::Body::Golem(_)) => object::Body::Chest,
Some(common::comp::Body::BipedLarge(_))
| Some(common::comp::Body::QuadrupedLow(_)) => object::Body::MeatDrop,
_ => object::Body::Steak,
})
.with(item)
.build();
if let Some(vel) = vel {
let _ = state
.create_object(comp::Pos(pos.0 + Vec3::unit_z() * 0.25), match old_body {
Some(common::comp::Body::Humanoid(_)) => object::Body::Pouch,
Some(common::comp::Body::Golem(_)) => object::Body::Chest,
Some(common::comp::Body::BipedLarge(_))
| Some(common::comp::Body::QuadrupedLow(_)) => object::Body::MeatDrop,
_ => object::Body::Steak,
})
.with(vel)
.with(item)
.build();
} else {
error!(
?entity,
"Entity doesn't have a velocity, no bag is being dropped"
)
}
} else {
error!(
?entity,

View File

@ -22,7 +22,9 @@ pub mod sit;
pub mod sneak;
pub mod spin;
pub mod spinmelee;
pub mod staggered;
pub mod stand;
pub mod stunned;
pub mod swim;
pub mod swimwield;
pub mod wield;
@ -36,8 +38,8 @@ pub use self::{
jump::JumpAnimation, leapmelee::LeapAnimation, repeater::RepeaterAnimation,
roll::RollAnimation, run::RunAnimation, shockwave::ShockwaveAnimation, shoot::ShootAnimation,
sit::SitAnimation, sneak::SneakAnimation, spin::SpinAnimation, spinmelee::SpinMeleeAnimation,
stand::StandAnimation, swim::SwimAnimation, swimwield::SwimWieldAnimation,
wield::WieldAnimation,
staggered::StaggeredAnimation, stand::StandAnimation, stunned::StunnedAnimation,
swim::SwimAnimation, swimwield::SwimWieldAnimation, wield::WieldAnimation,
};
use super::{make_bone, vek::*, FigureBoneData, Skeleton};
use common::comp;

View File

@ -123,15 +123,19 @@ impl Animation for RunAnimation {
* Quaternion::rotation_x(head_look.y + 0.45 * speednorm);
next.head.scale = Vec3::one() * s_a.head_scale;
next.chest.position = Vec3::new(0.0, s_a.chest.0, s_a.chest.1 + 1.0 + shortalt * -0.8);
next.chest.position = Vec3::new(
0.0,
s_a.chest.0,
s_a.chest.1 + 1.0 * speednorm + shortalt * -0.8,
);
next.chest.orientation = Quaternion::rotation_z(short * 0.06 + tilt * -0.6)
* Quaternion::rotation_y(tilt * 1.6)
* Quaternion::rotation_x(
impact * 0.06 + shortalter * 0.035 + speed * -0.07 + (tilt.abs()),
impact * 0.06 + shortalter * 0.035 + speednorm * -0.5 + (tilt.abs()),
);
next.belt.position = Vec3::new(0.0, 0.25 + s_a.belt.0, 0.25 + s_a.belt.1);
next.belt.orientation = Quaternion::rotation_x(0.1)
next.belt.orientation = Quaternion::rotation_x(0.1 * speednorm)
* Quaternion::rotation_z(short * 0.1 + tilt * -1.1)
* Quaternion::rotation_y(tilt * 0.5);
@ -140,34 +144,36 @@ impl Animation for RunAnimation {
Quaternion::rotation_x(-0.05 + short * 0.02 + noisea * 0.02 + noiseb * 0.02);
next.shorts.position = Vec3::new(0.0, 0.65 + s_a.shorts.0, 0.65 + s_a.shorts.1);
next.shorts.orientation = Quaternion::rotation_x(0.2)
next.shorts.orientation = Quaternion::rotation_x(0.2 * speednorm)
* Quaternion::rotation_z(short * 0.25 + tilt * -1.5)
* Quaternion::rotation_y(tilt * 0.7);
next.hand_l.position = Vec3::new(
-s_a.hand.0 + foothorir * -1.3,
3.0 + s_a.hand.1 + foothorir * -7.0 * speednorm,
1.5 + s_a.hand.2 - foothorir * 5.5 * speednorm,
-s_a.hand.0 + foothorir * -1.3 * speednorm,
3.0 * speednorm + s_a.hand.1 + foothorir * -7.0 * speednorm,
1.5 * speednorm + s_a.hand.2 - foothorir * 5.5 * speednorm,
);
next.hand_l.orientation = Quaternion::rotation_x(0.6 + (footrotr * -1.2) * speednorm)
* Quaternion::rotation_y(footrotr * 0.4);
next.hand_l.orientation =
Quaternion::rotation_x(0.6 * speednorm + (footrotr * -1.2) * speednorm)
* Quaternion::rotation_y(footrotr * 0.4 * speednorm);
next.hand_r.position = Vec3::new(
s_a.hand.0 + foothoril * 1.3,
3.0 + s_a.hand.1 + foothoril * -7.0 * speednorm,
1.5 + s_a.hand.2 - foothoril * 5.5 * speednorm,
s_a.hand.0 + foothoril * 1.3 * speednorm,
3.0 * speednorm + s_a.hand.1 + foothoril * -7.0 * speednorm,
1.5 * speednorm + s_a.hand.2 - foothoril * 5.5 * speednorm,
);
next.hand_r.orientation = Quaternion::rotation_x(0.6 + (footrotl * -1.2) * speednorm)
* Quaternion::rotation_y(footrotl * -0.4);
next.hand_r.orientation =
Quaternion::rotation_x(0.6 * speednorm + (footrotl * -1.2) * speednorm)
* Quaternion::rotation_y(footrotl * -0.4 * speednorm);
//
next.foot_l.position = Vec3::new(
-s_a.foot.0 + footstrafel * sideabs * 3.0 + tilt * -2.0,
s_a.foot.1
+ (1.0 - sideabs) * (-1.5 + foothoril * -10.5 * speednorm)
+ (1.0 - sideabs) * (-1.5 * speednorm + foothoril * -10.5 * speednorm)
+ (direction * 5.0).max(0.0),
s_a.foot.2
+ (1.0 - sideabs) * (2.0 + ((footvertl * -2.1 * speednorm).max(-1.0)))
+ (1.0 - sideabs) * (2.0 * speednorm + ((footvertl * -2.1 * speednorm).max(-1.0)))
+ side * ((footvertsl * 1.5).max(-1.0)),
);
next.foot_l.orientation = Quaternion::rotation_x(
@ -179,10 +185,10 @@ impl Animation for RunAnimation {
next.foot_r.position = Vec3::new(
s_a.foot.0 + footstrafer * sideabs * 3.0 + tilt * -2.0,
s_a.foot.1
+ (1.0 - sideabs) * (-1.5 + foothorir * -10.5 * speednorm)
+ (1.0 - sideabs) * (-1.5 * speednorm + foothorir * -10.5 * speednorm)
+ (direction * 5.0).max(0.0),
s_a.foot.2
+ (1.0 - sideabs) * (2.0 + ((footvertr * -2.1 * speednorm).max(-1.0)))
+ (1.0 - sideabs) * (2.0 * speednorm + ((footvertr * -2.1 * speednorm).max(-1.0)))
+ side * ((footvertsr * -1.5).max(-1.0)),
);
next.foot_r.orientation = Quaternion::rotation_x(
@ -253,7 +259,7 @@ impl Animation for RunAnimation {
next.lantern.scale = Vec3::one() * 0.65;
next.hold.scale = Vec3::one() * 0.0;
next.torso.position = Vec3::new(0.0, -0.3, 0.0) * s_a.scaler;
next.torso.position = Vec3::new(0.0, 0.0, 0.0) * s_a.scaler;
next.torso.scale = Vec3::one() / 11.0 * s_a.scaler;
next.second.scale = match (

View File

@ -0,0 +1,213 @@
use super::{
super::{vek::*, Animation},
CharacterSkeleton, SkeletonAttr,
};
use common::{comp::item::ToolKind, states::utils::StageSection};
pub struct StaggeredAnimation;
impl Animation for StaggeredAnimation {
type Dependency = (
Option<ToolKind>,
Option<ToolKind>,
f32,
f64,
Option<StageSection>,
f64,
bool,
);
type Skeleton = CharacterSkeleton;
#[cfg(feature = "use-dyn-lib")]
const UPDATE_FN: &'static [u8] = b"character_staggered\0";
#[cfg_attr(feature = "be-dyn-lib", export_name = "character_staggered")]
#[allow(clippy::approx_constant)] // TODO: Pending review in #587
fn update_skeleton_inner(
skeleton: &Self::Skeleton,
(
active_tool_kind,
_second_tool_kind,
_velocity,
global_time,
stage_section,
timer,
wield_status,
): Self::Dependency,
anim_time: f64,
rate: &mut f32,
s_a: &SkeletonAttr,
) -> Self::Skeleton {
*rate = 1.0;
let mut next = (*skeleton).clone();
let (movement1base, movement2) = match stage_section {
Some(StageSection::Buildup) => ((anim_time as f32).powf(0.25), 0.0),
Some(StageSection::Recover) => (1.0, (anim_time as f32).powf(4.0)),
_ => (0.0, 0.0),
};
let pullback = 1.0 - movement2;
let subtract = global_time - timer;
let check = subtract - subtract.trunc();
let mirror = (check - 0.5).signum() as f32;
let movement1 = movement1base * pullback * mirror;
let movement1abs = movement1base * pullback;
next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1);
next.head.orientation =
Quaternion::rotation_x(movement1abs * -0.2) * Quaternion::rotation_z(movement1 * 0.3);
next.shorts.orientation =
Quaternion::rotation_x(movement1abs * 0.2) * Quaternion::rotation_z(movement1 * -0.3);
next.belt.orientation =
Quaternion::rotation_x(movement1abs * 0.1) * Quaternion::rotation_z(movement1 * -0.2);
next.shorts.position = Vec3::new(0.0, s_a.shorts.0 + movement1abs * 1.0, s_a.shorts.1);
next.chest.position = Vec3::new(0.0, s_a.chest.0, s_a.chest.1 + movement1abs * -4.0);
next.chest.orientation =
Quaternion::rotation_x(movement1abs * -0.1) * Quaternion::rotation_z(movement1 * 1.0);
if wield_status {
next.main.position = Vec3::new(0.0, 0.0, 0.0);
next.main.orientation = Quaternion::rotation_x(0.0);
match active_tool_kind {
Some(ToolKind::Sword) => {
next.hand_l.position = Vec3::new(s_a.shl.0, s_a.shl.1, s_a.shl.2);
next.hand_l.orientation =
Quaternion::rotation_x(s_a.shl.3) * Quaternion::rotation_y(s_a.shl.4);
next.hand_r.position = Vec3::new(s_a.shr.0, s_a.shr.1, s_a.shr.2);
next.hand_r.orientation =
Quaternion::rotation_x(s_a.shr.3) * Quaternion::rotation_y(s_a.shr.4);
next.control.position = Vec3::new(s_a.sc.0, s_a.sc.1, s_a.sc.2);
next.control.orientation = Quaternion::rotation_x(s_a.sc.3);
},
Some(ToolKind::Axe) => {
next.hand_l.position = Vec3::new(s_a.ahl.0, s_a.ahl.1, s_a.ahl.2);
next.hand_l.orientation =
Quaternion::rotation_x(s_a.ahl.3) * Quaternion::rotation_y(s_a.ahl.4);
next.hand_r.position = Vec3::new(s_a.ahr.0, s_a.ahr.1, s_a.ahr.2);
next.hand_r.orientation =
Quaternion::rotation_x(s_a.ahr.3) * Quaternion::rotation_z(s_a.ahr.5);
next.control.position = Vec3::new(s_a.ac.0, s_a.ac.1, s_a.ac.2);
next.control.orientation = Quaternion::rotation_x(s_a.ac.3)
* Quaternion::rotation_y(s_a.ac.4)
* Quaternion::rotation_z(s_a.ac.5);
},
Some(ToolKind::Hammer) => {
next.hand_l.position = Vec3::new(s_a.hhl.0, s_a.hhl.1, s_a.hhl.2);
next.hand_l.orientation =
Quaternion::rotation_x(s_a.hhl.3) * Quaternion::rotation_y(s_a.hhl.4);
next.hand_r.position = Vec3::new(s_a.hhr.0, s_a.hhr.1, s_a.hhr.2);
next.hand_r.orientation =
Quaternion::rotation_x(s_a.hhr.3) * Quaternion::rotation_y(s_a.hhr.4);
next.control.position = Vec3::new(s_a.hc.0, s_a.hc.1, s_a.hc.2);
next.control.orientation = Quaternion::rotation_x(s_a.hc.3)
* Quaternion::rotation_y(s_a.hc.4)
* Quaternion::rotation_z(s_a.hc.5);
},
Some(ToolKind::Staff) | Some(ToolKind::Sceptre) => {
next.hand_r.position = Vec3::new(s_a.sthr.0, s_a.sthr.1, s_a.sthr.2);
next.hand_r.orientation =
Quaternion::rotation_x(s_a.sthr.3) * Quaternion::rotation_y(s_a.sthr.4);
next.control.position = Vec3::new(s_a.stc.0, s_a.stc.1, s_a.stc.2);
next.hand_l.position = Vec3::new(s_a.sthl.0, s_a.sthl.1, s_a.sthl.2);
next.hand_l.orientation = Quaternion::rotation_x(s_a.sthl.3);
next.control.orientation = Quaternion::rotation_x(s_a.stc.3)
* Quaternion::rotation_y(s_a.stc.4)
* Quaternion::rotation_z(s_a.stc.5);
},
Some(ToolKind::Bow) => {
next.hand_l.position = Vec3::new(s_a.bhl.0, s_a.bhl.1, s_a.bhl.2);
next.hand_l.orientation = Quaternion::rotation_x(s_a.bhl.3);
next.hand_r.position = Vec3::new(s_a.bhr.0, s_a.bhr.1, s_a.bhr.2);
next.hand_r.orientation = Quaternion::rotation_x(s_a.bhr.3);
next.hold.position = Vec3::new(0.0, -1.0, -5.2);
next.hold.orientation = Quaternion::rotation_x(-1.57);
next.hold.scale = Vec3::one() * 1.0;
next.control.position = Vec3::new(s_a.bc.0, s_a.bc.1, s_a.bc.2);
next.control.orientation =
Quaternion::rotation_y(s_a.bc.4) * Quaternion::rotation_z(s_a.bc.5);
},
Some(ToolKind::Debug) => {
next.hand_l.position = Vec3::new(-7.0, 4.0, 3.0);
next.hand_l.orientation = Quaternion::rotation_x(1.27);
next.main.position = Vec3::new(-5.0, 5.0, 23.0);
next.main.orientation = Quaternion::rotation_x(3.14);
},
Some(ToolKind::Farming) => {
next.hand_l.position = Vec3::new(9.0, 1.0, 1.0);
next.hand_l.orientation = Quaternion::rotation_x(1.57);
next.hand_r.position = Vec3::new(9.0, 1.0, 11.0);
next.hand_r.orientation = Quaternion::rotation_x(1.57);
next.main.position = Vec3::new(7.5, 7.5, 13.2);
next.main.orientation = Quaternion::rotation_y(3.14);
next.control.position = Vec3::new(-11.0, 1.8, 4.0);
},
_ => {},
}
} else {
if mirror > 0.0 {
next.hand_r.position = Vec3::new(
s_a.hand.0 + movement1abs * -9.0,
s_a.hand.1 + movement1 * 9.0,
s_a.hand.2 + movement1 * 5.0,
);
next.hand_r.orientation = Quaternion::rotation_x(movement1 * 1.2)
* Quaternion::rotation_y(movement1 * 1.5);
next.hand_l.position = Vec3::new(
-s_a.hand.0,
s_a.hand.1 + movement1abs * 3.0,
s_a.hand.2 + movement1abs * -1.0,
);
next.hand_l.orientation = Quaternion::rotation_x(movement1abs * 0.5)
* Quaternion::rotation_y(movement1 * 0.3);
next.foot_l.position =
Vec3::new(-s_a.foot.0, s_a.foot.1 + movement1abs * -7.0, s_a.foot.2);
next.foot_l.orientation = Quaternion::rotation_x(movement1abs * -1.2)
* Quaternion::rotation_z(movement1 * 0.8);
next.foot_r.position = Vec3::new(
s_a.foot.0 + movement1 * -5.0,
s_a.foot.1 + movement1abs * 3.0,
s_a.foot.2,
);
next.foot_r.orientation = Quaternion::rotation_z(movement1 * 0.6);
} else {
next.hand_l.position = Vec3::new(
-s_a.hand.0 + movement1abs * 9.0,
s_a.hand.1 + movement1abs * 9.0,
s_a.hand.2 + movement1abs * 5.0,
);
next.hand_l.orientation = Quaternion::rotation_x(movement1abs * 1.2)
* Quaternion::rotation_y(movement1 * 1.5);
next.hand_r.position = Vec3::new(
s_a.hand.0,
s_a.hand.1 + movement1abs * 3.0,
s_a.hand.2 + movement1abs * -1.0,
);
next.hand_r.orientation = Quaternion::rotation_x(movement1abs * 0.5)
* Quaternion::rotation_y(movement1 * 0.3);
next.foot_r.position =
Vec3::new(s_a.foot.0, s_a.foot.1 + movement1abs * -7.0, s_a.foot.2);
next.foot_r.orientation = Quaternion::rotation_x(movement1abs * -1.2)
* Quaternion::rotation_z(movement1 * 0.8);
next.foot_l.position = Vec3::new(
-s_a.foot.0 + movement1 * -5.0,
s_a.foot.1 + movement1abs * 3.0,
s_a.foot.2,
);
next.foot_l.orientation = Quaternion::rotation_z(movement1 * 0.6);
};
};
next.torso.position = Vec3::new(0.0, 0.0, 0.0) * s_a.scaler;
next.torso.orientation = Quaternion::rotation_z(0.0);
next
}
}

View File

@ -0,0 +1,177 @@
use super::{
super::{vek::*, Animation},
CharacterSkeleton, SkeletonAttr,
};
use common::{comp::item::ToolKind, states::utils::StageSection};
pub struct StunnedAnimation;
impl Animation for StunnedAnimation {
type Dependency = (
Option<ToolKind>,
Option<ToolKind>,
f32,
f64,
Option<StageSection>,
f64,
bool,
);
type Skeleton = CharacterSkeleton;
#[cfg(feature = "use-dyn-lib")]
const UPDATE_FN: &'static [u8] = b"character_stunned\0";
#[cfg_attr(feature = "be-dyn-lib", export_name = "character_stunned")]
#[allow(clippy::approx_constant)] // TODO: Pending review in #587
fn update_skeleton_inner(
skeleton: &Self::Skeleton,
(
active_tool_kind,
_second_tool_kind,
_velocity,
global_time,
stage_section,
timer,
wield_status,
): Self::Dependency,
anim_time: f64,
rate: &mut f32,
s_a: &SkeletonAttr,
) -> Self::Skeleton {
*rate = 1.0;
let mut next = (*skeleton).clone();
let (movement1base, movement2) = match stage_section {
Some(StageSection::Buildup) => ((anim_time as f32).powf(0.25), 0.0),
Some(StageSection::Recover) => (1.0, (anim_time as f32).powf(4.0)),
_ => (0.0, 0.0),
};
let pullback = 1.0 - movement2;
let subtract = global_time - timer;
let check = subtract - subtract.trunc();
let mirror = (check - 0.5).signum() as f32;
let movement1 = movement1base * pullback * mirror;
let movement1abs = movement1base * pullback;
println!("wield {}", wield_status);
next.head.position = Vec3::new(0.0, s_a.head.0, s_a.head.1);
next.head.orientation = Quaternion::rotation_z(movement1 * 0.3);
next.shorts.orientation =
Quaternion::rotation_x(movement1abs * -0.2) * Quaternion::rotation_z(movement1 * -0.3);
next.belt.orientation =
Quaternion::rotation_x(movement1abs * -0.1) * Quaternion::rotation_z(movement1 * -0.2);
next.chest.orientation =
Quaternion::rotation_x(movement1abs * 0.3) * Quaternion::rotation_z(movement1 * 0.5);
if wield_status {
next.main.position = Vec3::new(0.0, 0.0, 0.0);
next.main.orientation = Quaternion::rotation_x(0.0);
match active_tool_kind {
Some(ToolKind::Sword) => {
next.hand_l.position = Vec3::new(s_a.shl.0, s_a.shl.1, s_a.shl.2);
next.hand_l.orientation =
Quaternion::rotation_x(s_a.shl.3) * Quaternion::rotation_y(s_a.shl.4);
next.hand_r.position = Vec3::new(s_a.shr.0, s_a.shr.1, s_a.shr.2);
next.hand_r.orientation =
Quaternion::rotation_x(s_a.shr.3) * Quaternion::rotation_y(s_a.shr.4);
next.control.position = Vec3::new(s_a.sc.0, s_a.sc.1, s_a.sc.2);
next.control.orientation = Quaternion::rotation_x(s_a.sc.3);
},
Some(ToolKind::Axe) => {
next.hand_l.position = Vec3::new(s_a.ahl.0, s_a.ahl.1, s_a.ahl.2);
next.hand_l.orientation =
Quaternion::rotation_x(s_a.ahl.3) * Quaternion::rotation_y(s_a.ahl.4);
next.hand_r.position = Vec3::new(s_a.ahr.0, s_a.ahr.1, s_a.ahr.2);
next.hand_r.orientation =
Quaternion::rotation_x(s_a.ahr.3) * Quaternion::rotation_z(s_a.ahr.5);
next.control.position = Vec3::new(s_a.ac.0, s_a.ac.1, s_a.ac.2);
next.control.orientation = Quaternion::rotation_x(s_a.ac.3)
* Quaternion::rotation_y(s_a.ac.4)
* Quaternion::rotation_z(s_a.ac.5);
},
Some(ToolKind::Hammer) => {
next.hand_l.position = Vec3::new(s_a.hhl.0, s_a.hhl.1, s_a.hhl.2);
next.hand_l.orientation =
Quaternion::rotation_x(s_a.hhl.3) * Quaternion::rotation_y(s_a.hhl.4);
next.hand_r.position = Vec3::new(s_a.hhr.0, s_a.hhr.1, s_a.hhr.2);
next.hand_r.orientation =
Quaternion::rotation_x(s_a.hhr.3) * Quaternion::rotation_y(s_a.hhr.4);
next.control.position = Vec3::new(s_a.hc.0, s_a.hc.1, s_a.hc.2);
next.control.orientation = Quaternion::rotation_x(s_a.hc.3)
* Quaternion::rotation_y(s_a.hc.4)
* Quaternion::rotation_z(s_a.hc.5);
},
Some(ToolKind::Staff) | Some(ToolKind::Sceptre) => {
next.hand_r.position = Vec3::new(s_a.sthr.0, s_a.sthr.1, s_a.sthr.2);
next.hand_r.orientation =
Quaternion::rotation_x(s_a.sthr.3) * Quaternion::rotation_y(s_a.sthr.4);
next.control.position = Vec3::new(s_a.stc.0, s_a.stc.1, s_a.stc.2);
next.hand_l.position = Vec3::new(s_a.sthl.0, s_a.sthl.1, s_a.sthl.2);
next.hand_l.orientation = Quaternion::rotation_x(s_a.sthl.3);
next.control.orientation = Quaternion::rotation_x(s_a.stc.3)
* Quaternion::rotation_y(s_a.stc.4)
* Quaternion::rotation_z(s_a.stc.5);
},
Some(ToolKind::Bow) => {
next.hand_l.position = Vec3::new(s_a.bhl.0, s_a.bhl.1, s_a.bhl.2);
next.hand_l.orientation = Quaternion::rotation_x(s_a.bhl.3);
next.hand_r.position = Vec3::new(s_a.bhr.0, s_a.bhr.1, s_a.bhr.2);
next.hand_r.orientation = Quaternion::rotation_x(s_a.bhr.3);
next.hold.position = Vec3::new(0.0, -1.0, -5.2);
next.hold.orientation = Quaternion::rotation_x(-1.57);
next.hold.scale = Vec3::one() * 1.0;
next.control.position = Vec3::new(s_a.bc.0, s_a.bc.1, s_a.bc.2);
next.control.orientation =
Quaternion::rotation_y(s_a.bc.4) * Quaternion::rotation_z(s_a.bc.5);
},
Some(ToolKind::Debug) => {
next.hand_l.position = Vec3::new(-7.0, 4.0, 3.0);
next.hand_l.orientation = Quaternion::rotation_x(1.27);
next.main.position = Vec3::new(-5.0, 5.0, 23.0);
next.main.orientation = Quaternion::rotation_x(3.14);
},
Some(ToolKind::Farming) => {
next.hand_l.position = Vec3::new(9.0, 1.0, 1.0);
next.hand_l.orientation = Quaternion::rotation_x(1.57);
next.hand_r.position = Vec3::new(9.0, 1.0, 11.0);
next.hand_r.orientation = Quaternion::rotation_x(1.57);
next.main.position = Vec3::new(7.5, 7.5, 13.2);
next.main.orientation = Quaternion::rotation_y(3.14);
next.control.position = Vec3::new(-11.0, 1.8, 4.0);
},
_ => {},
}
} else {
if mirror > 0.0 {
next.hand_r.position = Vec3::new(
s_a.hand.0 + movement1abs * -4.0,
s_a.hand.1 + movement1 * 7.0,
s_a.hand.2 + movement1 * 6.0,
);
next.hand_r.orientation = Quaternion::rotation_x(movement1 * 1.2)
* Quaternion::rotation_y(movement1 * 1.2);
} else {
next.hand_l.position = Vec3::new(
-s_a.hand.0 + movement1abs * 4.0,
s_a.hand.1 + movement1abs * 7.0,
s_a.hand.2 + movement1abs * 6.0,
);
next.hand_l.orientation = Quaternion::rotation_x(movement1abs * 1.2)
* Quaternion::rotation_y(movement1 * 1.2);
};
};
next.torso.position = Vec3::new(0.0, 0.0, 0.0) * s_a.scaler;
next.torso.orientation = Quaternion::rotation_z(0.0);
next
}
}

View File

@ -148,8 +148,7 @@ pub fn init() {
// "Debounces" events since I can't find the option to do this in the latest
// `notify`
thread::spawn(move || {
let mut modified_paths = HashSet::new();
let mut modified_paths = std::collections::HashSet::new();
while let Ok(path) = reload_recv.recv() {
modified_paths.insert(path);
// Wait for any additional modify events before reloading

View File

@ -1122,6 +1122,62 @@ impl FigureMgr {
skeleton_attr,
)
},
CharacterState::Stunned(s) => {
let stage_time = s.timer.as_secs_f64();
let wield_status = s.was_wielded;
let stage_progress = match s.stage_section {
StageSection::Buildup => {
stage_time / s.static_data.buildup_duration.as_secs_f64()
},
StageSection::Recover => {
stage_time / s.static_data.recover_duration.as_secs_f64()
},
_ => 0.0,
};
anim::character::StunnedAnimation::update_skeleton(
&target_base,
(
active_tool_kind,
second_tool_kind,
vel.0.magnitude(),
time,
Some(s.stage_section),
state.state_time,
wield_status,
),
stage_progress,
&mut state_animation_rate,
skeleton_attr,
)
},
CharacterState::Staggered(s) => {
let stage_time = s.timer.as_secs_f64();
let wield_status = s.was_wielded;
let stage_progress = match s.stage_section {
StageSection::Buildup => {
stage_time / s.static_data.buildup_duration.as_secs_f64()
},
StageSection::Recover => {
stage_time / s.static_data.recover_duration.as_secs_f64()
},
_ => 0.0,
};
anim::character::StaggeredAnimation::update_skeleton(
&target_base,
(
active_tool_kind,
second_tool_kind,
vel.0.magnitude(),
time,
Some(s.stage_section),
state.state_time,
wield_status,
),
stage_progress,
&mut state_animation_rate,
skeleton_attr,
)
},
CharacterState::BasicBeam(s) => {
let stage_time = s.timer.as_secs_f64();
let stage_progress = match s.stage_section {