From 951acfca216bb41ac23f9dd094d7c5da85593947 Mon Sep 17 00:00:00 2001 From: jiminycrick Date: Sun, 20 Sep 2020 21:16:52 -0700 Subject: [PATCH] Add 3rd skill for hammer, bow, and axe minus skillbar UI stuff --- common/src/comp/ability.rs | 106 ++++++++++++ common/src/comp/character_state.rs | 10 ++ common/src/comp/inventory/item/tool.rs | 55 ++++++- common/src/states/charged_melee.rs | 171 ++++++++++++++++++++ common/src/states/leap_melee.rs | 44 ++++- common/src/states/mod.rs | 2 + common/src/states/repeater_ranged.rs | 153 ++++++++++++++++++ common/src/sys/character_behavior.rs | 4 + common/src/sys/stats.rs | 2 + voxygen/src/anim/src/character/charge.rs | 16 ++ voxygen/src/anim/src/character/leapmelee.rs | 141 ++++++++++++++-- voxygen/src/hud/skillbar.rs | 15 ++ 12 files changed, 698 insertions(+), 21 deletions(-) create mode 100644 common/src/states/charged_melee.rs create mode 100644 common/src/states/repeater_ranged.rs diff --git a/common/src/comp/ability.rs b/common/src/comp/ability.rs index 345ad6997b..c07beab4a0 100644 --- a/common/src/comp/ability.rs +++ b/common/src/comp/ability.rs @@ -18,6 +18,7 @@ pub enum CharacterAbilityType { BasicMelee, BasicRanged, Boost, + ChargedMelee, ChargedRanged, DashMelee, BasicBlock, @@ -26,6 +27,7 @@ pub enum CharacterAbilityType { SpinMelee, GroundShockwave, BasicBeam, + RepeaterRanged, } impl From<&CharacterState> for CharacterAbilityType { @@ -39,9 +41,11 @@ impl From<&CharacterState> for CharacterAbilityType { CharacterState::LeapMelee(_) => Self::LeapMelee, CharacterState::ComboMelee(data) => Self::ComboMelee(data.stage_section, data.stage), CharacterState::SpinMelee(_) => Self::SpinMelee, + CharacterState::ChargedMelee(_) => Self::ChargedMelee, CharacterState::ChargedRanged(_) => Self::ChargedRanged, CharacterState::GroundShockwave(_) => Self::ChargedRanged, CharacterState::BasicBeam(_) => Self::BasicBeam, + CharacterState::RepeaterRanged(_) => Self::RepeaterRanged, _ => Self::BasicMelee, } } @@ -69,6 +73,20 @@ pub enum CharacterAbility { projectile_gravity: Option, projectile_speed: f32, }, + RepeaterRanged { + movement_duration: Duration, + energy_cost: u32, + holdable: bool, + prepare_duration: Duration, + recover_duration: Duration, + projectile: Projectile, + projectile_body: Body, + projectile_light: Option, + projectile_gravity: Option, + projectile_speed: f32, + repetitions: u32, + current_rep: u32, + }, Boost { duration: Duration, only_up: bool, @@ -107,6 +125,10 @@ pub enum CharacterAbility { buildup_duration: Duration, recover_duration: Duration, base_damage: u32, + range: f32, + max_angle: f32, + leap_speed: f32, + leap_vert_speed: f32, }, SpinMelee { buildup_duration: Duration, @@ -122,6 +144,19 @@ pub enum CharacterAbility { forward_speed: f32, num_spins: u32, }, + ChargedMelee { + energy_cost: u32, + energy_drain: u32, + initial_damage: u32, + max_damage: u32, + initial_knockback: f32, + max_knockback: f32, + prepare_duration: Duration, + charge_duration: Duration, + recover_duration: Duration, + range: f32, + max_angle: f32, + }, ChargedRanged { energy_cost: u32, energy_drain: u32, @@ -203,6 +238,14 @@ impl CharacterAbility { .energy .try_change_by(-(*energy_cost as i32), EnergySource::Ability) .is_ok(), + CharacterAbility::ChargedMelee { energy_cost, .. } => update + .energy + .try_change_by(-(*energy_cost as i32), EnergySource::Ability) + .is_ok(), + CharacterAbility::RepeaterRanged { energy_cost, .. } => update + .energy + .try_change_by(-(*energy_cost as i32), EnergySource::Ability) + .is_ok(), CharacterAbility::GroundShockwave { energy_cost, .. } => update .energy .try_change_by(-(*energy_cost as i32), EnergySource::Ability) @@ -424,6 +467,10 @@ impl From<&CharacterAbility> for CharacterState { buildup_duration, recover_duration, base_damage, + range, + max_angle, + leap_speed, + leap_vert_speed, } => CharacterState::LeapMelee(leap_melee::Data { initialize: true, exhausted: false, @@ -431,6 +478,10 @@ impl From<&CharacterAbility> for CharacterState { buildup_duration: *buildup_duration, recover_duration: *recover_duration, base_damage: *base_damage, + range: *range, + max_angle: *max_angle, + leap_speed: *leap_speed, + leap_vert_speed: *leap_vert_speed, }), CharacterAbility::SpinMelee { buildup_duration, @@ -465,6 +516,32 @@ impl From<&CharacterAbility> for CharacterState { stage_section: StageSection::Buildup, exhausted: false, }), + CharacterAbility::ChargedMelee { + energy_cost: _, + energy_drain, + initial_damage, + max_damage, + initial_knockback, + max_knockback, + prepare_duration, + charge_duration, + recover_duration, + range, + max_angle, + } => CharacterState::ChargedMelee(charged_melee::Data { + exhausted: false, + energy_drain: *energy_drain, + initial_damage: *initial_damage, + max_damage: *max_damage, + initial_knockback: *initial_knockback, + max_knockback: *max_knockback, + prepare_duration: *prepare_duration, + charge_duration: *charge_duration, + charge_timer: Duration::default(), + recover_duration: *recover_duration, + range: *range, + max_angle: *max_angle, + }), CharacterAbility::ChargedRanged { energy_cost: _, energy_drain, @@ -497,6 +574,35 @@ impl From<&CharacterAbility> for CharacterState { initial_projectile_speed: *initial_projectile_speed, max_projectile_speed: *max_projectile_speed, }), + CharacterAbility::RepeaterRanged { + energy_cost: _, + movement_duration, + holdable, + prepare_duration, + recover_duration, + projectile, + projectile_body, + projectile_light, + projectile_gravity, + projectile_speed, + repetitions, + current_rep, + } => CharacterState::RepeaterRanged(repeater_ranged::Data { + exhausted: false, + prepare_timer: Duration::default(), + holdable: *holdable, + movement_duration: *movement_duration, + prepare_duration: *prepare_duration, + recover_duration: *recover_duration, + projectile: projectile.clone(), + projectile_body: *projectile_body, + projectile_light: *projectile_light, + projectile_gravity: *projectile_gravity, + projectile_speed: *projectile_speed, + repetitions: *repetitions, + current_rep: *current_rep, + initialize: true, + }), CharacterAbility::GroundShockwave { energy_cost: _, buildup_duration, diff --git a/common/src/comp/character_state.rs b/common/src/comp/character_state.rs index 4e99d35469..4d68df5696 100644 --- a/common/src/comp/character_state.rs +++ b/common/src/comp/character_state.rs @@ -69,6 +69,10 @@ pub enum CharacterState { SpinMelee(spin_melee::Data), /// A charged ranged attack (e.g. bow) ChargedRanged(charged_ranged::Data), + /// A charged melee attack + ChargedMelee(charged_melee::Data), + /// A repeating ranged attack + RepeaterRanged(repeater_ranged::Data), /// A ground shockwave attack GroundShockwave(ground_shockwave::Data), /// A continuous attack that affects all creatures in a cone originating @@ -87,7 +91,9 @@ impl CharacterState { | CharacterState::BasicBlock | CharacterState::LeapMelee(_) | CharacterState::SpinMelee(_) + | CharacterState::ChargedMelee(_) | CharacterState::ChargedRanged(_) + | CharacterState::RepeaterRanged(_) | CharacterState::GroundShockwave(_) | CharacterState::BasicBeam(_) ) @@ -101,7 +107,9 @@ impl CharacterState { | CharacterState::ComboMelee(_) | CharacterState::LeapMelee(_) | CharacterState::SpinMelee(_) + | CharacterState::ChargedMelee(_) | CharacterState::ChargedRanged(_) + | CharacterState::RepeaterRanged(_) | CharacterState::GroundShockwave(_) | CharacterState::BasicBeam(_) ) @@ -115,7 +123,9 @@ impl CharacterState { | CharacterState::ComboMelee(_) | CharacterState::BasicBlock | CharacterState::LeapMelee(_) + | CharacterState::ChargedMelee(_) | CharacterState::ChargedRanged(_) + | CharacterState::RepeaterRanged(_) | CharacterState::GroundShockwave(_) | CharacterState::BasicBeam(_) ) diff --git a/common/src/comp/inventory/item/tool.rs b/common/src/comp/inventory/item/tool.rs index c16bcd8061..2c9cde6b8a 100644 --- a/common/src/comp/inventory/item/tool.rs +++ b/common/src/comp/inventory/item/tool.rs @@ -226,6 +226,17 @@ impl Tool { forward_speed: 0.0, num_spins: 1, }, + LeapMelee { + energy_cost: 300, + movement_duration: Duration::from_millis(200), + buildup_duration: Duration::from_millis(1000), + recover_duration: Duration::from_millis(600), + base_damage: (170.0 * self.base_power()) as u32, + range: 3.5, + max_angle: 50.0, + leap_speed: 20.0, + leap_vert_speed: 16.0, + }, ], Hammer(_) => vec![ BasicMelee { @@ -237,12 +248,29 @@ impl Tool { range: 3.5, max_angle: 20.0, }, + ChargedMelee { + energy_cost: 0, + energy_drain: 300, + initial_damage: (20.0 * self.base_power()) as u32, + max_damage: (170.0 * self.base_power()) as u32, + initial_knockback: 12.0, + max_knockback: 60.0, + prepare_duration: Duration::from_millis(200), + charge_duration: Duration::from_millis(1200), + recover_duration: Duration::from_millis(500), + range: 3.5, + max_angle: 30.0, + }, LeapMelee { - energy_cost: 800, + energy_cost: 700, movement_duration: Duration::from_millis(500), buildup_duration: Duration::from_millis(1000), recover_duration: Duration::from_millis(100), base_damage: (240.0 * self.base_power()) as u32, + range: 4.5, + max_angle: 360.0, + leap_speed: 24.0, + leap_vert_speed: 8.0, }, ], Farming(_) => vec![BasicMelee { @@ -293,6 +321,31 @@ impl Tool { initial_projectile_speed: 100.0, max_projectile_speed: 500.0, }, + RepeaterRanged { + energy_cost: 400, + holdable: true, + movement_duration: Duration::from_millis(200), + prepare_duration: Duration::from_millis(1000), + recover_duration: Duration::from_millis(1000), + projectile: Projectile { + hit_solid: vec![projectile::Effect::Stick], + hit_entity: vec![ + projectile::Effect::Damage((-40.0 * self.base_power()) as i32), + projectile::Effect::Knockback(10.0), + projectile::Effect::RewardEnergy(50), + projectile::Effect::Vanish, + ], + time_left: Duration::from_secs(15), + owner: None, + ignore_group: true, + }, + projectile_body: Body::Object(object::Body::Arrow), + projectile_light: None, + projectile_gravity: Some(Gravity(0.2)), + projectile_speed: 100.0, + repetitions: 4, + current_rep: 0, + }, ], Dagger(_) => vec![BasicMelee { energy_cost: 0, diff --git a/common/src/states/charged_melee.rs b/common/src/states/charged_melee.rs new file mode 100644 index 0000000000..68b35f4b5d --- /dev/null +++ b/common/src/states/charged_melee.rs @@ -0,0 +1,171 @@ +use crate::{ + comp::{Attacking, CharacterState, EnergySource, StateUpdate}, + states::utils::*, + sys::character_behavior::*, +}; +use serde::{Deserialize, Serialize}; +use std::time::Duration; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + /// Whether the attack fired already + pub exhausted: bool, + /// How much energy is drained per second when charging + pub energy_drain: u32, + /// How much damage is dealt with no charge + pub initial_damage: u32, + /// How much damage is dealt with max charge + pub max_damage: u32, + /// How much knockback there is with no charge + pub initial_knockback: f32, + /// How much knockback there is at max charge + pub max_knockback: f32, + /// How long the weapon needs to be prepared for + pub prepare_duration: Duration, + /// How long it takes to charge the weapon to max damage and knockback + pub charge_duration: Duration, + /// How long the state has been charging + pub charge_timer: Duration, + /// How long the state has until exiting + pub recover_duration: Duration, + /// Max range + pub range: f32, + /// Max angle (45.0 will give you a 90.0 angle window) + pub max_angle: f32, +} + +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate::from(data); + + handle_move(data, &mut update, 0.3); + handle_jump(data, &mut update); + + if self.prepare_duration != Duration::default() { + // Prepare (draw the bow) + update.character = CharacterState::ChargedMelee(Data { + exhausted: self.exhausted, + energy_drain: self.energy_drain, + initial_damage: self.initial_damage, + max_damage: self.max_damage, + initial_knockback: self.initial_knockback, + max_knockback: self.max_knockback, + prepare_duration: self + .prepare_duration + .checked_sub(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + charge_duration: self.charge_duration, + charge_timer: self.charge_timer, + recover_duration: self.recover_duration, + range: self.range, + max_angle: self.max_angle, + }); + } else if data.inputs.secondary.is_pressed() + && self.charge_timer < self.charge_duration + && update.energy.current() > 0 + { + // Charge the attack + update.character = CharacterState::ChargedMelee(Data { + exhausted: self.exhausted, + energy_drain: self.energy_drain, + initial_damage: self.initial_damage, + max_damage: self.max_damage, + initial_knockback: self.initial_knockback, + max_knockback: self.max_knockback, + prepare_duration: self.prepare_duration, + charge_timer: self + .charge_timer + .checked_add(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + charge_duration: self.charge_duration, + recover_duration: self.recover_duration, + range: self.range, + max_angle: self.max_angle, + }); + + // Consumes energy if there's enough left and RMB is held down + update.energy.change_by( + -(self.energy_drain as f32 * data.dt.0) as i32, + EnergySource::Ability, + ); + } else if data.inputs.secondary.is_pressed() { + // Charge the attack + update.character = CharacterState::ChargedMelee(Data { + exhausted: self.exhausted, + energy_drain: self.energy_drain, + initial_damage: self.initial_damage, + max_damage: self.max_damage, + initial_knockback: self.initial_knockback, + max_knockback: self.max_knockback, + prepare_duration: self.prepare_duration, + charge_timer: self.charge_timer, + charge_duration: self.charge_duration, + recover_duration: self.recover_duration, + range: self.range, + max_angle: self.max_angle, + }); + + // Consumes energy if there's enough left and RMB is held down + update.energy.change_by( + -(self.energy_drain as f32 * data.dt.0 / 5.0) as i32, + EnergySource::Ability, + ); + } else if !self.exhausted { + let charge_amount = + (self.charge_timer.as_secs_f32() / self.charge_duration.as_secs_f32()).min(1.0); + let damage = self.initial_damage as f32 + (charge_amount * (self.max_damage - self.initial_damage) as f32); + // Hit attempt + data.updater.insert(data.entity, Attacking { + base_damage: damage as u32, + base_heal: 0, + range: self.range, + max_angle: self.max_angle.to_radians(), + applied: false, + hit_count: 0, + knockback: self.initial_knockback + + charge_amount * (self.max_knockback - self.initial_knockback), + }); + + update.character = CharacterState::ChargedMelee(Data { + exhausted: true, + energy_drain: self.energy_drain, + initial_damage: self.initial_damage, + max_damage: self.max_damage, + initial_knockback: self.initial_knockback, + max_knockback: self.max_knockback, + prepare_duration: self.prepare_duration, + charge_timer: self.charge_timer, + charge_duration: self.charge_duration, + recover_duration: self.recover_duration, + range: self.range, + max_angle: self.max_angle, + }); + } else if self.recover_duration != Duration::default() { + // Recovery + update.character = CharacterState::ChargedMelee(Data { + exhausted: self.exhausted, + energy_drain: self.energy_drain, + initial_damage: self.initial_damage, + max_damage: self.max_damage, + initial_knockback: self.initial_knockback, + max_knockback: self.max_knockback, + prepare_duration: self.prepare_duration, + charge_timer: self.charge_timer, + charge_duration: self.charge_duration, + recover_duration: self + .recover_duration + .checked_sub(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + range: self.range, + max_angle: self.max_angle, + }); + } else { + // Done + update.character = CharacterState::Wielding; + // Make sure attack component is removed + data.updater.remove::(data.entity); + } + + update + } +} diff --git a/common/src/states/leap_melee.rs b/common/src/states/leap_melee.rs index 5575c110b7..fbae6da4c0 100644 --- a/common/src/states/leap_melee.rs +++ b/common/src/states/leap_melee.rs @@ -7,9 +7,8 @@ use serde::{Deserialize, Serialize}; use std::time::Duration; use vek::Vec3; -const LEAP_SPEED: f32 = 24.0; - -#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] +#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] +//#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize, Eq, Hash)] pub struct Data { /// How long the state is moving pub movement_duration: Duration, @@ -21,6 +20,14 @@ pub struct Data { pub base_damage: u32, /// Whether the attack can deal more damage pub exhausted: bool, + /// Max range + pub range: f32, + /// Max angle (45.0 will give you a 90.0 angle window) + pub max_angle: f32, + /// Leap speed + pub leap_speed: f32, + /// Leap vertical speed? + pub leap_vert_speed: f32, pub initialize: bool, } @@ -37,13 +44,17 @@ impl CharacterBehavior for Data { if self.movement_duration != Duration::default() { // Jumping - update.vel.0 = Vec3::new(data.inputs.look_dir.x, data.inputs.look_dir.y, 8.0) - * ((self.movement_duration.as_millis() as f32) / 250.0) + //update.vel.0 = Vec3::new(data.inputs.look_dir.x, data.inputs.look_dir.y, 8.0) + update.vel.0 = Vec3::new( + data.inputs.look_dir.x, + data.inputs.look_dir.y, + self.leap_vert_speed, + ) * ((self.movement_duration.as_millis() as f32) / 250.0) + (update.vel.0 * Vec3::new(2.0, 2.0, 0.0) + 0.25 * data.inputs.move_dir.try_normalized().unwrap_or_default()) .try_normalized() .unwrap_or_default() - * LEAP_SPEED + * self.leap_speed * (1.0 - data.inputs.look_dir.z.abs()); update.character = CharacterState::LeapMelee(Data { @@ -55,6 +66,10 @@ impl CharacterBehavior for Data { recover_duration: self.recover_duration, base_damage: self.base_damage, exhausted: false, + range: self.range, + max_angle: self.max_angle, + leap_speed: self.leap_speed, + leap_vert_speed: self.leap_vert_speed, initialize: false, }); } else if self.buildup_duration != Duration::default() && !data.physics.on_ground { @@ -68,6 +83,10 @@ impl CharacterBehavior for Data { recover_duration: self.recover_duration, base_damage: self.base_damage, exhausted: false, + range: self.range, + max_angle: self.max_angle, + leap_speed: self.leap_speed, + leap_vert_speed: self.leap_vert_speed, initialize: false, }); } else if !self.exhausted { @@ -75,8 +94,9 @@ impl CharacterBehavior for Data { data.updater.insert(data.entity, Attacking { base_damage: self.base_damage, base_heal: 0, - range: 4.5, - max_angle: 360_f32.to_radians(), + range: self.range, + //range: 4.5, + max_angle: self.max_angle.to_radians(), applied: false, hit_count: 0, knockback: 25.0, @@ -88,6 +108,10 @@ impl CharacterBehavior for Data { recover_duration: self.recover_duration, base_damage: self.base_damage, exhausted: true, + range: self.range, + max_angle: self.max_angle, + leap_speed: self.leap_speed, + leap_vert_speed: self.leap_vert_speed, initialize: false, }); } else if self.recover_duration != Duration::default() { @@ -102,6 +126,10 @@ impl CharacterBehavior for Data { .unwrap_or_default(), base_damage: self.base_damage, exhausted: true, + range: self.range, + max_angle: self.max_angle, + leap_speed: self.leap_speed, + leap_vert_speed: self.leap_vert_speed, initialize: false, }); } else { diff --git a/common/src/states/mod.rs b/common/src/states/mod.rs index 85474faa90..a93df8c30c 100644 --- a/common/src/states/mod.rs +++ b/common/src/states/mod.rs @@ -3,6 +3,7 @@ pub mod basic_block; pub mod basic_melee; pub mod basic_ranged; pub mod boost; +pub mod charged_melee; pub mod charged_ranged; pub mod climb; pub mod combo_melee; @@ -14,6 +15,7 @@ pub mod glide_wield; pub mod ground_shockwave; pub mod idle; pub mod leap_melee; +pub mod repeater_ranged; pub mod roll; pub mod sit; pub mod sneak; diff --git a/common/src/states/repeater_ranged.rs b/common/src/states/repeater_ranged.rs new file mode 100644 index 0000000000..25d321aa29 --- /dev/null +++ b/common/src/states/repeater_ranged.rs @@ -0,0 +1,153 @@ +use crate::{ + comp::{Body, CharacterState, Gravity, LightEmitter, Projectile, StateUpdate}, + event::ServerEvent, + states::utils::*, + sys::character_behavior::*, +}; +use serde::{Deserialize, Serialize}; +use std::time::Duration; +use vek::Vec3; + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Data { + /// How long the state is moving + pub movement_duration: Duration, + /// Can you hold the ability beyond the prepare duration + pub holdable: bool, + /// How long we have to prepare the weapon + pub prepare_duration: Duration, + /// How long we prepared the weapon already + pub prepare_timer: Duration, + /// How long the state has until exiting + pub recover_duration: Duration, + pub projectile: Projectile, + pub projectile_body: Body, + pub projectile_light: Option, + pub projectile_gravity: Option, + pub projectile_speed: f32, + /// Whether the attack fired already + pub exhausted: bool, + /// How many times to repeat + pub repetitions: u32, + /// Current repetition + pub current_rep: u32, + pub initialize: bool, +} + +impl CharacterBehavior for Data { + fn behavior(&self, data: &JoinData) -> StateUpdate { + let mut update = StateUpdate::from(data); + + handle_move(data, &mut update, 1.0); + handle_jump(data, &mut update); + + if !self.exhausted + && if self.holdable { + data.inputs.holding_ability_key() || self.prepare_timer < self.prepare_duration + } else { + self.prepare_timer < self.prepare_duration + } + { + // Prepare (draw the bow) + update.character = CharacterState::RepeaterRanged(Data { + movement_duration: self.movement_duration, + prepare_timer: self.prepare_timer + Duration::from_secs_f32(data.dt.0), + holdable: self.holdable, + prepare_duration: self.prepare_duration, + recover_duration: self.recover_duration, + projectile: self.projectile.clone(), + projectile_body: self.projectile_body, + projectile_light: self.projectile_light, + projectile_gravity: self.projectile_gravity, + projectile_speed: self.projectile_speed, + exhausted: false, + repetitions: self.repetitions, + current_rep: self.current_rep, + initialize: false, + }); + } else if self.movement_duration != Duration::default() { + // Jumping + update.vel.0 = Vec3::new(data.vel.0[0], data.vel.0[1], 10.0); + + update.character = CharacterState::RepeaterRanged(Data { + movement_duration: self + .movement_duration + .checked_sub(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + prepare_timer: self.prepare_timer, + holdable: self.holdable, + prepare_duration: self.prepare_duration, + recover_duration: self.recover_duration, + projectile: self.projectile.clone(), + projectile_body: self.projectile_body, + projectile_light: self.projectile_light, + projectile_gravity: self.projectile_gravity, + projectile_speed: self.projectile_speed, + exhausted: false, + repetitions: self.repetitions, + current_rep: self.current_rep, + initialize: false, + }); + } else if !self.exhausted && self.current_rep < self.repetitions { + let mut projectile = self.projectile.clone(); + projectile.owner = Some(*data.uid); + update.server_events.push_front(ServerEvent::Shoot { + entity: data.entity, + dir: data.inputs.look_dir, + body: self.projectile_body, + projectile, + light: self.projectile_light, + gravity: self.projectile_gravity, + speed: self.projectile_speed, + }); + + update.character = CharacterState::RepeaterRanged(Data { + movement_duration: self.movement_duration, + prepare_timer: self.prepare_timer, + holdable: self.holdable, + prepare_duration: self.prepare_duration, + //recover_duration: self.recover_duration, + recover_duration: self + .recover_duration + .checked_sub(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + projectile: self.projectile.clone(), + projectile_body: self.projectile_body, + projectile_light: self.projectile_light, + projectile_gravity: self.projectile_gravity, + projectile_speed: self.projectile_speed, + exhausted: false, + repetitions: self.repetitions, + current_rep: self.current_rep + 1, + initialize: false, + }); + } else if self.recover_duration != Duration::default() { + // Recovery + update.character = CharacterState::RepeaterRanged(Data { + movement_duration: Duration::default(), + prepare_timer: self.prepare_timer, + holdable: self.holdable, + prepare_duration: self.prepare_duration, + recover_duration: self + .recover_duration + .checked_sub(Duration::from_secs_f32(data.dt.0)) + .unwrap_or_default(), + projectile: self.projectile.clone(), + projectile_body: self.projectile_body, + projectile_light: self.projectile_light, + projectile_gravity: self.projectile_gravity, + projectile_speed: self.projectile_speed, + exhausted: true, + repetitions: self.repetitions, + current_rep: 0, + initialize: false, + }); + return update; + } else { + // Done + update.character = CharacterState::Wielding; + } + + update + } +} diff --git a/common/src/sys/character_behavior.rs b/common/src/sys/character_behavior.rs index 4ec197b19e..83aebf3912 100644 --- a/common/src/sys/character_behavior.rs +++ b/common/src/sys/character_behavior.rs @@ -261,7 +261,9 @@ impl<'a> System<'a> for Sys { CharacterState::DashMelee(data) => data.handle_event(&j, action), CharacterState::LeapMelee(data) => data.handle_event(&j, action), CharacterState::SpinMelee(data) => data.handle_event(&j, action), + CharacterState::ChargedMelee(data) => data.handle_event(&j, action), CharacterState::ChargedRanged(data) => data.handle_event(&j, action), + CharacterState::RepeaterRanged(data) => data.handle_event(&j, action), CharacterState::GroundShockwave(data) => data.handle_event(&j, action), CharacterState::BasicBeam(data) => data.handle_event(&j, action), }; @@ -291,7 +293,9 @@ impl<'a> System<'a> for Sys { CharacterState::DashMelee(data) => data.behavior(&j), CharacterState::LeapMelee(data) => data.behavior(&j), CharacterState::SpinMelee(data) => data.behavior(&j), + CharacterState::ChargedMelee(data) => data.behavior(&j), CharacterState::ChargedRanged(data) => data.behavior(&j), + CharacterState::RepeaterRanged(data) => data.behavior(&j), CharacterState::GroundShockwave(data) => data.behavior(&j), CharacterState::BasicBeam(data) => data.behavior(&j), }; diff --git a/common/src/sys/stats.rs b/common/src/sys/stats.rs index 9fe746effa..2590eb2f52 100644 --- a/common/src/sys/stats.rs +++ b/common/src/sys/stats.rs @@ -112,7 +112,9 @@ impl<'a> System<'a> for Sys { | CharacterState::SpinMelee { .. } | CharacterState::ComboMelee { .. } | CharacterState::BasicRanged { .. } + | CharacterState::ChargedMelee { .. } | CharacterState::ChargedRanged { .. } + | CharacterState::RepeaterRanged { .. } | CharacterState::GroundShockwave { .. } | CharacterState::BasicBeam { .. } => { if energy.get_unchecked().regen_rate != 0.0 { diff --git a/voxygen/src/anim/src/character/charge.rs b/voxygen/src/anim/src/character/charge.rs index 942bc0e2d8..df0a4dcdc4 100644 --- a/voxygen/src/anim/src/character/charge.rs +++ b/voxygen/src/anim/src/character/charge.rs @@ -147,6 +147,22 @@ impl Animation for ChargeAnimation { * Quaternion::rotation_z(stop * -0.6); next.control.scale = Vec3::one(); }, + Some(ToolKind::Hammer(_)) => { + next.l_hand.position = Vec3::new(-8.0, -2.0 + stop * -1.0, 13.0); + next.l_hand.orientation = Quaternion::rotation_x(2.1) + * Quaternion::rotation_y(0.7) + * Quaternion::rotation_z(-0.3); + next.l_hand.scale = Vec3::one() * 1.05; + next.r_hand.position = Vec3::new(-11.0, 2.0, 6.0); + next.r_hand.orientation = Quaternion::rotation_x(1.8) + * Quaternion::rotation_y(2.3) + * Quaternion::rotation_z(0.3); + next.r_hand.scale = Vec3::one() * 1.05; + next.main.position = Vec3::new(-12.0, 1.0, 4.0); + next.main.orientation = Quaternion::rotation_x(0.3) + * Quaternion::rotation_y(0.3) + * Quaternion::rotation_z(0.6); + }, _ => {}, } diff --git a/voxygen/src/anim/src/character/leapmelee.rs b/voxygen/src/anim/src/character/leapmelee.rs index dada5b978d..41d0739138 100644 --- a/voxygen/src/anim/src/character/leapmelee.rs +++ b/voxygen/src/anim/src/character/leapmelee.rs @@ -2,6 +2,7 @@ use super::{super::Animation, CharacterSkeleton, SkeletonAttr}; use common::comp::item::{Hands, ToolKind}; /* use std::f32::consts::PI; */ use super::super::vek::*; +use std::f32::consts::PI; pub struct LeapAnimation; @@ -31,6 +32,19 @@ impl Animation for LeapAnimation { .sqrt()) * ((anim_time as f32 * lab as f32 * 4.0).sin()); + // Spin stuff here + let foot = (((5.0) + / (1.1 + 3.9 * ((anim_time as f32 * lab as f32 * 10.32).sin()).powf(2.0 as f32))) + .sqrt()) + * ((anim_time as f32 * lab as f32 * 10.32).sin()); + + let decel = (anim_time as f32 * 16.0 * lab as f32).min(PI / 2.0).sin(); + + let spin = (anim_time as f32 * 2.8 * lab as f32).sin(); + let spinhalf = (anim_time as f32 * 1.4 * lab as f32).sin(); + + // end spin stuff + if let Some(ToolKind::Hammer(_)) = active_tool_kind { next.l_hand.position = Vec3::new(-12.0, 0.0, 0.0); next.l_hand.orientation = Quaternion::rotation_x(-0.0) * Quaternion::rotation_y(0.0); @@ -94,16 +108,119 @@ impl Animation for LeapAnimation { * Quaternion::rotation_y(0.0) * Quaternion::rotation_z(1.4 + slowersmooth * -0.4 + slower * 0.2); next.control.scale = Vec3::one(); + + next.lantern.position = Vec3::new( + skeleton_attr.lantern.0, + skeleton_attr.lantern.1, + skeleton_attr.lantern.2, + ); + next.glider.position = Vec3::new(0.0, 0.0, 10.0); + next.glider.scale = Vec3::one() * 0.0; + next.l_control.scale = Vec3::one(); + next.r_control.scale = Vec3::one(); + + next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z(0.0); + next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + } else if let Some(ToolKind::Axe(_)) = active_tool_kind { + //INTENTION: SWORD + 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, 0.0, 10.0); + next.main.position = Vec3::new(0.0, 0.0, -5.0); + next.main.orientation = Quaternion::rotation_x(1.6) + * Quaternion::rotation_y(0.0) + * Quaternion::rotation_z(-0.4); + next.main.scale = Vec3::one(); + + next.control.position = Vec3::new(-4.5 + spinhalf * 4.0, 11.0, 8.0); + next.control.orientation = Quaternion::rotation_x(0.6 + spinhalf * -3.3) + * Quaternion::rotation_y(0.2 + spin * -2.0) + * Quaternion::rotation_z(1.4 + spin * 0.1); + next.control.scale = Vec3::one(); + next.head.position = Vec3::new( + 0.0, + -2.0 + skeleton_attr.head.0 + spin * -0.8, + skeleton_attr.head.1, + ); + next.head.orientation = Quaternion::rotation_z(spin * -0.25) + * Quaternion::rotation_x(0.0 + spin * -0.1) + * Quaternion::rotation_y(spin * -0.2); + next.chest.position = Vec3::new(0.0, skeleton_attr.chest.0, skeleton_attr.chest.1); + next.chest.orientation = Quaternion::rotation_z(spin * 0.1) + * Quaternion::rotation_x(0.0 + spin * 0.1) + * Quaternion::rotation_y(decel * -0.2); + next.chest.scale = Vec3::one(); + + next.belt.position = Vec3::new(0.0, 0.0, -2.0); + next.belt.orientation = next.chest.orientation * -0.1; + next.belt.scale = Vec3::one(); + + next.shorts.position = Vec3::new(0.0, 0.0, -5.0); + next.belt.orientation = next.chest.orientation * -0.08; + next.shorts.scale = Vec3::one(); + next.torso.position = Vec3::new(0.0, 0.0, 0.1) * skeleton_attr.scaler; + next.torso.orientation = Quaternion::rotation_z((spin * 7.0).max(0.3)) + * Quaternion::rotation_x(0.0) + * Quaternion::rotation_y(0.3); + next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + + // Stuff after the branch in the spin animation file + + 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.lantern.position = Vec3::new( - skeleton_attr.lantern.0, - skeleton_attr.lantern.1, - skeleton_attr.lantern.2, - ); - next.glider.position = Vec3::new(0.0, 0.0, 10.0); - next.glider.scale = Vec3::one() * 0.0; - next.l_control.scale = Vec3::one(); - next.r_control.scale = Vec3::one(); + + //next.lantern.position = Vec3::new( + // skeleton_attr.lantern.0, + // skeleton_attr.lantern.1, + // skeleton_attr.lantern.2, + //); + //next.glider.position = Vec3::new(0.0, 0.0, 10.0); + //next.glider.scale = Vec3::one() * 0.0; + //next.l_control.scale = Vec3::one(); + //next.r_control.scale = Vec3::one(); next.second.scale = match ( active_tool_kind.map(|tk| tk.hands()), @@ -113,9 +230,9 @@ impl Animation for LeapAnimation { (_, _) => Vec3::zero(), }; - next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; - next.torso.orientation = Quaternion::rotation_z(0.0); - next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; + //next.torso.position = Vec3::new(0.0, 0.0, 0.0) * skeleton_attr.scaler; + //next.torso.orientation = Quaternion::rotation_z(0.0); + //next.torso.scale = Vec3::one() / 11.0 * skeleton_attr.scaler; next } } diff --git a/voxygen/src/hud/skillbar.rs b/voxygen/src/hud/skillbar.rs index 899581ba4f..a3b00ce5bb 100644 --- a/voxygen/src/hud/skillbar.rs +++ b/voxygen/src/hud/skillbar.rs @@ -724,6 +724,13 @@ impl<'a> Widget for Skillbar<'a> { Color::Rgba(0.3, 0.3, 0.3, 0.8) } }, + Some(ToolKind::Axe(_)) => { + if self.energy.current() as f64 >= 100.0 { + Color::Rgba(1.0, 1.0, 1.0, 1.0) + } else { + Color::Rgba(0.3, 0.3, 0.3, 0.8) + } + }, _ => Color::Rgba(1.0, 1.0, 1.0, 1.0), }) .set(state.ids.m2_content, ui); @@ -785,6 +792,14 @@ impl<'a> Widget for Skillbar<'a> { .map(|i| i.item.kind()) .and_then(|kind| match kind { ItemKind::Tool(Tool { kind, .. }) => match kind { + ToolKind::Hammer(_) => Some(( + "Smash of Doom", + "\nAn AOE attack with knockback. \nLeaps to position of \ + cursor.", + )), + ToolKind::Axe(_) => { + Some(("Spin Leap", "\nA slashing running spin leap.")) + }, ToolKind::Staff(_) => Some(( "Firebomb", "\nWhirls a big fireball into the air. \nExplodes the ground \