Merge branch 'sam/sword-overhaul' into 'master'

Sword overhaul

Closes #763 and #761

See merge request veloren/veloren!1365
This commit is contained in:
Samuel Keiffer 2020-09-21 23:55:49 +00:00
commit 18b912ea6f
35 changed files with 2012 additions and 994 deletions

View File

@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Figure meshing no longer blocks the main thread.
- Overhauled persistence layer including no longer storing serialized JSON items in the database
- Overhauled representation of blocks to permit fluid and sprite coexistence
- Overhauled sword
### Removed

View File

@ -6,7 +6,7 @@ ItemDef(
kind: Sword("Zweihander0"),
stats: (
equip_time_millis: 500,
power: 0.2,
power: 0.75,
),
)
),

View File

@ -94,19 +94,19 @@
],
threshold: 0.5,
),
Attack(TripleStrike(First), Sword): (
Attack(ComboMelee(Swing, 1), Sword): (
files: [
"voxygen.audio.sfx.abilities.swing_sword",
],
threshold: 0.7,
),
Attack(TripleStrike(Second), Sword): (
Attack(ComboMelee(Swing, 2), Sword): (
files: [
"voxygen.audio.sfx.abilities.separated_second_swing",
],
threshold: 0.7,
),
Attack(TripleStrike(Third), Sword): (
Attack(ComboMelee(Swing, 3), Sword): (
files: [
"voxygen.audio.sfx.abilities.separated_third_swing",
],
@ -174,19 +174,7 @@
],
threshold: 0.5,
),
Attack(TripleStrike(First), Axe): (
files: [
"voxygen.audio.sfx.abilities.swing",
],
threshold: 0.7,
),
Attack(TripleStrike(Second), Axe): (
files: [
"voxygen.audio.sfx.abilities.swing",
],
threshold: 0.7,
),
Attack(TripleStrike(Third), Axe): (
Attack(BasicMelee, Axe): (
files: [
"voxygen.audio.sfx.abilities.swing",
],

BIN
assets/voxygen/element/icons/sword_whirlwind.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -1,10 +1,9 @@
use crate::{
comp::{
ability::Stage,
item::{armor::Protection, Item, ItemKind},
Body, CharacterState, EnergySource, Gravity, LightEmitter, Projectile, StateUpdate,
},
states::{triple_strike::*, *},
states::{utils::StageSection, *},
sys::character_behavior::JoinData,
};
use arraygen::Arraygen;
@ -21,7 +20,7 @@ pub enum CharacterAbilityType {
ChargedRanged,
DashMelee,
BasicBlock,
TripleStrike(Stage),
ComboMelee(StageSection, u32),
LeapMelee,
SpinMelee,
GroundShockwave,
@ -36,7 +35,7 @@ impl From<&CharacterState> for CharacterAbilityType {
CharacterState::DashMelee(_) => Self::DashMelee,
CharacterState::BasicBlock => Self::BasicBlock,
CharacterState::LeapMelee(_) => Self::LeapMelee,
CharacterState::TripleStrike(data) => Self::TripleStrike(data.stage),
CharacterState::ComboMelee(data) => Self::ComboMelee(data.stage_section, data.stage),
CharacterState::SpinMelee(_) => Self::SpinMelee,
CharacterState::ChargedRanged(_) => Self::ChargedRanged,
CharacterState::GroundShockwave(_) => Self::ChargedRanged,
@ -73,15 +72,31 @@ pub enum CharacterAbility {
},
DashMelee {
energy_cost: u32,
buildup_duration: Duration,
recover_duration: Duration,
base_damage: u32,
max_damage: u32,
base_knockback: f32,
max_knockback: f32,
range: f32,
angle: f32,
energy_drain: u32,
forward_speed: f32,
buildup_duration: Duration,
charge_duration: Duration,
swing_duration: Duration,
recover_duration: Duration,
infinite_charge: bool,
is_interruptible: bool,
},
BasicBlock,
Roll,
TripleStrike {
base_damage: u32,
needs_timing: bool,
ComboMelee {
stage_data: Vec<combo_melee::Stage>,
initial_energy_gain: u32,
max_energy_gain: u32,
energy_increase: u32,
speed_increase: f32,
max_speed_increase: f32,
is_interruptible: bool,
},
LeapMelee {
energy_cost: u32,
@ -91,10 +106,18 @@ pub enum CharacterAbility {
base_damage: u32,
},
SpinMelee {
energy_cost: u32,
buildup_duration: Duration,
swing_duration: Duration,
recover_duration: Duration,
base_damage: u32,
knockback: f32,
range: f32,
energy_cost: u32,
is_infinite: bool,
is_helicopter: bool,
is_interruptible: bool,
forward_speed: f32,
num_spins: u32,
},
ChargedRanged {
energy_cost: u32,
@ -130,11 +153,6 @@ impl CharacterAbility {
/// applicable.
pub fn requirements_paid(&self, data: &JoinData, update: &mut StateUpdate) -> bool {
match self {
CharacterAbility::TripleStrike { .. } => {
data.physics.on_ground
&& data.body.is_humanoid()
&& data.inputs.look_dir.xy().magnitude_squared() > 0.01
},
CharacterAbility::Roll => {
data.physics.on_ground
&& data.body.is_humanoid()
@ -313,35 +331,71 @@ impl From<&CharacterAbility> for CharacterState {
}),
CharacterAbility::DashMelee {
energy_cost: _,
buildup_duration,
recover_duration,
base_damage,
max_damage,
base_knockback,
max_knockback,
range,
angle,
energy_drain,
forward_speed,
buildup_duration,
charge_duration,
swing_duration,
recover_duration,
infinite_charge,
is_interruptible,
} => CharacterState::DashMelee(dash_melee::Data {
initialize: true,
static_data: dash_melee::StaticData {
base_damage: *base_damage,
max_damage: *max_damage,
base_knockback: *base_knockback,
max_knockback: *max_knockback,
range: *range,
angle: *angle,
energy_drain: *energy_drain,
forward_speed: *forward_speed,
infinite_charge: *infinite_charge,
buildup_duration: *buildup_duration,
charge_duration: *charge_duration,
swing_duration: *swing_duration,
recover_duration: *recover_duration,
is_interruptible: *is_interruptible,
},
end_charge: false,
timer: Duration::default(),
stage_section: StageSection::Buildup,
exhausted: false,
buildup_duration: *buildup_duration,
recover_duration: *recover_duration,
base_damage: *base_damage,
}),
CharacterAbility::BasicBlock => CharacterState::BasicBlock,
CharacterAbility::Roll => CharacterState::Roll(roll::Data {
remaining_duration: Duration::from_millis(500),
was_wielded: false, // false by default. utils might set it to true
}),
CharacterAbility::TripleStrike {
base_damage,
needs_timing,
} => CharacterState::TripleStrike(triple_strike::Data {
base_damage: *base_damage,
stage: triple_strike::Stage::First,
stage_exhausted: false,
stage_time_active: Duration::default(),
initialized: false,
transition_style: if *needs_timing {
TransitionStyle::Timed(TimingState::NotPressed)
} else {
TransitionStyle::Hold(HoldingState::Holding)
CharacterAbility::ComboMelee {
stage_data,
initial_energy_gain,
max_energy_gain,
energy_increase,
speed_increase,
max_speed_increase,
is_interruptible,
} => CharacterState::ComboMelee(combo_melee::Data {
static_data: combo_melee::StaticData {
num_stages: stage_data.len() as u32,
stage_data: stage_data.clone(),
initial_energy_gain: *initial_energy_gain,
max_energy_gain: *max_energy_gain,
energy_increase: *energy_increase,
speed_increase: 1.0 - *speed_increase,
max_speed_increase: *max_speed_increase - 1.0,
is_interruptible: *is_interruptible,
},
stage: 1,
combo: 0,
timer: Duration::default(),
stage_section: StageSection::Buildup,
next_stage: false,
}),
CharacterAbility::LeapMelee {
energy_cost: _,
@ -358,24 +412,37 @@ impl From<&CharacterAbility> for CharacterState {
base_damage: *base_damage,
}),
CharacterAbility::SpinMelee {
energy_cost,
buildup_duration,
swing_duration,
recover_duration,
base_damage,
knockback,
range,
energy_cost,
is_infinite,
is_helicopter,
is_interruptible,
forward_speed,
num_spins,
} => CharacterState::SpinMelee(spin_melee::Data {
static_data: spin_melee::StaticData {
buildup_duration: *buildup_duration,
swing_duration: *swing_duration,
recover_duration: *recover_duration,
base_damage: *base_damage,
knockback: *knockback,
range: *range,
energy_cost: *energy_cost,
is_infinite: *is_infinite,
is_helicopter: *is_helicopter,
is_interruptible: *is_interruptible,
forward_speed: *forward_speed,
num_spins: *num_spins,
},
timer: Duration::default(),
spins_remaining: *num_spins - 1,
stage_section: StageSection::Buildup,
exhausted: false,
energy_cost: *energy_cost,
buildup_duration: *buildup_duration,
buildup_duration_default: *buildup_duration,
recover_duration: *recover_duration,
recover_duration_default: *recover_duration,
base_damage: *base_damage,
// This isn't needed for it's continuous implementation, but is left in should this
// skill be moved to the skillbar
hits_remaining: 1,
hits_remaining_default: 1, /* Should be the same value as hits_remaining, also
* this value can be removed if ability moved to
* skillbar */
}),
CharacterAbility::ChargedRanged {
energy_cost: _,

View File

@ -62,7 +62,7 @@ pub enum CharacterState {
DashMelee(dash_melee::Data),
/// A three-stage attack where each attack pushes player forward
/// and successive attacks increase in damage, while player holds button.
TripleStrike(triple_strike::Data),
ComboMelee(combo_melee::Data),
/// A leap followed by a small aoe ground attack
LeapMelee(leap_melee::Data),
/// Spin around, dealing damage to enemies surrounding you
@ -80,7 +80,7 @@ impl CharacterState {
| CharacterState::BasicMelee(_)
| CharacterState::BasicRanged(_)
| CharacterState::DashMelee(_)
| CharacterState::TripleStrike(_)
| CharacterState::ComboMelee(_)
| CharacterState::BasicBlock
| CharacterState::LeapMelee(_)
| CharacterState::SpinMelee(_)
@ -94,7 +94,7 @@ impl CharacterState {
CharacterState::BasicMelee(_)
| CharacterState::BasicRanged(_)
| CharacterState::DashMelee(_)
| CharacterState::TripleStrike(_)
| CharacterState::ComboMelee(_)
| CharacterState::LeapMelee(_)
| CharacterState::SpinMelee(_)
| CharacterState::ChargedRanged(_)
@ -107,7 +107,7 @@ impl CharacterState {
CharacterState::BasicMelee(_)
| CharacterState::BasicRanged(_)
| CharacterState::DashMelee(_)
| CharacterState::TripleStrike(_)
| CharacterState::ComboMelee(_)
| CharacterState::BasicBlock
| CharacterState::LeapMelee(_)
| CharacterState::ChargedRanged(_)

View File

@ -1,8 +1,9 @@
// Note: If you changes here "break" old character saves you can change the
// version in voxygen\src\meta.rs in order to reset save files to being empty
use crate::comp::{
body::object, projectile, Body, CharacterAbility, Gravity, LightEmitter, Projectile,
use crate::{
comp::{body::object, projectile, Body, CharacterAbility, Gravity, LightEmitter, Projectile},
states::combo_melee,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
@ -116,27 +117,110 @@ impl Tool {
match &self.kind {
Sword(_) => vec![
TripleStrike {
base_damage: (60.0 * self.base_power()) as u32,
needs_timing: false,
ComboMelee {
stage_data: vec![
combo_melee::Stage {
stage: 1,
base_damage: (100.0 * self.base_power()) as u32,
max_damage: (120.0 * self.base_power()) as u32,
damage_increase: (10.0 * self.base_power()) as u32,
knockback: 10.0,
range: 4.0,
angle: 30.0,
base_buildup_duration: Duration::from_millis(350),
base_swing_duration: Duration::from_millis(100),
base_recover_duration: Duration::from_millis(400),
forward_movement: 0.5,
},
combo_melee::Stage {
stage: 2,
base_damage: (80.0 * self.base_power()) as u32,
max_damage: (110.0 * self.base_power()) as u32,
damage_increase: (15.0 * self.base_power()) as u32,
knockback: 12.0,
range: 3.5,
angle: 180.0,
base_buildup_duration: Duration::from_millis(400),
base_swing_duration: Duration::from_millis(600),
base_recover_duration: Duration::from_millis(400),
forward_movement: 0.0,
},
combo_melee::Stage {
stage: 3,
base_damage: (130.0 * self.base_power()) as u32,
max_damage: (170.0 * self.base_power()) as u32,
damage_increase: (20.0 * self.base_power()) as u32,
knockback: 14.0,
range: 6.0,
angle: 10.0,
base_buildup_duration: Duration::from_millis(500),
base_swing_duration: Duration::from_millis(200),
base_recover_duration: Duration::from_millis(300),
forward_movement: 1.2,
},
],
initial_energy_gain: 0,
max_energy_gain: 100,
energy_increase: 20,
speed_increase: 0.05,
max_speed_increase: 1.8,
is_interruptible: true,
},
DashMelee {
energy_cost: 700,
buildup_duration: Duration::from_millis(500),
recover_duration: Duration::from_millis(500),
energy_cost: 200,
base_damage: (120.0 * self.base_power()) as u32,
max_damage: (260.0 * self.base_power()) as u32,
base_knockback: 10.0,
max_knockback: 20.0,
range: 5.0,
angle: 45.0,
energy_drain: 500,
forward_speed: 4.0,
buildup_duration: Duration::from_millis(250),
charge_duration: Duration::from_millis(400),
swing_duration: Duration::from_millis(100),
recover_duration: Duration::from_millis(500),
infinite_charge: true,
is_interruptible: true,
},
SpinMelee {
buildup_duration: Duration::from_millis(750),
swing_duration: Duration::from_millis(500),
recover_duration: Duration::from_millis(500),
base_damage: (140.0 * self.base_power()) as u32,
knockback: 10.0,
range: 3.5,
energy_cost: 200,
is_infinite: false,
is_helicopter: false,
is_interruptible: true,
forward_speed: 1.0,
num_spins: 3,
},
],
Axe(_) => vec![
TripleStrike {
base_damage: (80.0 * self.base_power()) as u32,
needs_timing: true,
BasicMelee {
energy_cost: 0,
buildup_duration: Duration::from_millis(700),
recover_duration: Duration::from_millis(300),
base_healthchange: (-120.0 * self.base_power()) as i32,
knockback: 0.0,
range: 3.5,
max_angle: 20.0,
},
SpinMelee {
energy_cost: 100,
buildup_duration: Duration::from_millis(125),
recover_duration: Duration::from_millis(125),
buildup_duration: Duration::from_millis(100),
swing_duration: Duration::from_millis(250),
recover_duration: Duration::from_millis(100),
base_damage: (60.0 * self.base_power()) as u32,
knockback: 0.0,
range: 3.5,
energy_cost: 100,
is_infinite: true,
is_helicopter: true,
is_interruptible: false,
forward_speed: 0.0,
num_spins: 1,
},
],
Hammer(_) => vec![
@ -206,23 +290,15 @@ impl Tool {
max_projectile_speed: 500.0,
},
],
Dagger(_) => vec![
BasicMelee {
energy_cost: 0,
buildup_duration: Duration::from_millis(100),
recover_duration: Duration::from_millis(400),
base_healthchange: (-50.0 * self.base_power()) as i32,
knockback: 0.0,
range: 3.5,
max_angle: 20.0,
},
DashMelee {
energy_cost: 700,
buildup_duration: Duration::from_millis(500),
recover_duration: Duration::from_millis(500),
base_damage: (100.0 * self.base_power()) as u32,
},
],
Dagger(_) => vec![BasicMelee {
energy_cost: 0,
buildup_duration: Duration::from_millis(100),
recover_duration: Duration::from_millis(400),
base_healthchange: (-50.0 * self.base_power()) as i32,
knockback: 0.0,
range: 3.5,
max_angle: 20.0,
}],
Staff(kind) => {
if kind == "Sceptre" {
vec![

View File

@ -66,7 +66,12 @@ impl LoadoutBuilder {
}
/// Builds loadout of creature when spawned
pub fn build_loadout(body: Body, alignment: Alignment, mut main_tool: Option<Item>) -> Self {
pub fn build_loadout(
body: Body,
alignment: Alignment,
mut main_tool: Option<Item>,
is_giant: bool,
) -> Self {
#![allow(clippy::single_match)] // For when this is done to more than just golems.
match body {
Body::Golem(golem) => match golem.species {
@ -76,6 +81,13 @@ impl LoadoutBuilder {
));
},
},
Body::Humanoid(_) => {
if is_giant {
main_tool = Some(Item::new_from_asset_expect(
"common.items.npc_weapons.sword.zweihander_sword_0",
));
}
},
_ => {},
};
@ -112,78 +124,114 @@ impl LoadoutBuilder {
};
let loadout = match body {
Body::Humanoid(_) => match alignment {
Alignment::Npc => Loadout {
active_item,
second_item: None,
shoulder: None,
chest: Some(Item::new_from_asset_expect(
match rand::thread_rng().gen_range(0, 10) {
0 => "common.items.armor.chest.worker_green_0",
1 => "common.items.armor.chest.worker_green_1",
2 => "common.items.armor.chest.worker_red_0",
3 => "common.items.armor.chest.worker_red_1",
4 => "common.items.armor.chest.worker_purple_0",
5 => "common.items.armor.chest.worker_purple_1",
6 => "common.items.armor.chest.worker_yellow_0",
7 => "common.items.armor.chest.worker_yellow_1",
8 => "common.items.armor.chest.worker_orange_0",
_ => "common.items.armor.chest.worker_orange_1",
Body::Humanoid(_) => {
if is_giant {
Loadout {
active_item,
second_item: None,
shoulder: Some(Item::new_from_asset_expect(
"common.items.armor.shoulder.plate_0",
)),
chest: Some(Item::new_from_asset_expect(
"common.items.armor.chest.plate_green_0",
)),
belt: Some(Item::new_from_asset_expect(
"common.items.armor.belt.plate_0",
)),
hand: Some(Item::new_from_asset_expect(
"common.items.armor.hand.plate_0",
)),
pants: Some(Item::new_from_asset_expect(
"common.items.armor.pants.plate_green_0",
)),
foot: Some(Item::new_from_asset_expect(
"common.items.armor.foot.plate_0",
)),
back: None,
ring: None,
neck: None,
lantern: None,
glider: None,
head: None,
tabard: None,
}
} else {
match alignment {
Alignment::Npc => Loadout {
active_item,
second_item: None,
shoulder: None,
chest: Some(Item::new_from_asset_expect(
match rand::thread_rng().gen_range(0, 10) {
0 => "common.items.armor.chest.worker_green_0",
1 => "common.items.armor.chest.worker_green_1",
2 => "common.items.armor.chest.worker_red_0",
3 => "common.items.armor.chest.worker_red_1",
4 => "common.items.armor.chest.worker_purple_0",
5 => "common.items.armor.chest.worker_purple_1",
6 => "common.items.armor.chest.worker_yellow_0",
7 => "common.items.armor.chest.worker_yellow_1",
8 => "common.items.armor.chest.worker_orange_0",
_ => "common.items.armor.chest.worker_orange_1",
},
)),
belt: Some(Item::new_from_asset_expect(
"common.items.armor.belt.leather_0",
)),
hand: None,
pants: Some(Item::new_from_asset_expect(
"common.items.armor.pants.worker_blue_0",
)),
foot: Some(Item::new_from_asset_expect(
match rand::thread_rng().gen_range(0, 2) {
0 => "common.items.armor.foot.leather_0",
_ => "common.items.armor.starter.sandals_0",
},
)),
back: None,
ring: None,
neck: None,
lantern: None,
glider: None,
head: None,
tabard: None,
},
)),
belt: Some(Item::new_from_asset_expect(
"common.items.armor.belt.leather_0",
)),
hand: None,
pants: Some(Item::new_from_asset_expect(
"common.items.armor.pants.worker_blue_0",
)),
foot: Some(Item::new_from_asset_expect(
match rand::thread_rng().gen_range(0, 2) {
0 => "common.items.armor.foot.leather_0",
_ => "common.items.armor.starter.sandals_0",
Alignment::Enemy => Loadout {
active_item,
second_item: None,
shoulder: Some(Item::new_from_asset_expect(
"common.items.armor.shoulder.cultist_shoulder_purple",
)),
chest: Some(Item::new_from_asset_expect(
"common.items.armor.chest.cultist_chest_purple",
)),
belt: Some(Item::new_from_asset_expect(
"common.items.armor.belt.cultist_belt",
)),
hand: Some(Item::new_from_asset_expect(
"common.items.armor.hand.cultist_hands_purple",
)),
pants: Some(Item::new_from_asset_expect(
"common.items.armor.pants.cultist_legs_purple",
)),
foot: Some(Item::new_from_asset_expect(
"common.items.armor.foot.cultist_boots",
)),
back: Some(Item::new_from_asset_expect(
"common.items.armor.back.dungeon_purple-0",
)),
ring: None,
neck: None,
lantern: Some(Item::new_from_asset_expect(
"common.items.lantern.black_0",
)),
glider: None,
head: None,
tabard: None,
},
)),
back: None,
ring: None,
neck: None,
lantern: None,
glider: None,
head: None,
tabard: None,
},
Alignment::Enemy => Loadout {
active_item,
second_item: None,
shoulder: Some(Item::new_from_asset_expect(
"common.items.armor.shoulder.cultist_shoulder_purple",
)),
chest: Some(Item::new_from_asset_expect(
"common.items.armor.chest.cultist_chest_purple",
)),
belt: Some(Item::new_from_asset_expect(
"common.items.armor.belt.cultist_belt",
)),
hand: Some(Item::new_from_asset_expect(
"common.items.armor.hand.cultist_hands_purple",
)),
pants: Some(Item::new_from_asset_expect(
"common.items.armor.pants.cultist_legs_purple",
)),
foot: Some(Item::new_from_asset_expect(
"common.items.armor.foot.cultist_boots",
)),
back: Some(Item::new_from_asset_expect(
"common.items.armor.back.dungeon_purple-0",
)),
ring: None,
neck: None,
lantern: Some(Item::new_from_asset_expect("common.items.lantern.black_0")),
glider: None,
head: None,
tabard: None,
},
_ => LoadoutBuilder::animal(body).build(),
_ => LoadoutBuilder::animal(body).build(),
}
}
},
Body::Golem(golem) => match golem.species {
golem::Species::StoneGolem => Loadout {

View File

@ -0,0 +1,275 @@
use crate::{
comp::{Attacking, CharacterState, EnergySource, StateUpdate},
states::utils::{StageSection, *},
sys::character_behavior::{CharacterBehavior, JoinData},
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Stage {
/// 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,
/// 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: Duration,
/// Duration of stage spent in swing (controls animation stuff, and can also
/// be used to handle movement separately to buildup)
pub base_swing_duration: Duration,
/// Initial recover duration of stage (how long until character exits state)
pub base_recover_duration: Duration,
/// How much forward movement there is in the swing portion of the stage
pub forward_movement: f32,
}
#[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>,
/// 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,
}
/// 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);
let stage_index = (self.stage - 1) as usize;
// Allows for other states to interrupt this state
if self.static_data.is_interruptible && !data.inputs.primary.is_pressed() {
handle_interrupt(data, &mut update);
match update.character {
CharacterState::ComboMelee(_) => {},
_ => {
return update;
},
}
}
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(),
stage: self.stage,
combo: self.combo,
timer: self
.timer
.checked_add(Duration::from_secs_f32(
(1.0 + self.static_data.max_speed_increase
* (1.0
- self.static_data.speed_increase.powi(self.combo as i32)))
* data.dt.0,
))
.unwrap_or_default(),
stage_section: self.stage_section,
next_stage: self.next_stage,
});
} else {
// Transitions to swing section of stage
update.character = CharacterState::ComboMelee(Data {
static_data: self.static_data.clone(),
stage: self.stage,
combo: self.combo,
timer: Duration::default(),
stage_section: StageSection::Swing,
next_stage: self.next_stage,
});
// Hit attempt
data.updater.insert(data.entity, Attacking {
base_healthchange: -((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,
)) as i32),
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: 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
forward_move(
data,
&mut update,
0.3,
self.static_data.stage_data[stage_index].forward_movement,
);
// Swings
update.character = CharacterState::ComboMelee(Data {
static_data: self.static_data.clone(),
stage: self.stage,
combo: self.combo,
timer: self
.timer
.checked_add(Duration::from_secs_f32(
(1.0 + self.static_data.max_speed_increase
* (1.0
- self.static_data.speed_increase.powi(self.combo as i32)))
* data.dt.0,
))
.unwrap_or_default(),
stage_section: self.stage_section,
next_stage: self.next_stage,
});
} else {
// Transitions to recover section of stage
update.character = CharacterState::ComboMelee(Data {
static_data: self.static_data.clone(),
stage: self.stage,
combo: self.combo,
timer: Duration::default(),
stage_section: StageSection::Recover,
next_stage: self.next_stage,
});
}
},
StageSection::Recover => {
if self.timer < self.static_data.stage_data[stage_index].base_recover_duration {
// Recovers
if data.inputs.primary.is_pressed() {
// Checks if state will transition to next stage after recover
update.character = CharacterState::ComboMelee(Data {
static_data: self.static_data.clone(),
stage: self.stage,
combo: self.combo,
timer: self
.timer
.checked_add(Duration::from_secs_f32(
(1.0 + self.static_data.max_speed_increase
* (1.0
- self
.static_data
.speed_increase
.powi(self.combo as i32)))
* data.dt.0,
))
.unwrap_or_default(),
stage_section: self.stage_section,
next_stage: true,
});
} else {
update.character = CharacterState::ComboMelee(Data {
static_data: self.static_data.clone(),
stage: self.stage,
combo: self.combo,
timer: self
.timer
.checked_add(Duration::from_secs_f32(
(1.0 + self.static_data.max_speed_increase
* (1.0
- self
.static_data
.speed_increase
.powi(self.combo as i32)))
* data.dt.0,
))
.unwrap_or_default(),
stage_section: self.stage_section,
next_stage: self.next_stage,
});
}
} 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,
combo: self.combo,
timer: Duration::default(),
stage_section: StageSection::Buildup,
next_stage: false,
});
} 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(energy, EnergySource::HitEnemy);
}
}
update
}
}

View File

@ -1,93 +1,256 @@
use crate::{
comp::{Attacking, CharacterState, StateUpdate},
states::utils::*,
comp::{Attacking, CharacterState, EnergySource, StateUpdate},
states::utils::{StageSection, *},
sys::character_behavior::*,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use vek::Vec3;
const DASH_SPEED: f32 = 19.0;
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct Data {
/// Separated out to condense update portions of character state
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct StaticData {
/// How much damage the attack initially does
pub base_damage: u32,
/// How much damage the attack does at max charge distance
pub max_damage: u32,
/// How much the attack knocks the target back initially
pub base_knockback: f32,
/// How much knockback happens at max charge distance
pub max_knockback: f32,
/// Range of the attack
pub range: f32,
/// Angle of the attack
pub angle: f32,
/// Rate of energy drain
pub energy_drain: u32,
/// How quickly dasher moves forward
pub forward_speed: f32,
/// Whether state keeps charging after reaching max charge duration
pub infinite_charge: bool,
/// How long until state should deal damage
pub buildup_duration: Duration,
/// How long the state charges for until it reaches max damage
pub charge_duration: Duration,
/// Suration of state spent in swing
pub swing_duration: Duration,
/// How long the state has until exiting
pub recover_duration: Duration,
/// Base damage
pub base_damage: u32,
/// Whether the attack can deal more damage
/// Whether the state can be interrupted by other abilities
pub is_interruptible: bool,
}
#[derive(Copy, 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,
/// Whether the charge should end
pub end_charge: bool,
/// Timer for each stage
pub timer: Duration,
/// What section the character stage is in
pub stage_section: StageSection,
/// Whether the state should attempt attacking again
pub exhausted: bool,
pub initialize: bool,
}
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData) -> StateUpdate {
let mut update = StateUpdate::from(data);
if self.initialize {
update.vel.0 = *data.inputs.look_dir * 20.0;
if let Some(dir) = Vec3::from(data.inputs.look_dir.xy()).try_normalized() {
update.ori.0 = dir.into();
handle_orientation(data, &mut update, 1.0);
handle_move(data, &mut update, 0.1);
// Allows for other states to interrupt this state
if self.static_data.is_interruptible && !data.inputs.secondary.is_pressed() {
handle_interrupt(data, &mut update);
match update.character {
CharacterState::DashMelee(_) => {},
_ => {
return update;
},
}
}
if self.buildup_duration != Duration::default() && data.physics.touch_entities.is_empty() {
// Build up (this will move you forward)
update.vel.0 = Vec3::new(0.0, 0.0, update.vel.0.z)
+ (update.vel.0 * Vec3::new(1.0, 1.0, 0.0)
+ 1.5 * data.inputs.move_dir.try_normalized().unwrap_or_default())
.try_normalized()
.unwrap_or_default()
* DASH_SPEED;
match self.stage_section {
StageSection::Buildup => {
if self.timer < self.static_data.buildup_duration {
// Build up
update.character = CharacterState::DashMelee(Data {
static_data: self.static_data,
end_charge: self.end_charge,
timer: self
.timer
.checked_add(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
stage_section: self.stage_section,
exhausted: self.exhausted,
});
} else {
// Transitions to charge section of stage
update.character = CharacterState::DashMelee(Data {
static_data: self.static_data,
end_charge: self.end_charge,
timer: Duration::default(),
stage_section: StageSection::Charge,
exhausted: self.exhausted,
});
}
},
StageSection::Charge => {
if (self.timer < self.static_data.charge_duration
|| (self.static_data.infinite_charge && data.inputs.secondary.is_pressed()))
&& update.energy.current() > 0
&& !self.end_charge
{
// Forward movement
forward_move(data, &mut update, 0.1, self.static_data.forward_speed);
update.character = CharacterState::DashMelee(Data {
buildup_duration: self
.buildup_duration
.checked_sub(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
recover_duration: self.recover_duration,
base_damage: self.base_damage,
exhausted: false,
initialize: false,
});
} else if !self.exhausted {
// Hit attempt
data.updater.insert(data.entity, Attacking {
base_healthchange: -(self.base_damage as i32),
range: 3.5,
max_angle: 45_f32.to_radians(),
applied: false,
hit_count: 0,
knockback: 0.0,
});
// Hit attempt (also checks if player is moving)
if !self.exhausted && update.vel.0.distance_squared(Vec3::zero()) > 1.0 {
let charge_frac = (self.timer.as_secs_f32()
/ self.static_data.charge_duration.as_secs_f32())
.min(1.0);
let damage = (self.static_data.max_damage as f32
- self.static_data.base_damage as f32)
* charge_frac
+ self.static_data.base_damage as f32;
let knockback = (self.static_data.max_knockback
- self.static_data.base_knockback)
* charge_frac
+ self.static_data.base_knockback;
data.updater.insert(data.entity, Attacking {
base_healthchange: -damage as i32,
range: self.static_data.range,
max_angle: self.static_data.angle.to_radians(),
applied: false,
hit_count: 0,
knockback,
});
}
update.character = CharacterState::DashMelee(Data {
buildup_duration: Duration::default(),
recover_duration: self.recover_duration,
base_damage: self.base_damage,
exhausted: true,
initialize: false,
});
} else if self.recover_duration != Duration::default() {
// Recovery
handle_move(data, &mut update, 0.7);
update.character = CharacterState::DashMelee(Data {
buildup_duration: self.buildup_duration,
recover_duration: self
.recover_duration
.checked_sub(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
base_damage: self.base_damage,
exhausted: true,
initialize: false,
});
} else {
// Done
update.character = CharacterState::Wielding;
// Make sure attack component is removed
data.updater.remove::<Attacking>(data.entity);
// This logic basically just decides if a charge should end, and prevents the
// character state spamming attacks while checking if it has hit something
if !self.exhausted {
update.character = CharacterState::DashMelee(Data {
static_data: self.static_data,
end_charge: self.end_charge,
timer: self
.timer
.checked_add(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
stage_section: StageSection::Charge,
exhausted: true,
})
} else if let Some(attack) = data.attacking {
if attack.applied && attack.hit_count > 0 {
update.character = CharacterState::DashMelee(Data {
static_data: self.static_data,
end_charge: !self.static_data.infinite_charge,
timer: self
.timer
.checked_add(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
stage_section: StageSection::Charge,
exhausted: false,
})
} else if attack.applied {
update.character = CharacterState::DashMelee(Data {
static_data: self.static_data,
end_charge: self.end_charge,
timer: self
.timer
.checked_add(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
stage_section: StageSection::Charge,
exhausted: false,
})
} else {
update.character = CharacterState::DashMelee(Data {
static_data: self.static_data,
end_charge: !self.static_data.infinite_charge,
timer: self
.timer
.checked_add(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
stage_section: StageSection::Charge,
exhausted: self.exhausted,
})
}
} else {
update.character = CharacterState::DashMelee(Data {
static_data: self.static_data,
end_charge: !self.static_data.infinite_charge,
timer: self
.timer
.checked_add(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
stage_section: StageSection::Charge,
exhausted: self.exhausted,
})
}
// Consumes energy if there's enough left and charge has not stopped
update.energy.change_by(
-(self.static_data.energy_drain as f32 * data.dt.0) as i32,
EnergySource::Ability,
);
} else {
// Transitions to swing section of stage
update.character = CharacterState::DashMelee(Data {
static_data: self.static_data,
end_charge: self.end_charge,
timer: Duration::default(),
stage_section: StageSection::Swing,
exhausted: self.exhausted,
});
}
},
StageSection::Swing => {
if self.timer < self.static_data.swing_duration {
// Swings
update.character = CharacterState::DashMelee(Data {
static_data: self.static_data,
end_charge: self.end_charge,
timer: self
.timer
.checked_add(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
stage_section: self.stage_section,
exhausted: self.exhausted,
});
} else {
// Transitions to recover section of stage
update.character = CharacterState::DashMelee(Data {
static_data: self.static_data,
end_charge: self.end_charge,
timer: Duration::default(),
stage_section: StageSection::Recover,
exhausted: self.exhausted,
});
}
},
StageSection::Recover => {
if self.timer < self.static_data.recover_duration {
// Recover
update.character = CharacterState::DashMelee(Data {
static_data: self.static_data,
end_charge: self.end_charge,
timer: self
.timer
.checked_add(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
stage_section: self.stage_section,
exhausted: self.exhausted,
});
} else {
// Done
update.character = CharacterState::Wielding;
// Make sure attack component is removed
data.updater.remove::<Attacking>(data.entity);
}
},
}
update

View File

@ -4,6 +4,7 @@ pub mod basic_ranged;
pub mod boost;
pub mod charged_ranged;
pub mod climb;
pub mod combo_melee;
pub mod dance;
pub mod dash_melee;
pub mod equipping;
@ -16,6 +17,5 @@ pub mod roll;
pub mod sit;
pub mod sneak;
pub mod spin_melee;
pub mod triple_strike;
pub mod utils;
pub mod wielding;

View File

@ -1,136 +1,193 @@
use crate::{
comp::{Attacking, CharacterState, EnergySource, StateUpdate},
states::utils::{StageSection, *},
sys::character_behavior::*,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use vek::Vec3;
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct Data {
/// Separated out to condense update portions of character state
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct StaticData {
/// How long until the state attacks
pub buildup_duration: Duration,
/// Allows for buildup_duration to be reset to default value
pub buildup_duration_default: Duration,
/// How long the state is in the swing duration
pub swing_duration: Duration,
/// How long until state ends
pub recover_duration: Duration,
/// Allows for recover_duration to be reset to default value
pub recover_duration_default: Duration,
/// Base damage
pub base_damage: u32,
/// Whether the attack can deal more damage
pub exhausted: bool,
/// How many hits it can do before ending
pub hits_remaining: u32,
/// Allows for hits_remaining to be reset to default value
pub hits_remaining_default: u32,
/// Knockback
pub knockback: f32,
/// Range
pub range: f32,
/// Energy cost per attack
pub energy_cost: u32,
/// Whether spin state is infinite
pub is_infinite: bool,
/// Used to maintain classic axe spin physics
pub is_helicopter: bool,
/// Whether the state can be interrupted by other abilities
pub is_interruptible: bool,
/// Used for forced forward movement
pub forward_speed: f32,
/// Number of spins
pub num_spins: u32,
}
const MOVE_SPEED: f32 = 5.0;
#[derive(Copy, 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,
/// Timer for each stage
pub timer: Duration,
/// How many spins it can do before ending
pub spins_remaining: u32,
/// What section the character stage is in
pub stage_section: StageSection,
/// Whether the state can deal damage
pub exhausted: bool,
}
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData) -> StateUpdate {
let mut update = StateUpdate::from(data);
if self.buildup_duration != Duration::default() {
// Allows for moving
update.vel.0 =
Vec3::new(data.inputs.move_dir.x, data.inputs.move_dir.y, 0.0) * MOVE_SPEED;
if self.static_data.is_helicopter {
update.vel.0 = Vec3::new(data.inputs.move_dir.x, data.inputs.move_dir.y, 0.0) * 5.0;
}
update.character = CharacterState::SpinMelee(Data {
buildup_duration: self
.buildup_duration
.checked_sub(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
buildup_duration_default: self.buildup_duration_default,
recover_duration: self.recover_duration,
recover_duration_default: self.recover_duration_default,
base_damage: self.base_damage,
exhausted: self.exhausted,
hits_remaining: self.hits_remaining,
hits_remaining_default: self.hits_remaining_default,
energy_cost: self.energy_cost,
});
} else if !self.exhausted {
//Hit attempt
data.updater.insert(data.entity, Attacking {
base_healthchange: -(self.base_damage as i32),
range: 3.5,
max_angle: 360_f32.to_radians(),
applied: false,
hit_count: 0,
knockback: 0.0,
});
// Allows for other states to interrupt this state
if self.static_data.is_interruptible && !data.inputs.ability3.is_pressed() {
handle_interrupt(data, &mut update);
match update.character {
CharacterState::SpinMelee(_) => {},
_ => {
return update;
},
}
}
update.character = CharacterState::SpinMelee(Data {
buildup_duration: self.buildup_duration,
buildup_duration_default: self.buildup_duration_default,
recover_duration: self.recover_duration,
recover_duration_default: self.recover_duration_default,
base_damage: self.base_damage,
exhausted: true,
hits_remaining: self.hits_remaining - 1,
hits_remaining_default: self.hits_remaining_default,
energy_cost: self.energy_cost,
});
} else if self.recover_duration != Duration::default() {
// Allows for moving
update.vel.0 =
Vec3::new(data.inputs.move_dir.x, data.inputs.move_dir.y, 0.0) * MOVE_SPEED;
match self.stage_section {
StageSection::Buildup => {
if self.timer < self.static_data.buildup_duration {
// Build up
update.character = CharacterState::SpinMelee(Data {
static_data: self.static_data,
timer: self
.timer
.checked_add(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
spins_remaining: self.spins_remaining,
stage_section: self.stage_section,
exhausted: self.exhausted,
});
} else {
// Transitions to swing section of stage
update.character = CharacterState::SpinMelee(Data {
static_data: self.static_data,
timer: Duration::default(),
spins_remaining: self.spins_remaining,
stage_section: StageSection::Swing,
exhausted: self.exhausted,
});
}
},
StageSection::Swing => {
if !self.exhausted {
update.character = CharacterState::SpinMelee(Data {
static_data: self.static_data,
timer: Duration::default(),
spins_remaining: self.spins_remaining,
stage_section: self.stage_section,
exhausted: true,
});
// Hit attempt
data.updater.insert(data.entity, Attacking {
base_healthchange: -(self.static_data.base_damage as i32),
range: self.static_data.range,
max_angle: 180_f32.to_radians(),
applied: false,
hit_count: 0,
knockback: self.static_data.knockback,
});
} else if self.timer < self.static_data.swing_duration {
if !self.static_data.is_helicopter {
forward_move(data, &mut update, 0.1, self.static_data.forward_speed);
handle_orientation(data, &mut update, 1.0);
}
update.character = CharacterState::SpinMelee(Data {
buildup_duration: self.buildup_duration,
buildup_duration_default: self.buildup_duration_default,
recover_duration: self
.recover_duration
.checked_sub(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
recover_duration_default: self.recover_duration_default,
base_damage: self.base_damage,
exhausted: self.exhausted,
hits_remaining: self.hits_remaining,
hits_remaining_default: self.hits_remaining_default,
energy_cost: self.energy_cost,
});
} else if self.hits_remaining != 0 {
// Allows for one ability usage to have multiple hits
// This isn't needed for it's continuous implementation, but is left in should
// this skill be moved to the skillbar
update.character = CharacterState::SpinMelee(Data {
buildup_duration: self.buildup_duration_default,
buildup_duration_default: self.buildup_duration_default,
recover_duration: self.recover_duration_default,
recover_duration_default: self.recover_duration_default,
base_damage: self.base_damage,
exhausted: false,
hits_remaining: self.hits_remaining,
hits_remaining_default: self.hits_remaining_default,
energy_cost: self.energy_cost,
});
} else if update.energy.current() >= self.energy_cost && data.inputs.secondary.is_pressed()
{
update.character = CharacterState::SpinMelee(Data {
buildup_duration: self.buildup_duration_default,
buildup_duration_default: self.buildup_duration_default,
recover_duration: self.recover_duration_default,
recover_duration_default: self.recover_duration_default,
base_damage: self.base_damage,
exhausted: false,
hits_remaining: self.hits_remaining_default,
hits_remaining_default: self.hits_remaining_default,
energy_cost: self.energy_cost,
});
// Consumes energy if there's enough left and RMB is held down
update
.energy
.change_by(-(self.energy_cost as i32), EnergySource::Ability);
} else {
// Done
update.character = CharacterState::Wielding;
// Make sure attack component is removed
data.updater.remove::<Attacking>(data.entity);
// Swings
update.character = CharacterState::SpinMelee(Data {
static_data: self.static_data,
timer: self
.timer
.checked_add(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
spins_remaining: self.spins_remaining,
stage_section: self.stage_section,
exhausted: self.exhausted,
});
} else if update.energy.current() >= self.static_data.energy_cost
&& (self.spins_remaining != 0
|| (self.static_data.is_infinite && data.inputs.secondary.is_pressed()))
{
let new_spins_remaining = if self.static_data.is_infinite {
self.spins_remaining
} else {
self.spins_remaining - 1
};
update.character = CharacterState::SpinMelee(Data {
static_data: self.static_data,
timer: Duration::default(),
spins_remaining: new_spins_remaining,
stage_section: self.stage_section,
exhausted: false,
});
// Consumes energy if there's enough left and RMB is held down
update.energy.change_by(
-(self.static_data.energy_cost as i32),
EnergySource::Ability,
);
} else {
// Transitions to recover section of stage
update.character = CharacterState::SpinMelee(Data {
static_data: self.static_data,
timer: Duration::default(),
spins_remaining: self.spins_remaining,
stage_section: StageSection::Recover,
exhausted: self.exhausted,
});
}
},
StageSection::Recover => {
if self.timer < self.static_data.recover_duration {
// Recover
update.character = CharacterState::SpinMelee(Data {
static_data: self.static_data,
timer: self
.timer
.checked_add(Duration::from_secs_f32(data.dt.0))
.unwrap_or_default(),
spins_remaining: self.spins_remaining,
stage_section: self.stage_section,
exhausted: self.exhausted,
});
} 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);
},
}
update

View File

@ -1,220 +0,0 @@
use crate::{
comp::{Attacking, CharacterState, EnergySource, StateUpdate},
states::utils::*,
sys::character_behavior::{CharacterBehavior, JoinData},
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use vek::vec::Vec3;
use HoldingState::*;
use TimingState::*;
use TransitionStyle::*;
// In millis
const STAGE_DURATION: u64 = 700;
const TIMING_DELAY: u64 = 350;
const INITIAL_ACCEL: f32 = 90.0;
const BASE_SPEED: f32 = 25.0;
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub enum Stage {
First,
Second,
Third,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub enum TimingState {
NotPressed,
PressedEarly,
Success,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub enum HoldingState {
Holding,
Released,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub enum TransitionStyle {
/// Player must time a button press properly to transition
Timed(TimingState),
/// Player must hold button for whole move
Hold(HoldingState),
}
/// ### A sequence of 3 incrementally increasing attacks.
///
/// While holding down the `primary` button, perform a series of 3 attacks,
/// each one pushes the player forward as the character steps into the swings.
/// The player can let go of the left mouse button at any time
/// and stop their attacks by interrupting the attack animation.
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)]
pub struct Data {
/// The tool this state will read to handle damage, etc.
pub base_damage: u32,
/// What stage (of 3) the attack is in
pub stage: Stage,
/// How long current stage has been active
pub stage_time_active: Duration,
/// Whether current stage has exhausted its attack
pub stage_exhausted: bool,
/// Whether state has performed initialization logic
pub initialized: bool,
/// What this instance's current transition stat is
pub transition_style: TransitionStyle,
}
impl CharacterBehavior for Data {
fn behavior(&self, data: &JoinData) -> StateUpdate {
let mut update = StateUpdate::from(data);
handle_move(data, &mut update, 0.3);
#[allow(clippy::or_fun_call)] // TODO: Pending review in #587
let stage_time_active = self
.stage_time_active
.checked_add(Duration::from_secs_f32(data.dt.0))
.unwrap_or(Duration::default());
if !self.initialized {
update.vel.0 = Vec3::zero();
if let Some(dir) = data.inputs.look_dir.try_normalized() {
update.ori.0 = dir.into();
}
}
let initialized = true;
// Update transition
let transition_style = match self.transition_style {
Timed(state) => match state {
NotPressed => {
if data.inputs.primary.is_just_pressed() {
if stage_time_active > Duration::from_millis(TIMING_DELAY) {
Timed(Success)
} else {
Timed(PressedEarly)
}
} else {
self.transition_style
}
},
_ => self.transition_style,
},
Hold(_) => {
if !data.inputs.primary.is_pressed() {
Hold(Released)
} else {
self.transition_style
}
},
};
// Handling movement
if stage_time_active < Duration::from_millis(STAGE_DURATION / 3) {
let adjusted_accel = match (self.stage, data.physics.touch_entities.is_empty()) {
(Stage::First, true) => INITIAL_ACCEL,
(Stage::Second, true) => INITIAL_ACCEL * 0.75,
(Stage::Third, true) => INITIAL_ACCEL * 0.75,
(_, _) => 0.0,
};
// Move player forward while in first third of each stage
if update.vel.0.magnitude_squared() < BASE_SPEED.powf(2.0) {
update.vel.0 += data.dt.0 * (adjusted_accel * Vec3::from(data.ori.0.xy()));
let mag2 = update.vel.0.magnitude_squared();
if mag2 > BASE_SPEED.powf(2.0) {
update.vel.0 = update.vel.0.normalized() * BASE_SPEED;
}
};
} else {
handle_orientation(data, &mut update, 50.0);
}
// Handling attacking
update.character = if stage_time_active > Duration::from_millis(STAGE_DURATION / 2)
&& !self.stage_exhausted
{
let dmg = match self.stage {
Stage::First => self.base_damage / 2,
Stage::Second => self.base_damage,
Stage::Third => (self.base_damage as f32 * 1.5) as u32,
};
update.vel.0 = Vec3::new(data.inputs.move_dir.x, data.inputs.move_dir.y, 0.0) * 5.0;
// Try to deal damage in second half of stage
data.updater.insert(data.entity, Attacking {
base_healthchange: -(dmg as i32),
range: 3.5,
max_angle: 45_f32.to_radians(),
applied: false,
hit_count: 0,
knockback: 10.0,
});
CharacterState::TripleStrike(Data {
base_damage: self.base_damage,
stage: self.stage,
stage_time_active,
stage_exhausted: true,
initialized,
transition_style,
})
} else if stage_time_active > Duration::from_millis(STAGE_DURATION) {
let next_stage =
// Determine whether stage can transition based on TransitionStyle
if let Hold(Holding) | Timed(Success) = transition_style {
// Determine what stage to transition to
match self.stage {
Stage::First => Some(Stage::Second),
Stage::Second => Some(Stage::Third),
Stage::Third => None,
}
}
// Player messed up inputs, don't transition
else { None };
update.vel.0 = Vec3::new(data.inputs.move_dir.x, data.inputs.move_dir.y, 0.0) * 5.0;
if let Some(stage) = next_stage {
CharacterState::TripleStrike(Data {
base_damage: self.base_damage,
stage,
stage_time_active: Duration::default(),
stage_exhausted: false,
initialized,
transition_style: match transition_style {
Hold(_) => Hold(Holding),
Timed(_) => Timed(NotPressed),
},
})
} else {
// Make sure attack component is removed
data.updater.remove::<Attacking>(data.entity);
// Done
CharacterState::Wielding
}
} else {
CharacterState::TripleStrike(Data {
base_damage: self.base_damage,
stage: self.stage,
stage_time_active,
stage_exhausted: self.stage_exhausted,
initialized,
transition_style,
})
};
// Grant energy on successful hit
if let Some(attack) = data.attacking {
if attack.applied && attack.hit_count > 0 {
data.updater.remove::<Attacking>(data.entity);
update.energy.change_by(50, EnergySource::HitEnemy);
}
}
update
}
}

View File

@ -8,6 +8,7 @@ use crate::{
sys::{character_behavior::JoinData, phys::GRAVITY},
util::Dir,
};
use serde::{Deserialize, Serialize};
use vek::*;
pub const MOVEMENT_THRESHOLD_VEL: f32 = 3.0;
@ -89,6 +90,21 @@ fn basic_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32) {
handle_orientation(data, update, data.body.base_ori_rate());
}
/// Similar to basic_move function, but with forced forward movement
pub fn forward_move(data: &JoinData, update: &mut StateUpdate, efficiency: f32, forward: f32) {
let accel = if data.physics.on_ground {
data.body.base_accel()
} else {
BASE_HUMANOID_AIR_ACCEL
};
update.vel.0 += Vec2::broadcast(data.dt.0)
* accel
* (data.inputs.move_dir * efficiency + (*update.ori.0).xy() * forward);
handle_orientation(data, update, data.body.base_ori_rate() * efficiency);
}
pub fn handle_orientation(data: &JoinData, update: &mut StateUpdate, rate: f32) {
// Set direction based on move direction
let ori_dir = if update.character.is_attack() | update.character.is_block() {
@ -330,3 +346,21 @@ pub fn unwrap_tool_data<'a>(data: &'a JoinData) -> Option<&'a Tool> {
None
}
}
pub fn handle_interrupt(data: &JoinData, update: &mut StateUpdate) {
handle_ability1_input(data, update);
handle_ability2_input(data, update);
handle_ability3_input(data, update);
handle_dodge_input(data, update);
}
/// Determines what portion a state is in. Used in all attacks (eventually). Is
/// used to control aspects of animation code, as well as logic within the
/// character states.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum StageSection {
Buildup,
Swing,
Recover,
Charge,
}

View File

@ -250,7 +250,7 @@ impl<'a> System<'a> for Sys {
CharacterState::Roll(data) => data.handle_event(&j, action),
CharacterState::Wielding => states::wielding::Data.handle_event(&j, action),
CharacterState::Equipping(data) => data.handle_event(&j, action),
CharacterState::TripleStrike(data) => data.handle_event(&j, action),
CharacterState::ComboMelee(data) => data.handle_event(&j, action),
CharacterState::BasicMelee(data) => data.handle_event(&j, action),
CharacterState::BasicRanged(data) => data.handle_event(&j, action),
CharacterState::Boost(data) => data.handle_event(&j, action),
@ -279,7 +279,7 @@ impl<'a> System<'a> for Sys {
CharacterState::Roll(data) => data.behavior(&j),
CharacterState::Wielding => states::wielding::Data.behavior(&j),
CharacterState::Equipping(data) => data.behavior(&j),
CharacterState::TripleStrike(data) => data.behavior(&j),
CharacterState::ComboMelee(data) => data.behavior(&j),
CharacterState::BasicMelee(data) => data.behavior(&j),
CharacterState::BasicRanged(data) => data.behavior(&j),
CharacterState::Boost(data) => data.behavior(&j),

View File

@ -153,10 +153,11 @@ impl<'a> System<'a> for Sys {
});
}
if attack.knockback != 0.0 && damage.healthchange != 0.0 {
let kb_dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0));
server_emitter.emit(ServerEvent::Knockback {
entity: b,
impulse: attack.knockback
* *Dir::slerp(ori.0, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5),
* *Dir::slerp(kb_dir, Dir::new(Vec3::new(0.0, 0.0, 1.0)), 0.5),
});
}
attack.hit_count += 1;

View File

@ -156,6 +156,9 @@ impl<'a> System<'a> for Sys {
let z_limits = collider.map(|c| c.get_z_limits()).unwrap_or((-0.5, 0.5));
let mass = mass.map(|m| m.0).unwrap_or(scale);
// Resets touch_entities in physics
physics.touch_entities.clear();
// Group to ignore collisions with
let ignore_group = projectile
.filter(|p| p.ignore_group)

View File

@ -110,7 +110,7 @@ impl<'a> System<'a> for Sys {
| CharacterState::DashMelee { .. }
| CharacterState::LeapMelee { .. }
| CharacterState::SpinMelee { .. }
| CharacterState::TripleStrike { .. }
| CharacterState::ComboMelee { .. }
| CharacterState::BasicRanged { .. }
| CharacterState::ChargedRanged { .. }
| CharacterState::GroundShockwave { .. } => {

View File

@ -621,7 +621,8 @@ fn handle_spawn(
.create_npc(
pos,
comp::Stats::new(get_npc_name(id).into(), body),
LoadoutBuilder::build_loadout(body, alignment, None).build(),
LoadoutBuilder::build_loadout(body, alignment, None, false)
.build(),
body,
)
.with(comp::Vel(vel))

View File

@ -1,7 +1,7 @@
use super::SysTimer;
use crate::{chunk_generator::ChunkGenerator, client::Client, Tick};
use common::{
comp::{self, bird_medium, Alignment, CharacterAbility, Player, Pos},
comp::{self, bird_medium, Alignment, Player, Pos},
event::{EventBus, ServerEvent},
generation::get_npc_name,
msg::ServerMsg,
@ -13,7 +13,7 @@ use common::{
};
use rand::Rng;
use specs::{Join, Read, ReadStorage, System, Write, WriteExpect, WriteStorage};
use std::{sync::Arc, time::Duration};
use std::sync::Arc;
use vek::*;
/// This system will handle loading generated chunks and unloading
@ -120,7 +120,9 @@ impl<'a> System<'a> for Sys {
// let damage = stats.level.level() as i32; TODO: Make NPC base damage
// non-linearly depend on their level
let mut loadout = LoadoutBuilder::build_loadout(body, alignment, main_tool).build();
let loadout =
LoadoutBuilder::build_loadout(body, alignment, main_tool, entity.is_giant)
.build();
let mut scale = entity.scale;
@ -151,53 +153,6 @@ impl<'a> System<'a> for Sys {
body,
);
}
loadout = comp::Loadout {
active_item: Some(comp::ItemConfig {
item: comp::Item::new_from_asset_expect(
"common.items.npc_weapons.sword.zweihander_sword_0",
),
ability1: Some(CharacterAbility::BasicMelee {
energy_cost: 0,
buildup_duration: Duration::from_millis(800),
recover_duration: Duration::from_millis(200),
base_healthchange: -100,
knockback: 0.0,
range: 3.5,
max_angle: 60.0,
}),
ability2: None,
ability3: None,
block_ability: None,
dodge_ability: None,
}),
second_item: None,
shoulder: Some(comp::Item::new_from_asset_expect(
"common.items.armor.shoulder.plate_0",
)),
chest: Some(comp::Item::new_from_asset_expect(
"common.items.armor.chest.plate_green_0",
)),
belt: Some(comp::Item::new_from_asset_expect(
"common.items.armor.belt.plate_0",
)),
hand: Some(comp::Item::new_from_asset_expect(
"common.items.armor.hand.plate_0",
)),
pants: Some(comp::Item::new_from_asset_expect(
"common.items.armor.pants.plate_green_0",
)),
foot: Some(comp::Item::new_from_asset_expect(
"common.items.armor.foot.plate_0",
)),
back: None,
ring: None,
neck: None,
lantern: None,
glider: None,
head: None,
tabard: None,
};
stats.level.set_level(rand::thread_rng().gen_range(30, 35));
scale = 2.0 + rand::random::<f32>();
}

View File

@ -8,7 +8,7 @@ version = "0.7.0"
name = "voxygen_anim"
# Uncomment to use animation hot reloading
# Note: this breaks `cargo test`
# crate-type = ["lib", "cdylib"]
crate-type = ["lib", "cdylib"]
[features]
be-dyn-lib = []

View File

@ -2,13 +2,22 @@ use super::{
super::{vek::*, Animation},
CharacterSkeleton, SkeletonAttr,
};
use common::comp::item::{Hands, ToolKind};
use common::{
comp::item::{Hands, ToolKind},
states::utils::StageSection,
};
use std::f32::consts::PI;
pub struct AlphaAnimation;
impl Animation for AlphaAnimation {
type Dependency = (Option<ToolKind>, Option<ToolKind>, f32, f64);
type Dependency = (
Option<ToolKind>,
Option<ToolKind>,
f32,
f64,
Option<StageSection>,
);
type Skeleton = CharacterSkeleton;
#[cfg(feature = "use-dyn-lib")]
@ -18,7 +27,7 @@ impl Animation for AlphaAnimation {
#[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): Self::Dependency,
(active_tool_kind, second_tool_kind, velocity, _global_time, stage_section): Self::Dependency,
anim_time: f64,
rate: &mut f32,
skeleton_attr: &SkeletonAttr,
@ -61,74 +70,77 @@ impl Animation for AlphaAnimation {
.sqrt())
* ((anim_time as f32 * lab as f32 * 4.0).sin());
let movement = anim_time as f32 * 1.0;
let test = (anim_time as f32 * 1.75).sin();
if let Some(ToolKind::Sword(_)) = active_tool_kind {
next.l_hand.position = Vec3::new(-0.75, -1.0, 2.5);
next.l_hand.orientation = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.2);
next.l_hand.scale = Vec3::one() * 1.04;
next.r_hand.position = Vec3::new(0.75, -1.5, -0.5);
next.r_hand.orientation = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(0.3);
next.r_hand.scale = Vec3::one() * 1.05;
next.main.position = Vec3::new(0.0, 0.0, 2.0);
next.main.orientation = Quaternion::rotation_x(-0.1)
* Quaternion::rotation_y(0.0)
* Quaternion::rotation_z(0.0);
next.head.position = Vec3::new(0.0, skeleton_attr.head.0 + 0.0, skeleton_attr.head.1);
if let Some(stage_section) = stage_section {
match stage_section {
StageSection::Buildup => {
//println!("{:.3} build", anim_time);
next.control.position =
Vec3::new(-7.0, 7.0 + movement * -4.0, 2.0 + movement * 1.0);
next.control.orientation = Quaternion::rotation_x(movement * -0.5)
* Quaternion::rotation_y(movement * -1.0)
* Quaternion::rotation_z(movement * -1.2);
next.chest.orientation = Quaternion::rotation_z(movement * 1.5);
next.head.orientation = Quaternion::rotation_z(movement * -0.9);
},
StageSection::Swing => {
//println!("{:.3} swing", anim_time);
next.control.position = Vec3::new(-7.0, 3.0 + movement * 16.0, 3.0);
next.control.orientation =
Quaternion::rotation_x(-0.5 + movement * -1.0 * 0.0)
* Quaternion::rotation_y(-1.0 + movement * -0.6)
* Quaternion::rotation_z(-1.2 + movement * 1.3);
next.chest.orientation = Quaternion::rotation_z(1.5 + test * -3.0);
next.head.orientation = Quaternion::rotation_z(-0.9 + test * 2.5);
//next.head.orientation = Quaternion::rotation_z(-test
// * 0.8); next.chest.
// orientation = Quaternion::rotation_x(test * 0.15)
//* Quaternion::rotation_y(movement * 0.3)
//* Quaternion::rotation_z(movement * 1.5);
//next.belt.orientation = Quaternion::rotation_z(test2
// * 0.5); next.shorts.
// orientation = Quaternion::rotation_z(test2 * 1.5);
// next.torso.orientation = Quaternion::rotation_z(test2
// * 7.2);
},
StageSection::Recover => {
//println!("{:.3} recover", anim_time);
next.control.position = Vec3::new(-7.0, 15.0, 2.0);
next.control.orientation = Quaternion::rotation_x(-0.5)
* Quaternion::rotation_y(-1.57 + movement * 1.0)
* Quaternion::rotation_z(0.0);
next.control.scale = Vec3::one();
next.chest.orientation = Quaternion::rotation_y(0.0)
* Quaternion::rotation_z(-1.57 + movement * 0.5);
next.head.orientation = Quaternion::rotation_y(0.0)
* Quaternion::rotation_z(1.57 + movement * -0.5);
},
_ => {},
}
}
}
match active_tool_kind {
//TODO: Inventory
Some(ToolKind::Sword(_)) => {
next.head.position =
Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1);
next.head.orientation = Quaternion::rotation_z(slow * -0.25)
* Quaternion::rotation_x(0.0 + slow * 0.15)
* Quaternion::rotation_y(slow * -0.15);
next.head.scale = Vec3::one() * skeleton_attr.head_scale;
next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1);
next.chest.orientation = Quaternion::rotation_z(slow * 0.4)
* Quaternion::rotation_x(0.0 + slow * -0.2)
* Quaternion::rotation_y(slow * 0.2);
next.chest.scale = Vec3::one();
next.belt.position = Vec3::new(0.0, skeleton_attr.belt.0, skeleton_attr.belt.1);
next.belt.orientation = next.chest.orientation * -0.3;
next.shorts.position =
Vec3::new(0.0, skeleton_attr.shorts.0, skeleton_attr.shorts.1);
next.shorts.orientation = next.chest.orientation * -0.45;
next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5);
next.l_hand.orientation = Quaternion::rotation_x(1.27);
next.l_hand.scale = Vec3::one() * 1.05;
next.r_hand.position = Vec3::new(0.75, -1.5, -5.5);
next.r_hand.orientation = Quaternion::rotation_x(1.27);
next.r_hand.scale = Vec3::one() * 1.05;
next.main.position = Vec3::new(0.0, 0.0, 0.0);
next.main.orientation = Quaternion::rotation_x(-0.3)
* Quaternion::rotation_y(0.0)
* Quaternion::rotation_z(0.0);
next.control.position = Vec3::new(-10.0 + push * 5.0, 6.0 + push * 5.0, 2.0);
next.control.orientation = Quaternion::rotation_x(-1.4 + slow * 0.4)
* Quaternion::rotation_y(slow * -1.3)
* Quaternion::rotation_z(1.4 + slow * -0.5);
next.control.scale = Vec3::one();
next.l_foot.position = Vec3::new(
-skeleton_attr.foot.0,
slow * -3.0 + quick * 3.0 - 4.0,
skeleton_attr.foot.2,
);
next.l_foot.orientation = Quaternion::rotation_x(slow * 0.6)
* Quaternion::rotation_y((slow * -0.2).max(0.0));
next.l_foot.scale = Vec3::one();
next.r_foot.position = Vec3::new(
skeleton_attr.foot.0,
slow * 3.0 + quick * -3.0 + 5.0,
skeleton_attr.foot.2,
);
next.r_foot.orientation = Quaternion::rotation_x(slow * -0.6)
* Quaternion::rotation_y((slow * 0.2).min(0.0));
next.r_foot.scale = Vec3::one();
next.lantern.orientation =
Quaternion::rotation_x(slow * -0.7 + 0.4) * Quaternion::rotation_y(slow * 0.4);
next.hold.scale = Vec3::one() * 0.0;
next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler;
next.torso.orientation = Quaternion::rotation_z(0.0)
* Quaternion::rotation_x(0.0)
* Quaternion::rotation_y(0.0);
next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler;
},
Some(ToolKind::Dagger(_)) => {
next.head.position =
Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1);

View File

@ -2,12 +2,21 @@ use super::{
super::{vek::*, Animation},
CharacterSkeleton, SkeletonAttr,
};
use common::comp::item::{Hands, ToolKind};
use common::{
comp::item::{Hands, ToolKind},
states::utils::StageSection,
};
pub struct BetaAnimation;
impl Animation for BetaAnimation {
type Dependency = (Option<ToolKind>, Option<ToolKind>, f32, f64);
type Dependency = (
Option<ToolKind>,
Option<ToolKind>,
f32,
f64,
Option<StageSection>,
);
type Skeleton = CharacterSkeleton;
#[cfg(feature = "use-dyn-lib")]
@ -16,7 +25,7 @@ impl Animation for BetaAnimation {
#[cfg_attr(feature = "be-dyn-lib", export_name = "character_beta")]
fn update_skeleton_inner(
skeleton: &Self::Skeleton,
(active_tool_kind, second_tool_kind, _velocity, _global_time): Self::Dependency,
(active_tool_kind, second_tool_kind, _velocity, _global_time, stage_section): Self::Dependency,
anim_time: f64,
rate: &mut f32,
skeleton_attr: &SkeletonAttr,
@ -43,60 +52,175 @@ impl Animation for BetaAnimation {
.sqrt())
* ((anim_time as f32 * lab as f32 * 14.0).sin());
if let Some(
ToolKind::Axe(_) | ToolKind::Hammer(_) | ToolKind::Sword(_) | ToolKind::Dagger(_),
) = active_tool_kind
{
//INTENTION: SWORD
next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1);
next.head.orientation = Quaternion::rotation_z(slow * -0.18)
* Quaternion::rotation_x(-0.1 + slow * -0.28)
* Quaternion::rotation_y(0.2 + slow * 0.18);
next.head.scale = Vec3::one() * skeleton_attr.head_scale;
let recover = (anim_time as f32 * 8.0).sin();
next.chest.position = Vec3::new(0.0 + foot * 2.0, 0.0, 7.0);
next.chest.orientation = Quaternion::rotation_z(slow * 0.2)
* Quaternion::rotation_x(slow * 0.2)
* Quaternion::rotation_y(slow * -0.1);
let movement = anim_time as f32 * 1.0;
let stab = (anim_time as f32 * 2.5).sin();
next.belt.position = Vec3::new(0.0, 0.0, -2.0);
next.belt.orientation = Quaternion::rotation_z(slow * 0.1)
* Quaternion::rotation_x(slow * 0.1)
* Quaternion::rotation_y(slow * -0.04);
next.shorts.position = Vec3::new(0.0, 0.0, -5.0);
next.shorts.orientation = Quaternion::rotation_z(slow * 0.1)
* Quaternion::rotation_x(slow * 0.1)
* Quaternion::rotation_y(slow * -0.05);
next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5);
next.l_hand.orientation = Quaternion::rotation_x(1.27);
if let Some(ToolKind::Sword(_)) = active_tool_kind {
next.l_hand.position = Vec3::new(-0.75, -1.0, 2.5);
next.l_hand.orientation = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.2);
next.l_hand.scale = Vec3::one() * 1.04;
next.r_hand.position = Vec3::new(0.75, -1.5, -5.5);
next.r_hand.orientation = Quaternion::rotation_x(1.27);
next.r_hand.position = Vec3::new(0.75, -1.5, -0.5);
next.r_hand.orientation = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(0.3);
next.r_hand.scale = Vec3::one() * 1.05;
next.main.position = Vec3::new(0.0, 6.0, -1.0);
next.main.orientation = Quaternion::rotation_x(-0.3);
next.main.position = Vec3::new(0.0, 0.0, 2.0);
next.main.orientation = Quaternion::rotation_x(-0.1)
* Quaternion::rotation_y(0.0)
* Quaternion::rotation_z(0.0);
next.control.position = Vec3::new(-8.0 + slow * 1.5, 1.5 + slow * 1.0, 0.0);
next.control.orientation = Quaternion::rotation_x(-1.4)
* Quaternion::rotation_y(slow * 2.0 + 0.7)
* Quaternion::rotation_z(1.7 - slow * 0.4 + fast * 0.6);
next.control.scale = Vec3::one();
next.l_foot.position = Vec3::new(
-skeleton_attr.foot.0,
footquick * -9.5,
skeleton_attr.foot.2,
);
next.l_foot.orientation =
Quaternion::rotation_x(footquick * 0.3) * Quaternion::rotation_y(footquick * -0.6);
next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1);
next.r_foot.position =
Vec3::new(skeleton_attr.foot.0, footquick * 9.5, skeleton_attr.foot.2);
next.r_foot.orientation =
Quaternion::rotation_x(footquick * -0.3) * Quaternion::rotation_y(footquick * 0.2);
next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler;
next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler;
if let Some(stage_section) = stage_section {
match stage_section {
StageSection::Buildup => {
//println!("{:.3} recover", anim_time);
next.control.position = Vec3::new(
-8.0 + movement * -5.0,
1.0 - recover * 0.8 + movement * 2.0,
2.0 - recover * 0.4,
);
next.control.orientation = Quaternion::rotation_x(-1.57)
* Quaternion::rotation_y(0.0 + movement * 1.5)
* Quaternion::rotation_z(1.0);
next.chest.orientation = Quaternion::rotation_y(-0.1)
* Quaternion::rotation_z(0.4 + movement * 1.5);
next.head.orientation = Quaternion::rotation_y(0.1)
* Quaternion::rotation_z(-0.1 + movement * -1.1);
},
StageSection::Swing => {
//println!("{:.3} swing", anim_time);
next.control.position =
Vec3::new(-8.0 + stab * 30.0, 6.0 + movement * 2.0, 6.0);
next.control.orientation = Quaternion::rotation_x(-1.57)
* Quaternion::rotation_y(1.5 + stab * 0.5)
* Quaternion::rotation_z(1.0 + stab * 1.0);
next.chest.orientation = Quaternion::rotation_y(-0.1)
* Quaternion::rotation_z(1.9 + stab * -0.5);
next.head.orientation = Quaternion::rotation_y(0.1)
* Quaternion::rotation_z(-1.2 + stab * -0.5);
},
StageSection::Recover => {
next.control.position = Vec3::new(10.0 + movement * -5.0, 8.0, 6.0);
next.control.orientation = Quaternion::rotation_x(-1.57)
* Quaternion::rotation_y(2.0)
* Quaternion::rotation_z(2.0);
next.chest.orientation = Quaternion::rotation_y(-0.1)
* Quaternion::rotation_z(1.4 + movement * 1.0);
next.head.orientation =
Quaternion::rotation_y(0.1) * Quaternion::rotation_z(-1.5);
},
_ => {},
}
}
}
match active_tool_kind {
Some(ToolKind::Hammer(_)) => {
next.head.position =
Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1);
next.head.orientation = Quaternion::rotation_z(slow * -0.18)
* Quaternion::rotation_x(-0.1 + slow * -0.28)
* Quaternion::rotation_y(0.2 + slow * 0.18);
next.head.scale = Vec3::one() * skeleton_attr.head_scale;
next.chest.position = Vec3::new(0.0 + foot * 2.0, 0.0, 7.0);
next.chest.orientation = Quaternion::rotation_z(slow * 0.2)
* Quaternion::rotation_x(slow * 0.2)
* Quaternion::rotation_y(slow * -0.1);
next.belt.position = Vec3::new(0.0, 0.0, -2.0);
next.belt.orientation = Quaternion::rotation_z(slow * 0.1)
* Quaternion::rotation_x(slow * 0.1)
* Quaternion::rotation_y(slow * -0.04);
next.shorts.position = Vec3::new(0.0, 0.0, -5.0);
next.shorts.orientation = Quaternion::rotation_z(slow * 0.1)
* Quaternion::rotation_x(slow * 0.1)
* Quaternion::rotation_y(slow * -0.05);
next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5);
next.l_hand.orientation = Quaternion::rotation_x(1.27);
next.l_hand.scale = Vec3::one() * 1.04;
next.r_hand.position = Vec3::new(0.75, -1.5, -5.5);
next.r_hand.orientation = Quaternion::rotation_x(1.27);
next.r_hand.scale = Vec3::one() * 1.05;
next.main.position = Vec3::new(0.0, 6.0, -1.0);
next.main.orientation = Quaternion::rotation_x(-0.3);
next.control.position = Vec3::new(-8.0 + slow * 1.5, 1.5 + slow * 1.0, 0.0);
next.control.orientation = Quaternion::rotation_x(-1.4)
* Quaternion::rotation_y(slow * 2.0 + 0.7)
* Quaternion::rotation_z(1.7 - slow * 0.4 + fast * 0.6);
next.control.scale = Vec3::one();
next.l_foot.position = Vec3::new(
-skeleton_attr.foot.0,
footquick * -9.5,
skeleton_attr.foot.2,
);
next.l_foot.orientation = Quaternion::rotation_x(footquick * 0.3)
* Quaternion::rotation_y(footquick * -0.6);
next.r_foot.position =
Vec3::new(skeleton_attr.foot.0, footquick * 9.5, skeleton_attr.foot.2);
next.r_foot.orientation = Quaternion::rotation_x(footquick * -0.3)
* Quaternion::rotation_y(footquick * 0.2);
next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler;
next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler;
},
Some(ToolKind::Axe(_)) => {
next.head.position =
Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1);
next.head.orientation = Quaternion::rotation_z(slow * -0.18)
* Quaternion::rotation_x(-0.1 + slow * -0.28)
* Quaternion::rotation_y(0.2 + slow * 0.18);
next.head.scale = Vec3::one() * skeleton_attr.head_scale;
next.chest.position = Vec3::new(0.0 + foot * 2.0, 0.0, 7.0);
next.chest.orientation = Quaternion::rotation_z(slow * 0.2)
* Quaternion::rotation_x(slow * 0.2)
* Quaternion::rotation_y(slow * -0.1);
next.belt.position = Vec3::new(0.0, 0.0, -2.0);
next.belt.orientation = Quaternion::rotation_z(slow * 0.1)
* Quaternion::rotation_x(slow * 0.1)
* Quaternion::rotation_y(slow * -0.04);
next.shorts.position = Vec3::new(0.0, 0.0, -5.0);
next.shorts.orientation = Quaternion::rotation_z(slow * 0.1)
* Quaternion::rotation_x(slow * 0.1)
* Quaternion::rotation_y(slow * -0.05);
next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5);
next.l_hand.orientation = Quaternion::rotation_x(1.27);
next.l_hand.scale = Vec3::one() * 1.04;
next.r_hand.position = Vec3::new(0.75, -1.5, -5.5);
next.r_hand.orientation = Quaternion::rotation_x(1.27);
next.r_hand.scale = Vec3::one() * 1.05;
next.main.position = Vec3::new(0.0, 6.0, -1.0);
next.main.orientation = Quaternion::rotation_x(-0.3);
next.control.position = Vec3::new(-8.0 + slow * 1.5, 1.5 + slow * 1.0, 0.0);
next.control.orientation = Quaternion::rotation_x(-1.4)
* Quaternion::rotation_y(slow * 2.0 + 0.7)
* Quaternion::rotation_z(1.7 - slow * 0.4 + fast * 0.6);
next.control.scale = Vec3::one();
next.l_foot.position = Vec3::new(
-skeleton_attr.foot.0,
footquick * -9.5,
skeleton_attr.foot.2,
);
next.l_foot.orientation = Quaternion::rotation_x(footquick * 0.3)
* Quaternion::rotation_y(footquick * -0.6);
next.r_foot.position =
Vec3::new(skeleton_attr.foot.0, footquick * 9.5, skeleton_attr.foot.2);
next.r_foot.orientation = Quaternion::rotation_x(footquick * -0.3)
* Quaternion::rotation_y(footquick * 0.2);
next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler;
next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler;
},
_ => {},
}
next.l_shoulder.position = Vec3::new(

View File

@ -2,7 +2,11 @@ use super::{
super::{vek::*, Animation},
CharacterSkeleton, SkeletonAttr,
};
use common::comp::item::{Hands, ToolKind};
use common::{
comp::item::{Hands, ToolKind},
states::utils::StageSection,
};
use std::f32::consts::PI;
pub struct Input {
pub attack: bool,
@ -10,7 +14,12 @@ pub struct Input {
pub struct DashAnimation;
impl Animation for DashAnimation {
type Dependency = (Option<ToolKind>, Option<ToolKind>, f64);
type Dependency = (
Option<ToolKind>,
Option<ToolKind>,
f64,
Option<StageSection>,
);
type Skeleton = CharacterSkeleton;
#[cfg(feature = "use-dyn-lib")]
@ -20,7 +29,7 @@ impl Animation for DashAnimation {
#[allow(clippy::single_match)] // TODO: Pending review in #587
fn update_skeleton_inner(
skeleton: &Self::Skeleton,
(active_tool_kind, second_tool_kind, _global_time): Self::Dependency,
(active_tool_kind, second_tool_kind, _global_time, stage_section): Self::Dependency,
anim_time: f64,
rate: &mut f32,
skeleton_attr: &SkeletonAttr,
@ -29,58 +38,150 @@ impl Animation for DashAnimation {
let mut next = (*skeleton).clone();
let lab = 1.0;
let foot = (((5.0)
/ (1.1 + 3.9 * ((anim_time as f32 * lab as f32 * 25.0).sin()).powf(2.0 as f32)))
.sqrt())
* ((anim_time as f32 * lab as f32 * 25.0).sin());
let slow = (((5.0)
/ (1.1 + 3.9 * ((anim_time as f32 * lab as f32 * 12.4).sin()).powf(2.0 as f32)))
.sqrt())
* ((anim_time as f32 * lab as f32 * 12.4).sin());
let short = (((5.0)
/ (1.5 + 3.5 * ((anim_time as f32 * lab as f32 * 5.0).sin()).powf(2.0 as f32)))
.sqrt())
* ((anim_time as f32 * lab as f32 * 5.0).sin());
let foothoril = (anim_time as f32 * 5.0 * lab as f32 + PI * 1.45).sin();
let foothorir = (anim_time as f32 * 5.0 * lab as f32 + PI * (0.45)).sin();
let footvertl = (anim_time as f32 * 5.0 * lab as f32).sin();
let footvertr = (anim_time as f32 * 5.0 * lab as f32 + PI).sin();
let footrotl = (((1.0)
/ (0.05
+ (0.95)
* ((anim_time as f32 * 5.0 * lab as f32 + PI * 1.4).sin()).powf(2.0 as f32)))
.sqrt())
* ((anim_time as f32 * 5.0 * lab as f32 + PI * 1.4).sin());
let footrotr = (((1.0)
/ (0.05
+ (0.95)
* ((anim_time as f32 * 5.0 * lab as f32 + PI * 0.4).sin()).powf(2.0 as f32)))
.sqrt())
* ((anim_time as f32 * 5.0 * lab as f32 + PI * 0.4).sin());
let shortalt = (anim_time as f32 * lab as f32 * 5.0 + PI / 2.0).sin();
let movement = (anim_time as f32 * 1.0).min(1.0);
next.head.position = Vec3::new(0.0, -2.0 + skeleton_attr.head.0, skeleton_attr.head.1);
next.l_hand.position = Vec3::new(-0.75, -1.0, 2.5);
next.l_hand.orientation = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.2);
next.l_hand.scale = Vec3::one() * 1.04;
next.r_hand.position = Vec3::new(0.75, -1.5, -0.5);
next.r_hand.orientation = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(0.3);
next.r_hand.scale = Vec3::one() * 1.05;
next.main.position = Vec3::new(0.0, 0.0, 2.0);
next.main.orientation = Quaternion::rotation_x(-0.1)
* Quaternion::rotation_y(0.0)
* Quaternion::rotation_z(0.0);
match active_tool_kind {
//TODO: Inventory
Some(ToolKind::Sword(_)) => {
next.head.position = Vec3::new(
0.0,
-2.0 + skeleton_attr.head.0,
-2.0 + skeleton_attr.head.1,
);
next.head.orientation = Quaternion::rotation_z(0.0)
* Quaternion::rotation_x(0.0)
* Quaternion::rotation_y(0.0);
next.head.scale = Vec3::one() * skeleton_attr.head_scale;
if let Some(stage_section) = stage_section {
match stage_section {
StageSection::Buildup => {
next.head.orientation = Quaternion::rotation_z(movement * -0.9);
next.chest.position = Vec3::new(0.0, 0.0, 7.0 + slow * 2.0);
next.chest.orientation =
Quaternion::rotation_x(-0.5) * Quaternion::rotation_z(-0.7);
next.chest.orientation = Quaternion::rotation_z(movement * 1.1);
next.belt.position = Vec3::new(0.0, 1.0, -1.0);
next.belt.orientation = Quaternion::rotation_x(0.2) * Quaternion::rotation_z(0.2);
next.control.position = Vec3::new(-7.0 + movement * -5.0, 7.0, 2.0);
next.control.orientation = Quaternion::rotation_x(movement * -1.0)
* Quaternion::rotation_y(movement * 1.5)
* Quaternion::rotation_z(0.0);
next.control.scale = Vec3::one();
next.l_foot.position = Vec3::new(
-skeleton_attr.foot.0,
skeleton_attr.foot.1 + movement * -12.0,
skeleton_attr.foot.2,
);
next.l_foot.orientation = Quaternion::rotation_x(movement * -1.0);
next.r_foot.position = Vec3::new(
skeleton_attr.foot.0,
skeleton_attr.foot.1,
skeleton_attr.foot.2,
);
},
StageSection::Charge => {
next.head.position = Vec3::new(
0.0,
-2.0 + skeleton_attr.head.0,
skeleton_attr.head.1 + movement * 1.0,
);
next.shorts.position = Vec3::new(0.0, 3.0, -3.0);
next.shorts.orientation = Quaternion::rotation_x(0.4) * Quaternion::rotation_z(0.3);
next.head.orientation = Quaternion::rotation_x(0.0)
* Quaternion::rotation_y(movement * -0.3)
* Quaternion::rotation_z(-0.9 + movement * -0.2 + short * -0.05);
next.chest.position = Vec3::new(
0.0,
skeleton_attr.chest.0,
skeleton_attr.chest.1 + 2.0 + shortalt * -2.5,
);
next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5);
next.l_hand.orientation = Quaternion::rotation_x(1.27);
next.l_hand.scale = Vec3::one() * 1.04;
next.r_hand.position = Vec3::new(0.75, -1.5, -5.5);
next.r_hand.orientation = Quaternion::rotation_x(1.27);
next.r_hand.scale = Vec3::one() * 1.05;
next.main.position = Vec3::new(0.0, 6.0, -1.0);
next.main.orientation = Quaternion::rotation_x(-0.3);
next.main.scale = Vec3::one();
next.chest.orientation = Quaternion::rotation_x(movement * -0.4)
* Quaternion::rotation_y(movement * -0.2)
* Quaternion::rotation_z(1.1);
next.control.position = Vec3::new(-8.0 - slow * 0.5, 3.0 - foot * 0.6, 3.0);
next.control.orientation =
Quaternion::rotation_x(-0.3) * Quaternion::rotation_z(1.1 + slow * 0.2);
next.control.scale = Vec3::one();
next.l_foot.position = Vec3::new(-1.4, foot * 3.0 + 2.0, skeleton_attr.foot.2);
next.l_foot.orientation = Quaternion::rotation_x(foot * -0.4 - 0.8);
next.control.position =
Vec3::new(-13.0, 7.0 + movement * -2.0, 2.0 + movement * 2.0);
next.control.orientation =
Quaternion::rotation_x(-1.0) * Quaternion::rotation_y(1.5);
next.control.scale = Vec3::one();
next.r_foot.position = Vec3::new(5.4, foot * -3.0 - 1.0, skeleton_attr.foot.2);
next.r_foot.orientation = Quaternion::rotation_x(foot * 0.4 - 0.8);
next.shorts.orientation = Quaternion::rotation_z(short * 0.25);
next.belt.orientation = Quaternion::rotation_z(short * 0.1);
next.l_foot.position = Vec3::new(
2.0 - skeleton_attr.foot.0,
skeleton_attr.foot.1 + foothoril * -7.5,
2.0 + skeleton_attr.foot.2 + ((footvertl * -4.0).max(-1.0)),
);
next.l_foot.orientation =
Quaternion::rotation_x(-0.6 + footrotl * -0.6)
* Quaternion::rotation_z(-0.2);
next.r_foot.position = Vec3::new(
2.0 + skeleton_attr.foot.0,
skeleton_attr.foot.1 + foothorir * -7.5,
2.0 + skeleton_attr.foot.2 + ((footvertr * -4.0).max(-1.0)),
);
next.r_foot.orientation =
Quaternion::rotation_x(-0.6 + footrotr * -0.6)
* Quaternion::rotation_z(-0.2);
},
StageSection::Swing => {
next.head.orientation = Quaternion::rotation_y(0.2 + movement * -0.2)
* Quaternion::rotation_z(-1.1 + movement * 1.8);
next.chest.orientation = Quaternion::rotation_y(-0.2 + movement * 0.3)
* Quaternion::rotation_z(1.1 + movement * -2.2);
next.control.position = Vec3::new(-13.0 + movement * -2.0, 5.0, 4.0);
next.control.orientation =
Quaternion::rotation_x(-1.0 + movement * -0.5)
* Quaternion::rotation_y(1.5 + movement * -2.5);
next.control.scale = Vec3::one();
},
StageSection::Recover => {
next.head.orientation = Quaternion::rotation_z(0.7);
next.chest.orientation = Quaternion::rotation_z(-1.1);
next.control.position = Vec3::new(-15.0, 5.0, 2.0);
next.control.orientation =
Quaternion::rotation_x(-1.5) * Quaternion::rotation_y(-1.0);
next.control.scale = Vec3::one();
},
}
}
},
Some(ToolKind::Dagger(_)) => {
next.head.position = Vec3::new(
@ -113,15 +214,15 @@ impl Animation for DashAnimation {
next.main.orientation = Quaternion::rotation_x(-0.3);
next.main.scale = Vec3::one();
next.control.position = Vec3::new(-8.0 - slow * 0.5, 3.0 - foot * 0.6, 3.0);
next.control.position = Vec3::new(-8.0 - slow * 0.5, 3.0, 3.0);
next.control.orientation =
Quaternion::rotation_x(-0.3) * Quaternion::rotation_z(1.1 + slow * 0.2);
next.control.scale = Vec3::one();
next.l_foot.position = Vec3::new(-1.4, foot * 3.0 + 2.0, skeleton_attr.foot.2);
next.l_foot.orientation = Quaternion::rotation_x(foot * -0.4 - 0.8);
next.l_foot.position = Vec3::new(-1.4, 2.0, skeleton_attr.foot.2);
next.l_foot.orientation = Quaternion::rotation_x(-0.8);
next.r_foot.position = Vec3::new(5.4, foot * -3.0 - 1.0, skeleton_attr.foot.2);
next.r_foot.orientation = Quaternion::rotation_x(foot * 0.4 - 0.8);
next.r_foot.position = Vec3::new(5.4, -1.0, skeleton_attr.foot.2);
next.r_foot.orientation = Quaternion::rotation_x(-0.8);
},
_ => {},
}
@ -172,11 +273,11 @@ impl Animation for DashAnimation {
next.second.orientation = Quaternion::rotation_x(-0.3);
next.second.scale = Vec3::one();
next.l_foot.position = Vec3::new(-1.4, foot * 3.0 + 2.0, skeleton_attr.foot.2);
next.l_foot.orientation = Quaternion::rotation_x(foot * -0.4 - 0.8);
next.l_foot.position = Vec3::new(-1.4, 2.0, skeleton_attr.foot.2);
next.l_foot.orientation = Quaternion::rotation_x(-0.8);
next.r_foot.position = Vec3::new(5.4, foot * -3.0 - 1.0, skeleton_attr.foot.2);
next.r_foot.orientation = Quaternion::rotation_x(foot * 0.4 - 0.8);
next.r_foot.position = Vec3::new(5.4, -1.0, skeleton_attr.foot.2);
next.r_foot.orientation = Quaternion::rotation_x(-0.8);
},
_ => {},
}
@ -190,9 +291,6 @@ impl Animation for DashAnimation {
Quaternion::rotation_x(slow * -0.7 + 0.4) * Quaternion::rotation_y(slow * 0.4);
next.hold.scale = Vec3::one() * 0.0;
next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler;
next.torso.orientation =
Quaternion::rotation_z(0.0) * Quaternion::rotation_x(0.0) * Quaternion::rotation_y(0.0);
next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler;
next.l_control.scale = Vec3::one();

View File

@ -2,7 +2,10 @@ use super::{
super::{vek::*, Animation},
CharacterSkeleton, SkeletonAttr,
};
use common::comp::item::{Hands, ToolKind};
use common::{
comp::item::{Hands, ToolKind},
states::utils::StageSection,
};
use std::f32::consts::PI;
pub struct Input {
@ -11,7 +14,12 @@ pub struct Input {
pub struct SpinAnimation;
impl Animation for SpinAnimation {
type Dependency = (Option<ToolKind>, Option<ToolKind>, f64);
type Dependency = (
Option<ToolKind>,
Option<ToolKind>,
f64,
Option<StageSection>,
);
type Skeleton = CharacterSkeleton;
#[cfg(feature = "use-dyn-lib")]
@ -20,7 +28,7 @@ impl Animation for SpinAnimation {
#[cfg_attr(feature = "be-dyn-lib", export_name = "character_spin")]
fn update_skeleton_inner(
skeleton: &Self::Skeleton,
(active_tool_kind, second_tool_kind, _global_time): Self::Dependency,
(active_tool_kind, second_tool_kind, _global_time, stage_section): Self::Dependency,
anim_time: f64,
rate: &mut f32,
skeleton_attr: &SkeletonAttr,
@ -40,9 +48,86 @@ impl Animation for SpinAnimation {
let spin = (anim_time as f32 * 2.8 * lab as f32).sin();
let spinhalf = (anim_time as f32 * 1.4 * lab as f32).sin();
if let Some(
ToolKind::Axe(_) | ToolKind::Hammer(_) | ToolKind::Sword(_) | ToolKind::Dagger(_),
) = active_tool_kind
let build = (anim_time as f32 * 8.0).sin();
let recover = (anim_time as f32 * 8.0).sin();
let movement = anim_time as f32 * 1.0;
let stab = (anim_time as f32 * 8.0).sin();
let rotate = (anim_time as f32 * 1.0).sin();
next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1);
if let Some(ToolKind::Sword(_)) = active_tool_kind {
next.l_hand.position = Vec3::new(-0.75, -1.0, 2.5);
next.l_hand.orientation = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.2);
next.l_hand.scale = Vec3::one() * 1.04;
next.r_hand.position = Vec3::new(0.75, -1.5, -0.5);
next.r_hand.orientation = Quaternion::rotation_x(1.47) * Quaternion::rotation_y(0.3);
next.r_hand.scale = Vec3::one() * 1.05;
next.main.position = Vec3::new(0.0, 0.0, 2.0);
next.main.orientation = Quaternion::rotation_x(-0.1)
* Quaternion::rotation_y(0.0)
* Quaternion::rotation_z(0.0);
if let Some(stage_section) = stage_section {
match stage_section {
StageSection::Buildup => {
//println!("{:.3} build", anim_time);
next.control.position =
Vec3::new(5.0, 11.0 + build * 0.6, 2.0 + build * 0.6);
next.control.orientation = Quaternion::rotation_x(0.0)
* Quaternion::rotation_y(-0.57 + movement * 2.0)
* Quaternion::rotation_z(0.0);
next.chest.orientation = Quaternion::rotation_y(movement * -0.1)
* Quaternion::rotation_z(-1.07 + movement * -0.6);
next.belt.orientation = Quaternion::rotation_x(movement * 0.1);
next.shorts.orientation = Quaternion::rotation_x(movement * 0.1);
next.head.orientation = Quaternion::rotation_y(movement * 0.1)
* Quaternion::rotation_z(1.07 + movement * 0.4);
},
StageSection::Swing => {
//println!("{:.3} swing", anim_time);
next.control.position = Vec3::new(
7.0 + movement * -8.0,
11.0 + stab * 3.0,
2.0 + stab * 3.5 + movement * 3.0,
);
next.control.orientation =
Quaternion::rotation_x(-1.57 + movement * -0.6 + stab * -0.25)
* Quaternion::rotation_y(2.8 + movement * -2.0)
* Quaternion::rotation_z(1.0 + movement * 1.0);
next.head.orientation = Quaternion::rotation_z(-stab * 0.8);
next.chest.orientation = Quaternion::rotation_x(stab * 0.15)
* Quaternion::rotation_y(movement * 0.3)
* Quaternion::rotation_z(movement * 1.5);
next.belt.orientation = Quaternion::rotation_z(rotate * 0.5);
next.shorts.orientation = Quaternion::rotation_z(rotate * 1.5);
next.torso.orientation = Quaternion::rotation_z(rotate * 7.2);
},
StageSection::Recover => {
//println!("{:.3} recover", anim_time);
next.control.position = Vec3::new(
-8.0,
11.0 - recover * 0.8 + movement * -10.0,
6.0 - recover * 0.4 + movement * -4.0,
);
next.control.orientation = Quaternion::rotation_x(-1.57)
* Quaternion::rotation_y(0.0)
* Quaternion::rotation_z(1.0);
next.chest.orientation = Quaternion::rotation_y(movement * -0.1)
* Quaternion::rotation_z(movement * 0.4);
next.head.orientation = Quaternion::rotation_y(movement * 0.1)
* Quaternion::rotation_z(movement * -0.1);
},
_ => {},
}
}
}
// println!("{:?}", stage_progress),
if let Some(ToolKind::Axe(_) | ToolKind::Hammer(_) | ToolKind::Dagger(_)) = active_tool_kind
{
//INTENTION: SWORD
next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5);
@ -88,46 +173,47 @@ impl Animation for SpinAnimation {
* Quaternion::rotation_x(0.0)
* Quaternion::rotation_y(0.0);
next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler;
next.l_foot.position =
Vec3::new(-skeleton_attr.foot.0, foot * 1.0, skeleton_attr.foot.2);
next.l_foot.orientation = Quaternion::rotation_x(foot * -1.2);
next.l_foot.scale = Vec3::one();
next.r_foot.position =
Vec3::new(skeleton_attr.foot.0, foot * -1.0, skeleton_attr.foot.2);
next.r_foot.orientation = Quaternion::rotation_x(foot * 1.2);
next.r_foot.scale = Vec3::one();
next.l_shoulder.position = Vec3::new(-5.0, 0.0, 4.7);
next.l_shoulder.orientation = Quaternion::rotation_x(0.0);
next.l_shoulder.scale = Vec3::one() * 1.1;
next.r_shoulder.position = Vec3::new(5.0, 0.0, 4.7);
next.r_shoulder.orientation = Quaternion::rotation_x(0.0);
next.r_shoulder.scale = Vec3::one() * 1.1;
next.glider.position = Vec3::new(0.0, 5.0, 0.0);
next.glider.orientation = Quaternion::rotation_y(0.0);
next.glider.scale = Vec3::one() * 0.0;
next.lantern.position = Vec3::new(
skeleton_attr.lantern.0,
skeleton_attr.lantern.1,
skeleton_attr.lantern.2,
);
next.lantern.orientation =
Quaternion::rotation_x(spin * -0.7 + 0.4) * Quaternion::rotation_y(spin * 0.4);
next.lantern.scale = Vec3::one() * 0.65;
next.hold.scale = Vec3::one() * 0.0;
next.l_control.position = Vec3::new(0.0, 0.0, 0.0);
next.l_control.orientation = Quaternion::rotation_x(0.0);
next.l_control.scale = Vec3::one();
next.r_control.position = Vec3::new(0.0, 0.0, 0.0);
next.r_control.orientation = Quaternion::rotation_x(0.0);
next.r_control.scale = Vec3::one();
}
next.l_foot.position = Vec3::new(-skeleton_attr.foot.0, foot * 1.0, skeleton_attr.foot.2);
next.l_foot.orientation = Quaternion::rotation_x(foot * -1.2);
next.l_foot.scale = Vec3::one();
next.r_foot.position = Vec3::new(skeleton_attr.foot.0, foot * -1.0, skeleton_attr.foot.2);
next.r_foot.orientation = Quaternion::rotation_x(foot * 1.2);
next.r_foot.scale = Vec3::one();
next.l_shoulder.position = Vec3::new(-5.0, 0.0, 4.7);
next.l_shoulder.orientation = Quaternion::rotation_x(0.0);
next.l_shoulder.scale = Vec3::one() * 1.1;
next.r_shoulder.position = Vec3::new(5.0, 0.0, 4.7);
next.r_shoulder.orientation = Quaternion::rotation_x(0.0);
next.r_shoulder.scale = Vec3::one() * 1.1;
next.glider.position = Vec3::new(0.0, 5.0, 0.0);
next.glider.orientation = Quaternion::rotation_y(0.0);
next.glider.scale = Vec3::one() * 0.0;
next.lantern.position = Vec3::new(
skeleton_attr.lantern.0,
skeleton_attr.lantern.1,
skeleton_attr.lantern.2,
);
next.lantern.orientation =
Quaternion::rotation_x(spin * -0.7 + 0.4) * Quaternion::rotation_y(spin * 0.4);
next.lantern.scale = Vec3::one() * 0.65;
next.hold.scale = Vec3::one() * 0.0;
next.l_control.position = Vec3::new(0.0, 0.0, 0.0);
next.l_control.orientation = Quaternion::rotation_x(0.0);
next.l_control.scale = Vec3::one();
next.r_control.position = Vec3::new(0.0, 0.0, 0.0);
next.r_control.orientation = Quaternion::rotation_x(0.0);
next.r_control.scale = Vec3::one();
next.second.scale = match (
active_tool_kind.map(|tk| tk.hands()),
second_tool_kind.map(|tk| tk.hands()),

View File

@ -2,13 +2,22 @@ use super::{
super::{vek::*, Animation},
CharacterSkeleton, SkeletonAttr,
};
use common::comp::item::{Hands, ToolKind};
use common::{
comp::item::{Hands, ToolKind},
states::utils::StageSection,
};
use std::f32::consts::PI;
pub struct SpinMeleeAnimation;
impl Animation for SpinMeleeAnimation {
type Dependency = (Option<ToolKind>, Option<ToolKind>, Vec3<f32>, f64);
type Dependency = (
Option<ToolKind>,
Option<ToolKind>,
Vec3<f32>,
f64,
Option<StageSection>,
);
type Skeleton = CharacterSkeleton;
#[cfg(feature = "use-dyn-lib")]
@ -18,7 +27,7 @@ impl Animation for SpinMeleeAnimation {
#[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): Self::Dependency,
(active_tool_kind, second_tool_kind, velocity, _global_time, stage_section): Self::Dependency,
anim_time: f64,
rate: &mut f32,
skeleton_attr: &SkeletonAttr,
@ -44,109 +53,205 @@ impl Animation for SpinMeleeAnimation {
} else {
lab as f32 * anim_time as f32 * 0.9
};
let movement = anim_time as f32 * 1.0;
//feet
let slowersmooth = (anim_time as f32 * lab as f32 * 4.0).sin();
let quick = (anim_time as f32 * lab as f32 * 8.0).sin();
if let Some(ToolKind::Axe(_)) = active_tool_kind {
next.l_hand.position = Vec3::new(-0.5, 0.0, 4.0);
next.l_hand.orientation = Quaternion::rotation_x(PI / 2.0)
* Quaternion::rotation_z(0.0)
* Quaternion::rotation_y(PI);
next.l_hand.scale = Vec3::one() * 1.08;
next.r_hand.position = Vec3::new(0.5, 0.0, -2.5);
next.r_hand.orientation = Quaternion::rotation_x(PI / 2.0)
* Quaternion::rotation_z(0.0)
* Quaternion::rotation_y(0.0);
next.r_hand.scale = Vec3::one() * 1.06;
next.main.position = Vec3::new(-0.0, -2.0, -1.0);
next.main.orientation = Quaternion::rotation_x(0.0)
* Quaternion::rotation_y(0.0)
* Quaternion::rotation_z(0.0);
match active_tool_kind {
Some(ToolKind::Sword(_)) => {
next.l_hand.position = Vec3::new(-0.75, -1.0, 2.5);
next.l_hand.orientation =
Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.2);
next.l_hand.scale = Vec3::one() * 1.04;
next.r_hand.position = Vec3::new(0.75, -1.5, -0.5);
next.r_hand.orientation =
Quaternion::rotation_x(1.47) * Quaternion::rotation_y(0.3);
next.r_hand.scale = Vec3::one() * 1.05;
next.main.position = Vec3::new(0.0, 0.0, 2.0);
next.main.orientation = Quaternion::rotation_x(-0.1)
* Quaternion::rotation_y(0.0)
* Quaternion::rotation_z(0.0);
next.head.position =
Vec3::new(0.0, skeleton_attr.head.0 + 0.0, skeleton_attr.head.1);
next.control.position = Vec3::new(0.0, 16.0, 3.0);
next.control.orientation = Quaternion::rotation_x(-1.4)
* Quaternion::rotation_y(0.0)
* Quaternion::rotation_z(1.4);
next.control.scale = Vec3::one();
if let Some(stage_section) = stage_section {
match stage_section {
StageSection::Buildup => {
next.control.position =
Vec3::new(-7.0, 7.0 + movement * -8.0, 2.0 + movement * -6.0);
next.control.orientation = Quaternion::rotation_x(movement * -0.5)
* Quaternion::rotation_y(movement * 0.3)
* Quaternion::rotation_z(movement * -1.5);
next.chest.position = Vec3::new(
0.0,
skeleton_attr.chest.0 + movement * -1.0,
skeleton_attr.chest.1 + movement * -2.5,
);
next.chest.orientation = Quaternion::rotation_x(movement * -1.1)
* Quaternion::rotation_z(movement * -0.35);
next.belt.orientation = Quaternion::rotation_z(movement * 0.35);
next.shorts.orientation = Quaternion::rotation_z(movement * 0.5);
next.head.position = Vec3::new(
0.0,
skeleton_attr.head.0 - 2.0 + movement * -6.0,
skeleton_attr.head.1 + movement * -4.0,
);
next.head.orientation = Quaternion::rotation_x(movement * 0.9)
* Quaternion::rotation_y(0.0)
* Quaternion::rotation_z(movement * 0.05);
next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1);
next.head.orientation = Quaternion::rotation_z(0.0)
* Quaternion::rotation_x(-0.15)
* Quaternion::rotation_y(0.08);
next.chest.position = Vec3::new(
0.0,
skeleton_attr.chest.0 - 3.0,
skeleton_attr.chest.1 - 2.0,
);
next.chest.orientation = Quaternion::rotation_z(0.0)
* Quaternion::rotation_x(-0.1)
* Quaternion::rotation_y(0.3);
next.chest.scale = Vec3::one();
next.l_foot.position = Vec3::new(
-skeleton_attr.foot.0,
skeleton_attr.foot.1 + movement * 4.0,
skeleton_attr.foot.2,
);
next.l_foot.orientation = Quaternion::rotation_x(movement * 0.2);
next.r_foot.position = Vec3::new(
skeleton_attr.foot.0,
skeleton_attr.foot.1 + movement * -12.0,
skeleton_attr.foot.2 + movement * 1.0 + quick * 1.0,
);
next.r_foot.orientation = Quaternion::rotation_x(movement * -1.0)
* Quaternion::rotation_z(movement * -0.8);
},
StageSection::Swing => {
next.head.position =
Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1);
next.belt.position = Vec3::new(0.0, 1.0, -1.0);
next.belt.orientation = Quaternion::rotation_z(0.0)
* Quaternion::rotation_x(0.4)
* Quaternion::rotation_y(0.0);
next.belt.scale = Vec3::one() * 0.98;
next.shorts.position = Vec3::new(0.0, 3.0, -2.5);
next.shorts.orientation = Quaternion::rotation_z(0.0)
* Quaternion::rotation_x(0.7)
* Quaternion::rotation_y(0.0);
next.shorts.scale = Vec3::one();
next.torso.position = Vec3::new(
-xshift * (anim_time as f32).min(0.6),
-yshift * (anim_time as f32).min(0.6),
0.0,
) * skeleton_attr.scaler;
next.torso.orientation = Quaternion::rotation_z(spin * -16.0)
* Quaternion::rotation_x(0.0)
* Quaternion::rotation_y(0.0);
next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler;
next.control.position = Vec3::new(-7.0, 7.0, 2.0);
next.control.orientation = Quaternion::rotation_x(-PI / 2.0)
* Quaternion::rotation_z(-PI / 2.0);
next.torso.orientation = Quaternion::rotation_z(movement * PI * 2.0);
next.chest.position =
Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1);
next.chest.orientation = Quaternion::rotation_y(0.3);
next.head.position =
Vec3::new(0.0, skeleton_attr.head.0 - 2.0, skeleton_attr.head.1);
next.head.orientation = Quaternion::rotation_x(-0.15);
next.belt.orientation = Quaternion::rotation_x(0.1);
next.shorts.orientation = Quaternion::rotation_x(0.2);
},
StageSection::Recover => {
next.head.position =
Vec3::new(0.0, skeleton_attr.head.0 - 2.0, skeleton_attr.head.1);
next.control.position = Vec3::new(-7.0, 7.0, 2.0);
next.control.orientation =
Quaternion::rotation_x(-PI / 2.0 + movement * PI / 2.0)
* Quaternion::rotation_z(-PI / 2.0 + movement * PI / 2.0);
next.head.orientation = Quaternion::rotation_x(-0.15 + movement * 0.15);
next.chest.orientation = Quaternion::rotation_y(0.3 + movement * -0.3)
},
_ => {},
}
}
},
Some(ToolKind::Axe(_)) => {
next.l_hand.position = Vec3::new(-0.5, 0.0, 4.0);
next.l_hand.orientation = Quaternion::rotation_x(PI / 2.0)
* Quaternion::rotation_z(0.0)
* Quaternion::rotation_y(PI);
next.l_hand.scale = Vec3::one() * 1.08;
next.r_hand.position = Vec3::new(0.5, 0.0, -2.5);
next.r_hand.orientation = Quaternion::rotation_x(PI / 2.0)
* Quaternion::rotation_z(0.0)
* Quaternion::rotation_y(0.0);
next.r_hand.scale = Vec3::one() * 1.06;
next.main.position = Vec3::new(-0.0, -2.0, -1.0);
next.main.orientation = Quaternion::rotation_x(0.0)
* Quaternion::rotation_y(0.0)
* Quaternion::rotation_z(0.0);
next.control.position = Vec3::new(0.0, 16.0, 3.0);
next.control.orientation = Quaternion::rotation_x(-1.4)
* Quaternion::rotation_y(0.0)
* Quaternion::rotation_z(1.4);
next.control.scale = Vec3::one();
next.head.position = Vec3::new(0.0, skeleton_attr.head.0, skeleton_attr.head.1);
next.head.orientation = Quaternion::rotation_z(0.0)
* Quaternion::rotation_x(-0.15)
* Quaternion::rotation_y(0.08);
next.chest.position = Vec3::new(
0.0,
skeleton_attr.chest.0 - 3.0,
skeleton_attr.chest.1 - 2.0,
);
next.chest.orientation = Quaternion::rotation_z(0.0)
* Quaternion::rotation_x(-0.1)
* Quaternion::rotation_y(0.3);
next.chest.scale = Vec3::one();
next.belt.position = Vec3::new(0.0, 1.0, -1.0);
next.belt.orientation = Quaternion::rotation_z(0.0)
* Quaternion::rotation_x(0.4)
* Quaternion::rotation_y(0.0);
next.belt.scale = Vec3::one() * 0.98;
next.shorts.position = Vec3::new(0.0, 3.0, -2.5);
next.shorts.orientation = Quaternion::rotation_z(0.0)
* Quaternion::rotation_x(0.7)
* Quaternion::rotation_y(0.0);
next.shorts.scale = Vec3::one();
next.torso.position = Vec3::new(
-xshift * (anim_time as f32).min(0.6),
-yshift * (anim_time as f32).min(0.6),
0.0,
) * skeleton_attr.scaler;
next.torso.orientation = Quaternion::rotation_z(spin * -16.0)
* Quaternion::rotation_x(0.0)
* Quaternion::rotation_y(0.0);
next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler;
if velocity.z.abs() > 0.1 {
next.l_foot.position =
Vec3::new(-skeleton_attr.foot.0, 8.0, skeleton_attr.foot.2 + 2.0);
next.l_foot.orientation =
Quaternion::rotation_x(1.0) * Quaternion::rotation_z(0.0);
next.l_foot.scale = Vec3::one();
next.r_foot.position =
Vec3::new(skeleton_attr.foot.0, 8.0, skeleton_attr.foot.2 + 2.0);
next.r_foot.orientation = Quaternion::rotation_x(1.0);
next.r_foot.scale = Vec3::one();
} else if speed < 0.5 {
next.l_foot.position = Vec3::new(
-skeleton_attr.foot.0,
2.0 + quick * -6.0,
skeleton_attr.foot.2,
);
next.l_foot.orientation = Quaternion::rotation_x(0.5 + slowersmooth * 0.2)
* Quaternion::rotation_z(0.0);
next.l_foot.scale = Vec3::one();
next.r_foot.position =
Vec3::new(skeleton_attr.foot.0, 4.0, skeleton_attr.foot.2);
next.r_foot.orientation = Quaternion::rotation_x(0.5 - slowersmooth * 0.2)
* Quaternion::rotation_y(-0.4);
next.r_foot.scale = Vec3::one();
} else {
next.l_foot.position = Vec3::new(
-skeleton_attr.foot.0,
2.0 + quick * -6.0,
skeleton_attr.foot.2,
);
next.l_foot.orientation = Quaternion::rotation_x(0.5 + slowersmooth * 0.2)
* Quaternion::rotation_z(0.0);
next.l_foot.scale = Vec3::one();
next.r_foot.position = Vec3::new(
skeleton_attr.foot.0,
2.0 + quick * 6.0,
skeleton_attr.foot.2,
);
next.r_foot.orientation = Quaternion::rotation_x(0.5 - slowersmooth * 0.2)
* Quaternion::rotation_z(0.0);
next.r_foot.scale = Vec3::one();
};
},
_ => {},
}
if velocity.z.abs() > 0.1 {
next.l_foot.position =
Vec3::new(-skeleton_attr.foot.0, 8.0, skeleton_attr.foot.2 + 2.0);
next.l_foot.orientation = Quaternion::rotation_x(1.0) * Quaternion::rotation_z(0.0);
next.l_foot.scale = Vec3::one();
next.r_foot.position = Vec3::new(skeleton_attr.foot.0, 8.0, skeleton_attr.foot.2 + 2.0);
next.r_foot.orientation = Quaternion::rotation_x(1.0);
next.r_foot.scale = Vec3::one();
} else if speed < 0.5 {
next.l_foot.position = Vec3::new(
-skeleton_attr.foot.0,
2.0 + quick * -6.0,
skeleton_attr.foot.2,
);
next.l_foot.orientation =
Quaternion::rotation_x(0.5 + slowersmooth * 0.2) * Quaternion::rotation_z(0.0);
next.l_foot.scale = Vec3::one();
next.r_foot.position = Vec3::new(skeleton_attr.foot.0, 4.0, skeleton_attr.foot.2);
next.r_foot.orientation =
Quaternion::rotation_x(0.5 - slowersmooth * 0.2) * Quaternion::rotation_y(-0.4);
next.r_foot.scale = Vec3::one();
} else {
next.l_foot.position = Vec3::new(
-skeleton_attr.foot.0,
2.0 + quick * -6.0,
skeleton_attr.foot.2,
);
next.l_foot.orientation =
Quaternion::rotation_x(0.5 + slowersmooth * 0.2) * Quaternion::rotation_z(0.0);
next.l_foot.scale = Vec3::one();
next.r_foot.position = Vec3::new(
skeleton_attr.foot.0,
2.0 + quick * 6.0,
skeleton_attr.foot.2,
);
next.r_foot.orientation =
Quaternion::rotation_x(0.5 - slowersmooth * 0.2) * Quaternion::rotation_z(0.0);
next.r_foot.scale = Vec3::one();
};
next.lantern.position = Vec3::new(
skeleton_attr.lantern.0,
skeleton_attr.lantern.1,

View File

@ -41,7 +41,7 @@ impl Animation for StandAnimation {
);
next.head.position = Vec3::new(
0.0,
-3.0 + skeleton_attr.head.0,
-2.0 + skeleton_attr.head.0,
skeleton_attr.head.1 + slow * 0.3 + breathe * -0.05,
);
next.head.orientation = Quaternion::rotation_z(head_look.x)
@ -174,7 +174,7 @@ impl Animation for StandAnimation {
next.lantern.orientation = Quaternion::rotation_x(0.1) * Quaternion::rotation_y(0.1);
next.lantern.scale = Vec3::one() * 0.65;
next.torso.position = Vec3::new(0.0, 0.0, 0.) * skeleton_attr.scaler;
next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler;
next.torso.orientation = Quaternion::rotation_x(0.0);
next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler;

View File

@ -111,18 +111,18 @@ impl Animation for WieldAnimation {
match active_tool_kind {
//TODO: Inventory
Some(ToolKind::Sword(_)) => {
next.l_hand.position = Vec3::new(-0.75, -1.0, -2.5);
next.l_hand.position = Vec3::new(-0.75, -1.0, 2.5);
next.l_hand.orientation =
Quaternion::rotation_x(1.47) * Quaternion::rotation_y(-0.2);
next.l_hand.scale = Vec3::one() * 1.04;
next.r_hand.position = Vec3::new(0.75, -1.5, -5.5);
next.r_hand.position = Vec3::new(0.75, -1.5, -0.5);
next.r_hand.orientation =
Quaternion::rotation_x(1.47) * Quaternion::rotation_y(0.3);
next.r_hand.scale = Vec3::one() * 1.05;
next.main.position = Vec3::new(0.0, 0.0, -3.0);
next.main.orientation = Quaternion::rotation_x(-0.1);
next.control.position = Vec3::new(-7.0, 6.0, 6.0);
next.control.position = Vec3::new(-7.0, 7.0, 2.0);
next.control.orientation = Quaternion::rotation_x(u_slow * 0.15)
* Quaternion::rotation_z(u_slowalt * 0.08);
next.control.scale = Vec3::one();

View File

@ -113,15 +113,34 @@ fn matches_ability_stage() {
});
let result = CombatEventMapper::map_event(
&CharacterState::TripleStrike(states::triple_strike::Data {
base_damage: 10,
stage: states::triple_strike::Stage::First,
stage_time_active: Duration::default(),
stage_exhausted: false,
initialized: true,
transition_style: states::triple_strike::TransitionStyle::Hold(
states::triple_strike::HoldingState::Released,
),
&CharacterState::ComboMelee(states::combo_melee::Data {
static_data: states::combo_melee::StaticData {
num_stages: 1,
stage_data: vec![states::combo_melee::Stage {
stage: 1,
base_damage: 100,
max_damage: 120,
damage_increase: 10,
knockback: 10.0,
range: 4.0,
angle: 30.0,
base_buildup_duration: Duration::from_millis(500),
base_swing_duration: Duration::from_millis(200),
base_recover_duration: Duration::from_millis(400),
forward_movement: 0.5,
}],
initial_energy_gain: 0,
max_energy_gain: 100,
energy_increase: 20,
speed_increase: 0.05,
max_speed_increase: 1.8,
is_interruptible: true,
},
stage: 1,
combo: 0,
timer: Duration::default(),
stage_section: states::utils::StageSection::Swing,
next_stage: false,
}),
&PreviousEntityState {
event: SfxEvent::Idle,
@ -134,7 +153,7 @@ fn matches_ability_stage() {
assert_eq!(
result,
SfxEvent::Attack(
CharacterAbilityType::TripleStrike(states::triple_strike::Stage::First),
CharacterAbilityType::ComboMelee(states::utils::StageSection::Swing, 1),
ToolCategory::Sword
)
);
@ -154,15 +173,34 @@ fn ignores_different_ability_stage() {
});
let result = CombatEventMapper::map_event(
&CharacterState::TripleStrike(states::triple_strike::Data {
base_damage: 10,
stage: states::triple_strike::Stage::Second,
stage_time_active: Duration::default(),
stage_exhausted: false,
initialized: true,
transition_style: states::triple_strike::TransitionStyle::Hold(
states::triple_strike::HoldingState::Released,
),
&CharacterState::ComboMelee(states::combo_melee::Data {
static_data: states::combo_melee::StaticData {
num_stages: 1,
stage_data: vec![states::combo_melee::Stage {
stage: 1,
base_damage: 100,
max_damage: 120,
damage_increase: 10,
knockback: 10.0,
range: 4.0,
angle: 30.0,
base_buildup_duration: Duration::from_millis(500),
base_swing_duration: Duration::from_millis(200),
base_recover_duration: Duration::from_millis(400),
forward_movement: 0.5,
}],
initial_energy_gain: 0,
max_energy_gain: 100,
energy_increase: 20,
speed_increase: 0.05,
max_speed_increase: 1.8,
is_interruptible: true,
},
stage: 1,
combo: 0,
timer: Duration::default(),
stage_section: states::utils::StageSection::Swing,
next_stage: false,
}),
&PreviousEntityState {
event: SfxEvent::Idle,
@ -175,7 +213,7 @@ fn ignores_different_ability_stage() {
assert_ne!(
result,
SfxEvent::Attack(
CharacterAbilityType::TripleStrike(states::triple_strike::Stage::First),
CharacterAbilityType::ComboMelee(states::utils::StageSection::Swing, 2),
ToolCategory::Sword
)
);

View File

@ -72,10 +72,9 @@
//! threshold: 1.2,
//! ),
//! // A multi-stage attack ability which depends on the weapon
//! Attack(TripleStrike(First), Sword): (
//! Attack(ComboMelee(Swing, 1), Sword): (
//! files: [
//! "voxygen.audio.sfx.weapon.sword_03",
//! "voxygen.audio.sfx.weapon.sword_04",
//! "voxygen.audio.sfx.abilities.swing_sword",
//! ],
//! threshold: 0.5,
//! ),

View File

@ -82,7 +82,7 @@ impl State {
} else if let ToolKind::Debug(kind) = &kind.kind {
kind == "Boost"
} else {
false
matches!(&kind.kind, ToolKind::Sword(_))
}
} else {
false

View File

@ -267,6 +267,7 @@ image_ids! {
fire_spell_1: "voxygen.element.icons.fire_spell_0",
snake_arrow_0: "voxygen.element.icons.snake",
heal_0: "voxygen.element.icons.heal_0",
sword_whirlwind: "voxygen.element.icons.sword_whirlwind",
// Buttons
button: "voxygen.element.buttons.button",

View File

@ -803,6 +803,10 @@ impl<'a> Widget for Skillbar<'a> {
"\nWhirls a big fireball into the air. \nExplodes the ground \
and does\na big amount of damage",
)),
ToolKind::Sword(_) => Some((
"Whirlwind",
"\nMove forward while spinning with \n your sword.",
)),
ToolKind::Debug(kind) => match kind.as_ref() {
"Boost" => Some((
"Possessing Arrow",

View File

@ -84,6 +84,7 @@ pub enum HotbarImage {
Item(ItemKey),
Fireball,
SnakeArrow,
SwordWhirlwind,
}
type HotbarSource<'a> = (&'a hotbar::State, &'a Inventory, &'a Loadout, &'a Energy);
@ -113,6 +114,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
"Boost" => Some(HotbarImage::SnakeArrow),
_ => None,
},
ToolKind::Sword(_) => Some(HotbarImage::SwordWhirlwind),
_ => None,
},
_ => None,
@ -143,6 +145,7 @@ impl<'a> SlotKey<HotbarSource<'a>, HotbarImageSource<'a>> for HotbarSlot {
HotbarImage::Item(key) => item_imgs.img_id_or_not_found_img(key.clone()),
HotbarImage::SnakeArrow => imgs.snake_arrow_0,
HotbarImage::Fireball => imgs.fire_spell_1,
HotbarImage::SwordWhirlwind => imgs.sword_whirlwind,
}
}
}

View File

@ -25,12 +25,13 @@ use anim::{
};
use common::{
comp::{
item::ItemKind, Body, CharacterState, Item, Last, LightAnimation, LightEmitter, Loadout,
Ori, PhysicsState, Pos, Scale, Stats, Vel,
item::{ItemKind, ToolKind},
Body, CharacterState, Item, Last, LightAnimation, LightEmitter, Loadout, Ori, PhysicsState,
Pos, Scale, Stats, Vel,
},
span,
state::{DeltaTime, State},
states::triple_strike,
states::utils::StageSection,
terrain::TerrainChunk,
vol::RectRasterableVol,
};
@ -809,7 +810,13 @@ impl FigureMgr {
CharacterState::BasicMelee(_) => {
anim::character::AlphaAnimation::update_skeleton(
&target_base,
(active_tool_kind, second_tool_kind, vel.0.magnitude(), time),
(
active_tool_kind,
second_tool_kind,
vel.0.magnitude(),
time,
None,
),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
@ -879,17 +886,43 @@ impl FigureMgr {
CharacterState::Boost(_) => {
anim::character::AlphaAnimation::update_skeleton(
&target_base,
(active_tool_kind, second_tool_kind, vel.0.magnitude(), time),
(
active_tool_kind,
second_tool_kind,
vel.0.magnitude(),
time,
None,
),
state.state_time,
&mut state_animation_rate,
skeleton_attr,
)
},
CharacterState::DashMelee(_) => {
CharacterState::DashMelee(s) => {
let stage_time = s.timer.as_secs_f64();
let stage_progress = match s.stage_section {
StageSection::Buildup => {
stage_time / s.static_data.buildup_duration.as_secs_f64()
},
StageSection::Charge => {
stage_time / s.static_data.charge_duration.as_secs_f64()
},
StageSection::Swing => {
stage_time / s.static_data.swing_duration.as_secs_f64()
},
StageSection::Recover => {
stage_time / s.static_data.recover_duration.as_secs_f64()
},
};
anim::character::DashAnimation::update_skeleton(
&target_base,
(active_tool_kind, second_tool_kind, time),
state.state_time,
(
active_tool_kind,
second_tool_kind,
time,
Some(s.stage_section),
),
stage_progress,
&mut state_animation_rate,
skeleton_attr,
)
@ -903,43 +936,106 @@ impl FigureMgr {
skeleton_attr,
)
},
CharacterState::SpinMelee(_) => {
CharacterState::SpinMelee(s) => {
let stage_progress = match active_tool_kind {
Some(ToolKind::Sword(_)) => {
let stage_time = s.timer.as_secs_f64();
match s.stage_section {
StageSection::Buildup => {
stage_time
/ s.static_data.buildup_duration.as_secs_f64()
},
StageSection::Swing => {
stage_time / s.static_data.swing_duration.as_secs_f64()
},
StageSection::Recover => {
stage_time
/ s.static_data.recover_duration.as_secs_f64()
},
_ => 0.0,
}
},
_ => state.state_time,
};
anim::character::SpinMeleeAnimation::update_skeleton(
&target_base,
(active_tool_kind, second_tool_kind, vel.0, time),
state.state_time,
(
active_tool_kind,
second_tool_kind,
vel.0,
time,
Some(s.stage_section),
),
stage_progress,
&mut state_animation_rate,
skeleton_attr,
)
},
CharacterState::TripleStrike(s) => match s.stage {
triple_strike::Stage::First => {
anim::character::AlphaAnimation::update_skeleton(
CharacterState::ComboMelee(s) => {
let stage_index = (s.stage - 1) as usize;
let stage_time = s.timer.as_secs_f64();
let stage_progress = match s.stage_section {
StageSection::Buildup => {
stage_time
/ s.static_data.stage_data[stage_index]
.base_buildup_duration
.as_secs_f64()
},
StageSection::Swing => {
stage_time
/ s.static_data.stage_data[stage_index]
.base_swing_duration
.as_secs_f64()
},
StageSection::Recover => {
stage_time
/ s.static_data.stage_data[stage_index]
.base_recover_duration
.as_secs_f64()
},
_ => 0.0,
};
match s.stage {
1 => anim::character::AlphaAnimation::update_skeleton(
&target_base,
(active_tool_kind, second_tool_kind, vel.0.magnitude(), time),
state.state_time,
(
active_tool_kind,
second_tool_kind,
vel.0.magnitude(),
time,
Some(s.stage_section),
),
stage_progress,
&mut state_animation_rate,
skeleton_attr,
)
},
triple_strike::Stage::Second => {
anim::character::SpinAnimation::update_skeleton(
),
2 => anim::character::SpinAnimation::update_skeleton(
&target_base,
(active_tool_kind, second_tool_kind, time),
state.state_time,
(
active_tool_kind,
second_tool_kind,
time,
Some(s.stage_section),
),
stage_progress,
&mut state_animation_rate,
skeleton_attr,
)
},
triple_strike::Stage::Third => {
anim::character::BetaAnimation::update_skeleton(
),
_ => anim::character::BetaAnimation::update_skeleton(
&target_base,
(active_tool_kind, second_tool_kind, vel.0.magnitude(), time),
state.state_time,
(
active_tool_kind,
second_tool_kind,
vel.0.magnitude(),
time,
Some(s.stage_section),
),
stage_progress,
&mut state_animation_rate,
skeleton_attr,
)
},
),
}
},
CharacterState::BasicBlock { .. } => {
anim::character::BlockIdleAnimation::update_skeleton(