Commit 6566802b authored by Samuel Keiffer's avatar Samuel Keiffer

Merge branch 'sam/small-fixes' into 'master'

Sam/small fixes part 1

Closes #525, #410, #412, and #397

See merge request !1455
parents 5b19158d 52c93f61
Pipeline #209018679 passed with stages
in 33 minutes and 39 seconds
use crate::{
comp::{HealthChange, HealthSource, Loadout},
sync::Uid,
util::Dir,
};
use serde::{Deserialize, Serialize};
use vek::*;
pub const BLOCK_EFFICIENCY: f32 = 0.9;
/// Each section of this struct determines what damage is applied to a
/// particular target, using some identifier
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Damages {
/// Targets enemies, and all other creatures not in your group
pub enemy: Option<Damage>,
/// Targets people in the same group as you, and any pets you have
pub group: Option<Damage>,
}
impl Damages {
pub fn new(enemy: Option<Damage>, group: Option<Damage>) -> Self { Damages { enemy, group } }
pub fn get_damage(self, same_group: bool) -> Option<Damage> {
if same_group { self.group } else { self.enemy }
}
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Damage {
Melee(f32),
Healing(f32),
Projectile(f32),
Explosion(f32),
Falling(f32),
Shockwave(f32),
Energy(f32),
}
impl Damage {
pub fn modify_damage(
self,
block: bool,
loadout: Option<&Loadout>,
uid: Option<Uid>,
) -> HealthChange {
match self {
Damage::Melee(damage) => {
let mut damage = damage;
// Critical hit
let mut critdamage = 0.0;
if rand::random() {
critdamage = damage * 0.3;
}
// Block
if block {
damage *= 1.0 - BLOCK_EFFICIENCY
}
// Armor
let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction());
damage *= 1.0 - damage_reduction;
// Critical damage applies after armor for melee
if (damage_reduction - 1.0).abs() > f32::EPSILON {
damage += critdamage;
}
HealthChange {
amount: -damage as i32,
cause: HealthSource::Attack { by: uid.unwrap() },
}
},
Damage::Projectile(damage) => {
let mut damage = damage;
// Critical hit
if rand::random() {
damage *= 1.2;
}
// Block
if block {
damage *= 1.0 - BLOCK_EFFICIENCY
}
// Armor
let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction());
damage *= 1.0 - damage_reduction;
HealthChange {
amount: -damage as i32,
cause: HealthSource::Projectile { owner: uid },
}
},
Damage::Explosion(damage) => {
let mut damage = damage;
// Block
if block {
damage *= 1.0 - BLOCK_EFFICIENCY
}
// Armor
let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction());
damage *= 1.0 - damage_reduction;
HealthChange {
amount: -damage as i32,
cause: HealthSource::Explosion { owner: uid },
}
},
Damage::Shockwave(damage) => {
let mut damage = damage;
// Armor
let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction());
damage *= 1.0 - damage_reduction;
HealthChange {
amount: -damage as i32,
cause: HealthSource::Attack { by: uid.unwrap() },
}
},
Damage::Energy(damage) => {
let mut damage = damage;
// Armor
let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction());
damage *= 1.0 - damage_reduction;
HealthChange {
amount: -damage as i32,
cause: HealthSource::Energy { owner: uid },
}
},
Damage::Healing(heal) => HealthChange {
amount: heal as i32,
cause: HealthSource::Healing { by: uid },
},
Damage::Falling(damage) => {
let mut damage = damage;
// Armor
let damage_reduction = loadout.map_or(0.0, |l| l.get_damage_reduction());
if (damage_reduction - 1.0).abs() < f32::EPSILON {
damage = 0.0;
}
HealthChange {
amount: -damage as i32,
cause: HealthSource::World,
}
},
}
}
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub enum Knockback {
Away(f32),
Towards(f32),
Up(f32),
TowardsUp(f32),
}
impl Knockback {
pub fn calculate_impulse(self, dir: Dir) -> Vec3<f32> {
match self {
Knockback::Away(strength) => strength * *Dir::slerp(dir, Dir::new(Vec3::unit_z()), 0.5),
Knockback::Towards(strength) => {
strength * *Dir::slerp(-dir, Dir::new(Vec3::unit_z()), 0.5)
},
Knockback::Up(strength) => strength * Vec3::unit_z(),
Knockback::TowardsUp(strength) => {
strength * *Dir::slerp(-dir, Dir::new(Vec3::unit_z()), 0.85)
},
}
}
}
......@@ -8,6 +8,7 @@ use crate::{
*,
},
sys::character_behavior::JoinData,
Knockback,
};
use arraygen::Arraygen;
use serde::{Deserialize, Serialize};
......@@ -59,16 +60,16 @@ pub enum CharacterAbility {
BasicMelee {
energy_cost: u32,
buildup_duration: Duration,
swing_duration: Duration,
recover_duration: Duration,
base_healthchange: i32,
base_damage: u32,
knockback: f32,
range: f32,
max_angle: f32,
},
BasicRanged {
energy_cost: u32,
holdable: bool,
prepare_duration: Duration,
buildup_duration: Duration,
recover_duration: Duration,
projectile: Projectile,
projectile_body: Body,
......@@ -91,7 +92,7 @@ pub enum CharacterAbility {
reps_remaining: u32,
},
Boost {
duration: Duration,
movement_duration: Duration,
only_up: bool,
},
DashMelee {
......@@ -169,7 +170,7 @@ pub enum CharacterAbility {
max_damage: u32,
initial_knockback: f32,
max_knockback: f32,
prepare_duration: Duration,
buildup_duration: Duration,
charge_duration: Duration,
recover_duration: Duration,
projectile_body: Body,
......@@ -184,7 +185,7 @@ pub enum CharacterAbility {
swing_duration: Duration,
recover_duration: Duration,
damage: u32,
knockback: f32,
knockback: Knockback,
shockwave_angle: f32,
shockwave_vertical_angle: f32,
shockwave_speed: f32,
......@@ -234,10 +235,13 @@ impl CharacterAbility {
.energy
.try_change_by(-(*energy_cost as i32), EnergySource::Ability)
.is_ok(),
CharacterAbility::LeapMelee { energy_cost, .. } => update
.energy
.try_change_by(-(*energy_cost as i32), EnergySource::Ability)
.is_ok(),
CharacterAbility::LeapMelee { energy_cost, .. } => {
update.vel.0.z >= 0.0
&& update
.energy
.try_change_by(-(*energy_cost as i32), EnergySource::Ability)
.is_ok()
},
CharacterAbility::SpinMelee { energy_cost, .. } => update
.energy
.try_change_by(-(*energy_cost as i32), EnergySource::Ability)
......@@ -250,10 +254,15 @@ impl CharacterAbility {
.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::RepeaterRanged {
energy_cost, leap, ..
} => {
(leap.is_none() || update.vel.0.z >= 0.0)
&& update
.energy
.try_change_by(-(*energy_cost as i32), EnergySource::Ability)
.is_ok()
},
CharacterAbility::Shockwave { energy_cost, .. } => update
.energy
.try_change_by(-(*energy_cost as i32), EnergySource::Ability)
......@@ -356,24 +365,29 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState {
match ability {
CharacterAbility::BasicMelee {
buildup_duration,
swing_duration,
recover_duration,
base_healthchange,
base_damage,
knockback,
range,
max_angle,
energy_cost: _,
} => CharacterState::BasicMelee(basic_melee::Data {
static_data: basic_melee::StaticData {
buildup_duration: *buildup_duration,
swing_duration: *swing_duration,
recover_duration: *recover_duration,
base_damage: *base_damage,
knockback: *knockback,
range: *range,
max_angle: *max_angle,
},
timer: Duration::default(),
stage_section: StageSection::Buildup,
exhausted: false,
buildup_duration: *buildup_duration,
recover_duration: *recover_duration,
base_healthchange: *base_healthchange,
knockback: *knockback,
range: *range,
max_angle: *max_angle,
}),
CharacterAbility::BasicRanged {
holdable,
prepare_duration,
buildup_duration,
recover_duration,
projectile,
projectile_body,
......@@ -382,21 +396,29 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState {
projectile_speed,
energy_cost: _,
} => CharacterState::BasicRanged(basic_ranged::Data {
static_data: basic_ranged::StaticData {
buildup_duration: *buildup_duration,
recover_duration: *recover_duration,
projectile: projectile.clone(),
projectile_body: *projectile_body,
projectile_light: *projectile_light,
projectile_gravity: *projectile_gravity,
projectile_speed: *projectile_speed,
ability_key: key,
},
timer: Duration::default(),
stage_section: StageSection::Buildup,
exhausted: false,
prepare_timer: Duration::default(),
holdable: *holdable,
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,
ability_key: key,
}),
CharacterAbility::Boost { duration, only_up } => CharacterState::Boost(boost::Data {
duration: *duration,
only_up: *only_up,
CharacterAbility::Boost {
movement_duration,
only_up,
} => CharacterState::Boost(boost::Data {
static_data: boost::StaticData {
movement_duration: *movement_duration,
only_up: *only_up,
},
timer: Duration::default(),
}),
CharacterAbility::DashMelee {
energy_cost: _,
......@@ -431,14 +453,21 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState {
recover_duration: *recover_duration,
is_interruptible: *is_interruptible,
},
end_charge: false,
auto_charge: false,
timer: Duration::default(),
refresh_timer: Duration::default(),
stage_section: StageSection::Buildup,
exhausted: false,
}),
CharacterAbility::BasicBlock => CharacterState::BasicBlock,
CharacterAbility::Roll => CharacterState::Roll(roll::Data {
remaining_duration: Duration::from_millis(500),
static_data: roll::StaticData {
buildup_duration: Duration::from_millis(100),
movement_duration: Duration::from_millis(300),
recover_duration: Duration::from_millis(100),
},
timer: Duration::default(),
stage_section: StageSection::Buildup,
was_wielded: false, // false by default. utils might set it to true
}),
CharacterAbility::ComboMelee {
......@@ -566,7 +595,7 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState {
max_damage,
initial_knockback,
max_knockback,
prepare_duration,
buildup_duration,
charge_duration,
recover_duration,
projectile_body,
......@@ -575,21 +604,24 @@ impl From<(&CharacterAbility, AbilityKey)> for CharacterState {
initial_projectile_speed,
max_projectile_speed,
} => CharacterState::ChargedRanged(charged_ranged::Data {
static_data: charged_ranged::StaticData {
buildup_duration: *buildup_duration,
charge_duration: *charge_duration,
recover_duration: *recover_duration,
energy_drain: *energy_drain,
initial_damage: *initial_damage,
max_damage: *max_damage,
initial_knockback: *initial_knockback,
max_knockback: *max_knockback,
projectile_body: *projectile_body,
projectile_light: *projectile_light,
projectile_gravity: *projectile_gravity,
initial_projectile_speed: *initial_projectile_speed,
max_projectile_speed: *max_projectile_speed,
},
timer: Duration::default(),
stage_section: StageSection::Buildup,
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,
projectile_body: *projectile_body,
projectile_light: *projectile_light,
projectile_gravity: *projectile_gravity,
initial_projectile_speed: *initial_projectile_speed,
max_projectile_speed: *max_projectile_speed,
}),
CharacterAbility::RepeaterRanged {
energy_cost: _,
......
use crate::sync::Uid;
use crate::{sync::Uid, Damages};
use serde::{Deserialize, Serialize};
use specs::{Component, FlaggedStorage};
use specs_idvs::IdvStorage;
......@@ -8,8 +8,7 @@ use std::time::Duration;
pub struct Properties {
pub angle: f32,
pub speed: f32,
pub damage: u32,
pub heal: u32,
pub damages: Damages,
pub lifesteal_eff: f32,
pub energy_regen: u32,
pub energy_cost: u32,
......
......@@ -3,6 +3,7 @@ use crate::{
event::{LocalEvent, ServerEvent},
states::*,
sys::character_behavior::JoinData,
Damages, Knockback,
};
use serde::{Deserialize, Serialize};
use specs::{Component, FlaggedStorage, VecStorage};
......@@ -152,13 +153,12 @@ impl Component for CharacterState {
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Attacking {
pub base_damage: u32,
pub base_heal: u32,
pub damages: Damages,
pub range: f32,
pub max_angle: f32,
pub applied: bool,
pub hit_count: u32,
pub knockback: f32,
pub knockback: Knockback,
}
impl Component for Attacking {
......
use crate::comp::Loadout;
use serde::{Deserialize, Serialize};
pub const BLOCK_EFFICIENCY: f32 = 0.9;
pub struct Damage {
pub healthchange: f32,
pub source: DamageSource,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum DamageSource {
Melee,
Healing,
Projectile,
Explosion,
Falling,
Shockwave,
Energy,
}
impl Damage {
pub fn modify_damage(&mut self, block: bool, loadout: &Loadout) {
match self.source {
DamageSource::Melee => {
// Critical hit
let mut critdamage = 0.0;
if rand::random() {
critdamage = self.healthchange * 0.3;
}
// Block
if block {
self.healthchange *= 1.0 - BLOCK_EFFICIENCY
}
// Armor
let damage_reduction = loadout.get_damage_reduction();
self.healthchange *= 1.0 - damage_reduction;
// Critical damage applies after armor for melee
if (damage_reduction - 1.0).abs() > f32::EPSILON {
self.healthchange += critdamage;
}
},
DamageSource::Projectile => {
// Critical hit
if rand::random() {
self.healthchange *= 1.2;
}
// Block
if block {
self.healthchange *= 1.0 - BLOCK_EFFICIENCY
}
// Armor
let damage_reduction = loadout.get_damage_reduction();
self.healthchange *= 1.0 - damage_reduction;
},
DamageSource::Explosion => {
// Block
if block {
self.healthchange *= 1.0 - BLOCK_EFFICIENCY
}
// Armor
let damage_reduction = loadout.get_damage_reduction();
self.healthchange *= 1.0 - damage_reduction;
},
DamageSource::Shockwave => {
// Armor
let damage_reduction = loadout.get_damage_reduction();
self.healthchange *= 1.0 - damage_reduction;
},
DamageSource::Energy => {
// Armor
let damage_reduction = loadout.get_damage_reduction();
self.healthchange *= 1.0 - damage_reduction;
},
_ => {},
}
}
}
......@@ -4,7 +4,7 @@
use crate::{
comp::{body::object, projectile, Body, CharacterAbility, Gravity, LightEmitter, Projectile},
states::combo_melee,
Explosion,
Damage, Damages, Explosion, Knockback,
};
use serde::{Deserialize, Serialize};
use std::time::Duration;
......@@ -174,15 +174,15 @@ impl Tool {
DashMelee {
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,
max_damage: (240.0 * self.base_power()) as u32,
base_knockback: 8.0,
max_knockback: 15.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),
charge_duration: Duration::from_millis(600),
swing_duration: Duration::from_millis(100),
recover_duration: Duration::from_millis(500),
infinite_charge: true,
......@@ -206,9 +206,10 @@ impl Tool {
Axe(_) => vec![
BasicMelee {
energy_cost: 0,
buildup_duration: Duration::from_millis(700),
buildup_duration: Duration::from_millis(600),
swing_duration: Duration::from_millis(100),
recover_duration: Duration::from_millis(300),
base_healthchange: (-120.0 * self.base_power()) as i32,
base_damage: (120.0 * self.base_power()) as u32,
knockback: 0.0,
range: 3.5,
max_angle: 20.0,
......@@ -244,9 +245,10 @@ impl Tool {
Hammer(_) => vec![
BasicMelee {
energy_cost: 0,
buildup_duration: Duration::from_millis(700),
buildup_duration: Duration::from_millis(600),
swing_duration: Duration::from_millis(100),
recover_duration: Duration::from_millis(300),
base_healthchange: (-120.0 * self.base_power()) as i32,
base_damage: (120.0 * self.base_power()) as u32,
knockback: 0.0,
range: 3.5,