mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Finish rooted debuff impl and Lung Pummel
This commit is contained in:
parent
76dc196996
commit
38c74bf182
@ -233,7 +233,7 @@
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
Simple(Hammer(PileDriver), "common.abilities.hammer.pile_driver"),
|
Simple(Hammer(PileDriver), "common.abilities.hammer.pile_driver"),
|
||||||
// Simple(Hammer(LungPummel), "common.abilities.hammer.lung_pummel"),
|
Simple(Hammer(LungPummel), "common.abilities.hammer.lung_pummel"),
|
||||||
// Simple(Hammer(HelmCrusher), "common.abilities.hammer.helm_crusher"),
|
// Simple(Hammer(HelmCrusher), "common.abilities.hammer.helm_crusher"),
|
||||||
// Simple(Hammer(Rampart), "common.abilities.hammer.rampart"),
|
// Simple(Hammer(Rampart), "common.abilities.hammer.rampart"),
|
||||||
// Simple(Hammer(Tenacity), "common.abilities.hammer.tenacity"),
|
// Simple(Hammer(Tenacity), "common.abilities.hammer.tenacity"),
|
||||||
|
@ -14,7 +14,7 @@ BasicMelee(
|
|||||||
range: 3.0,
|
range: 3.0,
|
||||||
angle: 15.0,
|
angle: 15.0,
|
||||||
attack_effect: Some((Poise(40), TargetBlocking)),
|
attack_effect: Some((Poise(40), TargetBlocking)),
|
||||||
precision_flank_multiplier: 1.5,
|
precision_flank_multipliers: (back: 1.0, side: 1.0, front: 1.5),
|
||||||
precision_flank_invert: true,
|
precision_flank_invert: true,
|
||||||
),
|
),
|
||||||
ori_modifier: 0.6,
|
ori_modifier: 0.6,
|
||||||
|
25
assets/common/abilities/hammer/lung_pummel.ron
Normal file
25
assets/common/abilities/hammer/lung_pummel.ron
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
FinisherMelee(
|
||||||
|
energy_cost: 0,
|
||||||
|
buildup_duration: 0.15,
|
||||||
|
swing_duration: 0.15,
|
||||||
|
recover_duration: 0.2,
|
||||||
|
melee_constructor: (
|
||||||
|
kind: Bash(
|
||||||
|
damage: 20,
|
||||||
|
poise: 20,
|
||||||
|
knockback: 12,
|
||||||
|
energy_regen: 0,
|
||||||
|
),
|
||||||
|
range: 4.0,
|
||||||
|
angle: 60.0,
|
||||||
|
damage_effect: Some(Buff((
|
||||||
|
kind: Winded,
|
||||||
|
dur_secs: 8,
|
||||||
|
strength: Value(1.0),
|
||||||
|
chance: 1.0,
|
||||||
|
))),
|
||||||
|
precision_flank_multipliers: (front: 1.0, side: 2.0, back: 1.0),
|
||||||
|
),
|
||||||
|
minimum_combo: 0,
|
||||||
|
combo_consumption: Cost,
|
||||||
|
)
|
@ -13,7 +13,7 @@ FinisherMelee(
|
|||||||
range: 4.0,
|
range: 4.0,
|
||||||
angle: 15.0,
|
angle: 15.0,
|
||||||
attack_effect: Some((Poise(50), BehindTarget)),
|
attack_effect: Some((Poise(50), BehindTarget)),
|
||||||
precision_flank_multiplier: 2.0,
|
precision_flank_multipliers: (front: 1.0, side: 1.0, back: 2.0),
|
||||||
),
|
),
|
||||||
minimum_combo: 10,
|
minimum_combo: 10,
|
||||||
combo_consumption: Cost,
|
combo_consumption: Cost,
|
||||||
|
BIN
assets/voxygen/element/de_buffs/debuff_winded.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/de_buffs/debuff_winded.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
assets/voxygen/element/skills/hammer/lung_pummel.png
(Stored with Git LFS)
Normal file
BIN
assets/voxygen/element/skills/hammer/lung_pummel.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -127,6 +127,9 @@ buff-scornfultaunt = Scornful Taunt
|
|||||||
## Rooted
|
## Rooted
|
||||||
buff-rooted = Rooted
|
buff-rooted = Rooted
|
||||||
.desc = You are stuck in place and cannot move.
|
.desc = You are stuck in place and cannot move.
|
||||||
|
## Winded
|
||||||
|
buff-winded = Winded
|
||||||
|
.desc = You can barely breathe hampering how much energy you can recover and how quickly you can move.
|
||||||
## Util
|
## Util
|
||||||
buff-text-over_seconds = over { $dur_secs } seconds
|
buff-text-over_seconds = over { $dur_secs } seconds
|
||||||
buff-text-for_seconds = for { $dur_secs } seconds
|
buff-text-for_seconds = for { $dur_secs } seconds
|
||||||
|
@ -416,3 +416,6 @@ common-abilities-hammer-breach = Breach
|
|||||||
common-abilities-hammer-pile_driver = Pile Driver
|
common-abilities-hammer-pile_driver = Pile Driver
|
||||||
.desc =
|
.desc =
|
||||||
Strike your enemy into the ground, stopping their movement until they free their legs.
|
Strike your enemy into the ground, stopping their movement until they free their legs.
|
||||||
|
common-abilities-hammer-lung_pummel = Lung Pummel
|
||||||
|
.desc =
|
||||||
|
Swipe your hammer into your foe's side, winding them.
|
||||||
|
@ -191,6 +191,7 @@ lazy_static! {
|
|||||||
BuffKind::Heatstroke => "heatstroke",
|
BuffKind::Heatstroke => "heatstroke",
|
||||||
BuffKind::ScornfulTaunt => "scornful_taunt",
|
BuffKind::ScornfulTaunt => "scornful_taunt",
|
||||||
BuffKind::Rooted => "rooted",
|
BuffKind::Rooted => "rooted",
|
||||||
|
BuffKind::Winded => "winded",
|
||||||
};
|
};
|
||||||
let mut buff_parser = HashMap::new();
|
let mut buff_parser = HashMap::new();
|
||||||
for kind in BuffKind::iter() {
|
for kind in BuffKind::iter() {
|
||||||
|
@ -2,7 +2,7 @@ use crate::{
|
|||||||
comp::{
|
comp::{
|
||||||
ability::Capability,
|
ability::Capability,
|
||||||
aura::{AuraKindVariant, EnteredAuras},
|
aura::{AuraKindVariant, EnteredAuras},
|
||||||
buff::{Buff, BuffChange, BuffData, BuffKind, BuffSource},
|
buff::{Buff, BuffChange, BuffData, BuffKind, BuffSource, DestInfo},
|
||||||
inventory::{
|
inventory::{
|
||||||
item::{
|
item::{
|
||||||
armor::Protection,
|
armor::Protection,
|
||||||
@ -13,7 +13,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
skillset::SkillGroupKind,
|
skillset::SkillGroupKind,
|
||||||
Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, HealthChange,
|
Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, HealthChange,
|
||||||
Inventory, Ori, Player, Poise, PoiseChange, SkillSet, Stats,
|
Inventory, Mass, Ori, Player, Poise, PoiseChange, SkillSet, Stats,
|
||||||
},
|
},
|
||||||
event::{
|
event::{
|
||||||
BuffEvent, ComboChangeEvent, EmitExt, EnergyChangeEvent, EntityAttackedHookEvent,
|
BuffEvent, ComboChangeEvent, EmitExt, EnergyChangeEvent, EntityAttackedHookEvent,
|
||||||
@ -72,6 +72,7 @@ pub struct AttackerInfo<'a> {
|
|||||||
pub combo: Option<&'a Combo>,
|
pub combo: Option<&'a Combo>,
|
||||||
pub inventory: Option<&'a Inventory>,
|
pub inventory: Option<&'a Inventory>,
|
||||||
pub stats: Option<&'a Stats>,
|
pub stats: Option<&'a Stats>,
|
||||||
|
pub mass: Option<&'a Mass>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TargetInfo<'a> {
|
pub struct TargetInfo<'a> {
|
||||||
@ -85,6 +86,7 @@ pub struct TargetInfo<'a> {
|
|||||||
pub char_state: Option<&'a CharacterState>,
|
pub char_state: Option<&'a CharacterState>,
|
||||||
pub energy: Option<&'a Energy>,
|
pub energy: Option<&'a Energy>,
|
||||||
pub buffs: Option<&'a Buffs>,
|
pub buffs: Option<&'a Buffs>,
|
||||||
|
pub mass: Option<&'a Mass>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
@ -464,8 +466,8 @@ impl Attack {
|
|||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
buff_change: BuffChange::Add(b.to_buff(
|
buff_change: BuffChange::Add(b.to_buff(
|
||||||
time,
|
time,
|
||||||
attacker.map(|a| a.uid),
|
attacker,
|
||||||
target.stats,
|
target,
|
||||||
applied_damage,
|
applied_damage,
|
||||||
strength_modifier,
|
strength_modifier,
|
||||||
)),
|
)),
|
||||||
@ -697,8 +699,8 @@ impl Attack {
|
|||||||
entity: target.entity,
|
entity: target.entity,
|
||||||
buff_change: BuffChange::Add(b.to_buff(
|
buff_change: BuffChange::Add(b.to_buff(
|
||||||
time,
|
time,
|
||||||
attacker.map(|a| a.uid),
|
attacker,
|
||||||
target.stats,
|
target,
|
||||||
accumulated_damage,
|
accumulated_damage,
|
||||||
strength_modifier,
|
strength_modifier,
|
||||||
)),
|
)),
|
||||||
@ -1322,17 +1324,21 @@ impl CombatBuff {
|
|||||||
fn to_buff(
|
fn to_buff(
|
||||||
self,
|
self,
|
||||||
time: Time,
|
time: Time,
|
||||||
uid: Option<Uid>,
|
attacker_info: Option<AttackerInfo>,
|
||||||
tgt_stats: Option<&Stats>,
|
target_info: &TargetInfo,
|
||||||
damage: f32,
|
damage: f32,
|
||||||
strength_modifier: f32,
|
strength_modifier: f32,
|
||||||
) -> Buff {
|
) -> Buff {
|
||||||
// TODO: Generate BufCategoryId vec (probably requires damage overhaul?)
|
// TODO: Generate BufCategoryId vec (probably requires damage overhaul?)
|
||||||
let source = if let Some(uid) = uid {
|
let source = if let Some(uid) = attacker_info.map(|a| a.uid) {
|
||||||
BuffSource::Character { by: uid }
|
BuffSource::Character { by: uid }
|
||||||
} else {
|
} else {
|
||||||
BuffSource::Unknown
|
BuffSource::Unknown
|
||||||
};
|
};
|
||||||
|
let dest_info = DestInfo {
|
||||||
|
stats: target_info.stats,
|
||||||
|
mass: target_info.mass,
|
||||||
|
};
|
||||||
Buff::new(
|
Buff::new(
|
||||||
self.kind,
|
self.kind,
|
||||||
BuffData::new(
|
BuffData::new(
|
||||||
@ -1342,7 +1348,8 @@ impl CombatBuff {
|
|||||||
Vec::new(),
|
Vec::new(),
|
||||||
source,
|
source,
|
||||||
time,
|
time,
|
||||||
tgt_stats,
|
dest_info,
|
||||||
|
attacker_info.and_then(|a| a.mass),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1646,7 +1653,7 @@ pub fn compute_poise_resilience(
|
|||||||
pub fn precision_mult_from_flank(
|
pub fn precision_mult_from_flank(
|
||||||
attack_dir: Vec3<f32>,
|
attack_dir: Vec3<f32>,
|
||||||
target_ori: Option<&Ori>,
|
target_ori: Option<&Ori>,
|
||||||
precision_flank_multiplier: f32,
|
precision_flank_multipliers: FlankMults,
|
||||||
precision_flank_invert: bool,
|
precision_flank_invert: bool,
|
||||||
) -> Option<f32> {
|
) -> Option<f32> {
|
||||||
let angle = target_ori.map(|t_ori| {
|
let angle = target_ori.map(|t_ori| {
|
||||||
@ -1657,16 +1664,38 @@ pub fn precision_mult_from_flank(
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
match angle {
|
match angle {
|
||||||
Some(angle) if angle < FULL_FLANK_ANGLE => {
|
Some(angle) if angle < FULL_FLANK_ANGLE => Some(
|
||||||
Some(MAX_BACK_FLANK_PRECISION * precision_flank_multiplier)
|
MAX_BACK_FLANK_PRECISION
|
||||||
},
|
* if precision_flank_invert {
|
||||||
|
precision_flank_multipliers.front
|
||||||
|
} else {
|
||||||
|
precision_flank_multipliers.back
|
||||||
|
},
|
||||||
|
),
|
||||||
Some(angle) if angle < PARTIAL_FLANK_ANGLE => {
|
Some(angle) if angle < PARTIAL_FLANK_ANGLE => {
|
||||||
Some(MAX_SIDE_FLANK_PRECISION * precision_flank_multiplier)
|
Some(MAX_SIDE_FLANK_PRECISION * precision_flank_multipliers.side)
|
||||||
},
|
},
|
||||||
Some(_) | None => None,
|
Some(_) | None => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
pub struct FlankMults {
|
||||||
|
pub back: f32,
|
||||||
|
pub front: f32,
|
||||||
|
pub side: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for FlankMults {
|
||||||
|
fn default() -> Self {
|
||||||
|
FlankMults {
|
||||||
|
back: 1.0,
|
||||||
|
front: 1.0,
|
||||||
|
side: 1.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn block_strength(inventory: &Inventory, char_state: &CharacterState) -> f32 {
|
pub fn block_strength(inventory: &Inventory, char_state: &CharacterState) -> f32 {
|
||||||
match char_state {
|
match char_state {
|
||||||
CharacterState::BasicBlock(data) => data.static_data.block_strength,
|
CharacterState::BasicBlock(data) => data.static_data.block_strength,
|
||||||
|
@ -1113,7 +1113,7 @@ impl Default for CharacterAbility {
|
|||||||
attack_effect: None,
|
attack_effect: None,
|
||||||
simultaneous_hits: 1,
|
simultaneous_hits: 1,
|
||||||
custom_combo: None,
|
custom_combo: None,
|
||||||
precision_flank_multiplier: 1.0,
|
precision_flank_multipliers: Default::default(),
|
||||||
precision_flank_invert: false,
|
precision_flank_invert: false,
|
||||||
},
|
},
|
||||||
ori_modifier: 1.0,
|
ori_modifier: 1.0,
|
||||||
|
@ -4,7 +4,7 @@ use crate::{
|
|||||||
AttackEffect, CombatBuff, CombatBuffStrength, CombatEffect, CombatRequirement,
|
AttackEffect, CombatBuff, CombatBuffStrength, CombatEffect, CombatRequirement,
|
||||||
DamagedEffect, DeathEffect,
|
DamagedEffect, DeathEffect,
|
||||||
},
|
},
|
||||||
comp::{aura::AuraKey, Stats},
|
comp::{aura::AuraKey, Mass, Stats},
|
||||||
resources::{Secs, Time},
|
resources::{Secs, Time},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
};
|
};
|
||||||
@ -185,6 +185,11 @@ pub enum BuffKind {
|
|||||||
/// is higher than the strength allows for, duration gets reduced using a
|
/// is higher than the strength allows for, duration gets reduced using a
|
||||||
/// mutiplier from the ratio of masses.
|
/// mutiplier from the ratio of masses.
|
||||||
Rooted,
|
Rooted,
|
||||||
|
/// Slows movement speed and reduces energy reward
|
||||||
|
/// Both scale non-linearly with strength, 0.5 leads to 50% reduction of
|
||||||
|
/// energy reward and 33% reduction of move speed. 1.0 leads to 67%
|
||||||
|
/// reduction of energy reward and 50% reduction of move speed.
|
||||||
|
Winded,
|
||||||
// Complex, non-obvious buffs
|
// Complex, non-obvious buffs
|
||||||
/// Changed into another body.
|
/// Changed into another body.
|
||||||
Polymorphed,
|
Polymorphed,
|
||||||
@ -248,7 +253,8 @@ impl BuffKind {
|
|||||||
| BuffKind::Parried
|
| BuffKind::Parried
|
||||||
| BuffKind::PotionSickness
|
| BuffKind::PotionSickness
|
||||||
| BuffKind::Heatstroke
|
| BuffKind::Heatstroke
|
||||||
| BuffKind::Rooted => BuffDescriptor::SimpleNegative,
|
| BuffKind::Rooted
|
||||||
|
| BuffKind::Winded => BuffDescriptor::SimpleNegative,
|
||||||
BuffKind::Polymorphed => BuffDescriptor::Complex,
|
BuffKind::Polymorphed => BuffDescriptor::Complex,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -286,7 +292,10 @@ impl BuffKind {
|
|||||||
|
|
||||||
pub fn effects(&self, data: &BuffData, stats: Option<&Stats>) -> Vec<BuffEffect> {
|
pub fn effects(&self, data: &BuffData, stats: Option<&Stats>) -> Vec<BuffEffect> {
|
||||||
// Normalized nonlinear scaling
|
// Normalized nonlinear scaling
|
||||||
|
// TODO: Do we want to make denominator term parameterized. Come back to if we
|
||||||
|
// add nn_scaling3.
|
||||||
let nn_scaling = |a| a / (a + 0.5);
|
let nn_scaling = |a| a / (a + 0.5);
|
||||||
|
let nn_scaling2 = |a| a / (a + 1.0);
|
||||||
let instance = rand::random();
|
let instance = rand::random();
|
||||||
match self {
|
match self {
|
||||||
BuffKind::Bleeding => vec![BuffEffect::HealthChangeOverTime {
|
BuffKind::Bleeding => vec![BuffEffect::HealthChangeOverTime {
|
||||||
@ -489,6 +498,10 @@ impl BuffKind {
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
BuffKind::Rooted => vec![BuffEffect::MovementSpeed(0.0)],
|
BuffKind::Rooted => vec![BuffEffect::MovementSpeed(0.0)],
|
||||||
|
BuffKind::Winded => vec![
|
||||||
|
BuffEffect::MovementSpeed(1.0 - nn_scaling2(data.strength)),
|
||||||
|
BuffEffect::EnergyReward(1.0 - nn_scaling(data.strength)),
|
||||||
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -504,13 +517,18 @@ impl BuffKind {
|
|||||||
cat_ids
|
cat_ids
|
||||||
}
|
}
|
||||||
|
|
||||||
fn modify_data(&self, mut data: BuffData) -> BuffData {
|
fn modify_data(
|
||||||
|
&self,
|
||||||
|
mut data: BuffData,
|
||||||
|
source_mass: Option<&Mass>,
|
||||||
|
dest_mass: Option<&Mass>,
|
||||||
|
) -> BuffData {
|
||||||
// TODO: Remove clippy allow after another buff needs this
|
// TODO: Remove clippy allow after another buff needs this
|
||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
match self {
|
match self {
|
||||||
BuffKind::Rooted => {
|
BuffKind::Rooted => {
|
||||||
let source_mass = 50.0;
|
let source_mass = source_mass.map_or(50.0, |m| m.0 as f64);
|
||||||
let dest_mass = 50.0_f64;
|
let dest_mass = dest_mass.map_or(50.0, |m| m.0 as f64);
|
||||||
let ratio = (source_mass / dest_mass).min(1.0);
|
let ratio = (source_mass / dest_mass).min(1.0);
|
||||||
data.duration = data.duration.map(|dur| Secs(dur.0 * ratio));
|
data.duration = data.duration.map(|dur| Secs(dur.0 * ratio));
|
||||||
},
|
},
|
||||||
@ -710,10 +728,12 @@ impl Buff {
|
|||||||
cat_ids: Vec<BuffCategory>,
|
cat_ids: Vec<BuffCategory>,
|
||||||
source: BuffSource,
|
source: BuffSource,
|
||||||
time: Time,
|
time: Time,
|
||||||
stats: Option<&Stats>,
|
dest_info: DestInfo,
|
||||||
|
// Create source_info if we need more parameters from source
|
||||||
|
source_mass: Option<&Mass>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let data = kind.modify_data(data);
|
let data = kind.modify_data(data, source_mass, dest_info.mass);
|
||||||
let effects = kind.effects(&data, stats);
|
let effects = kind.effects(&data, dest_info.stats);
|
||||||
let cat_ids = kind.extend_cat_ids(cat_ids);
|
let cat_ids = kind.extend_cat_ids(cat_ids);
|
||||||
let start_time = Time(time.0 + data.delay.map_or(0.0, |delay| delay.0));
|
let start_time = Time(time.0 + data.delay.map_or(0.0, |delay| delay.0));
|
||||||
let end_time = if cat_ids
|
let end_time = if cat_ids
|
||||||
@ -958,6 +978,12 @@ impl Component for Buffs {
|
|||||||
type Storage = DerefFlaggedStorage<Self, VecStorage<Self>>;
|
type Storage = DerefFlaggedStorage<Self, VecStorage<Self>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Copy, Clone)]
|
||||||
|
pub struct DestInfo<'a> {
|
||||||
|
pub stats: Option<&'a Stats>,
|
||||||
|
pub mass: Option<&'a Mass>,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use crate::comp::buff::*;
|
use crate::comp::buff::*;
|
||||||
@ -973,6 +999,7 @@ pub mod tests {
|
|||||||
Vec::new(),
|
Vec::new(),
|
||||||
BuffSource::Unknown,
|
BuffSource::Unknown,
|
||||||
time,
|
time,
|
||||||
|
DestInfo::default(),
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
combat::{
|
combat::{
|
||||||
Attack, AttackDamage, AttackEffect, CombatBuff, CombatBuffStrength, CombatEffect,
|
Attack, AttackDamage, AttackEffect, CombatBuff, CombatBuffStrength, CombatEffect,
|
||||||
CombatRequirement, Damage, DamageKind, DamageSource, GroupTarget, Knockback, KnockbackDir,
|
CombatRequirement, Damage, DamageKind, DamageSource, FlankMults, GroupTarget, Knockback,
|
||||||
|
KnockbackDir,
|
||||||
},
|
},
|
||||||
comp::{
|
comp::{
|
||||||
buff::BuffKind,
|
buff::BuffKind,
|
||||||
@ -23,7 +24,7 @@ pub struct Melee {
|
|||||||
pub multi_target: Option<MultiTarget>,
|
pub multi_target: Option<MultiTarget>,
|
||||||
pub break_block: Option<(Vec3<i32>, Option<ToolKind>)>,
|
pub break_block: Option<(Vec3<i32>, Option<ToolKind>)>,
|
||||||
pub simultaneous_hits: u32,
|
pub simultaneous_hits: u32,
|
||||||
pub precision_flank_multiplier: f32,
|
pub precision_flank_multipliers: FlankMults,
|
||||||
pub precision_flank_invert: bool,
|
pub precision_flank_invert: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +54,6 @@ impl Component for Melee {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn default_simultaneous_hits() -> u32 { 1 }
|
fn default_simultaneous_hits() -> u32 { 1 }
|
||||||
fn default_precision_flank_multiplier() -> f32 { 1.0 }
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Scaled {
|
pub struct Scaled {
|
||||||
@ -80,8 +80,8 @@ pub struct MeleeConstructor {
|
|||||||
#[serde(default = "default_simultaneous_hits")]
|
#[serde(default = "default_simultaneous_hits")]
|
||||||
pub simultaneous_hits: u32,
|
pub simultaneous_hits: u32,
|
||||||
pub custom_combo: Option<CustomCombo>,
|
pub custom_combo: Option<CustomCombo>,
|
||||||
#[serde(default = "default_precision_flank_multiplier")]
|
#[serde(default)]
|
||||||
pub precision_flank_multiplier: f32,
|
pub precision_flank_multipliers: FlankMults,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub precision_flank_invert: bool,
|
pub precision_flank_invert: bool,
|
||||||
}
|
}
|
||||||
@ -393,7 +393,7 @@ impl MeleeConstructor {
|
|||||||
multi_target: self.multi_target,
|
multi_target: self.multi_target,
|
||||||
break_block: None,
|
break_block: None,
|
||||||
simultaneous_hits: self.simultaneous_hits,
|
simultaneous_hits: self.simultaneous_hits,
|
||||||
precision_flank_multiplier: self.precision_flank_multiplier,
|
precision_flank_multipliers: self.precision_flank_multipliers,
|
||||||
precision_flank_invert: self.precision_flank_invert,
|
precision_flank_invert: self.precision_flank_invert,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
comp::{
|
comp::{
|
||||||
buff::{Buff, BuffCategory, BuffChange, BuffData, BuffKind, BuffSource},
|
buff::{Buff, BuffCategory, BuffChange, BuffData, BuffKind, BuffSource, DestInfo},
|
||||||
character_state::OutputEvents,
|
character_state::OutputEvents,
|
||||||
CharacterState, StateUpdate,
|
CharacterState, StateUpdate,
|
||||||
},
|
},
|
||||||
@ -116,6 +116,11 @@ impl CharacterBehavior for Data {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let dest_info = DestInfo {
|
||||||
|
stats: Some(data.stats),
|
||||||
|
mass: Some(data.mass),
|
||||||
|
};
|
||||||
|
|
||||||
// Creates buff
|
// Creates buff
|
||||||
let buff = Buff::new(
|
let buff = Buff::new(
|
||||||
self.static_data.buff_kind,
|
self.static_data.buff_kind,
|
||||||
@ -126,7 +131,8 @@ impl CharacterBehavior for Data {
|
|||||||
buff_cat_ids,
|
buff_cat_ids,
|
||||||
BuffSource::Character { by: *data.uid },
|
BuffSource::Character { by: *data.uid },
|
||||||
*data.time,
|
*data.time,
|
||||||
Some(data.stats),
|
dest_info,
|
||||||
|
Some(data.mass),
|
||||||
);
|
);
|
||||||
output_events.emit_server(BuffEvent {
|
output_events.emit_server(BuffEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
|
@ -3,7 +3,7 @@ use crate::{
|
|||||||
comp::{
|
comp::{
|
||||||
ability::{AbilityInitEvent, AbilityMeta, Capability, SpecifiedAbility, Stance},
|
ability::{AbilityInitEvent, AbilityMeta, Capability, SpecifiedAbility, Stance},
|
||||||
arthropod, biped_large, biped_small, bird_medium,
|
arthropod, biped_large, biped_small, bird_medium,
|
||||||
buff::{Buff, BuffCategory, BuffChange, BuffData, BuffSource},
|
buff::{Buff, BuffCategory, BuffChange, BuffData, BuffSource, DestInfo},
|
||||||
character_state::OutputEvents,
|
character_state::OutputEvents,
|
||||||
controller::InventoryManip,
|
controller::InventoryManip,
|
||||||
golem,
|
golem,
|
||||||
@ -1332,6 +1332,10 @@ fn handle_ability(
|
|||||||
strength,
|
strength,
|
||||||
duration,
|
duration,
|
||||||
} => {
|
} => {
|
||||||
|
let dest_info = DestInfo {
|
||||||
|
stats: Some(data.stats),
|
||||||
|
mass: Some(data.mass),
|
||||||
|
};
|
||||||
output_events.emit_server(BuffEvent {
|
output_events.emit_server(BuffEvent {
|
||||||
entity: data.entity,
|
entity: data.entity,
|
||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
@ -1340,7 +1344,8 @@ fn handle_ability(
|
|||||||
vec![BuffCategory::SelfBuff],
|
vec![BuffCategory::SelfBuff],
|
||||||
BuffSource::Character { by: *data.uid },
|
BuffSource::Character { by: *data.uid },
|
||||||
*data.time,
|
*data.time,
|
||||||
Some(data.stats),
|
dest_info,
|
||||||
|
Some(data.mass),
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -4,9 +4,9 @@ use common::{
|
|||||||
combat,
|
combat,
|
||||||
comp::{
|
comp::{
|
||||||
aura::{AuraChange, AuraKey, AuraKind, AuraTarget, EnteredAuras},
|
aura::{AuraChange, AuraKey, AuraKind, AuraTarget, EnteredAuras},
|
||||||
buff::{Buff, BuffCategory, BuffChange, BuffSource},
|
buff::{Buff, BuffCategory, BuffChange, BuffSource, DestInfo},
|
||||||
group::Group,
|
group::Group,
|
||||||
Alignment, Aura, Auras, BuffKind, Buffs, CharacterState, Health, Player, Pos, Stats,
|
Alignment, Aura, Auras, BuffKind, Buffs, CharacterState, Health, Mass, Player, Pos, Stats,
|
||||||
},
|
},
|
||||||
event::{AuraEvent, BuffEvent, EmitExt},
|
event::{AuraEvent, BuffEvent, EmitExt},
|
||||||
event_emitters,
|
event_emitters,
|
||||||
@ -41,6 +41,7 @@ pub struct ReadData<'a> {
|
|||||||
buffs: ReadStorage<'a, Buffs>,
|
buffs: ReadStorage<'a, Buffs>,
|
||||||
auras: ReadStorage<'a, Auras>,
|
auras: ReadStorage<'a, Auras>,
|
||||||
entered_auras: ReadStorage<'a, EnteredAuras>,
|
entered_auras: ReadStorage<'a, EnteredAuras>,
|
||||||
|
masses: ReadStorage<'a, Mass>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -86,80 +87,74 @@ impl<'a> System<'a> for Sys {
|
|||||||
read_data.healths.get(target)?,
|
read_data.healths.get(target)?,
|
||||||
read_data.uids.get(target)?,
|
read_data.uids.get(target)?,
|
||||||
read_data.entered_auras.get(target)?,
|
read_data.entered_auras.get(target)?,
|
||||||
read_data.stats.get(target),
|
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
target_iter.for_each(
|
target_iter.for_each(|(target, target_pos, health, target_uid, entered_auras)| {
|
||||||
|(target, target_pos, health, target_uid, entered_auras, stats)| {
|
let target_buffs = match read_data.buffs.get(target) {
|
||||||
let target_buffs = match read_data.buffs.get(target) {
|
Some(buff) => buff,
|
||||||
Some(buff) => buff,
|
None => return,
|
||||||
None => return,
|
};
|
||||||
|
|
||||||
|
// Ensure entity is within the aura radius
|
||||||
|
if target_pos.0.distance_squared(pos.0) < aura.radius.powi(2) {
|
||||||
|
// Ensure the entity is in the group we want to target
|
||||||
|
let same_group = |uid: Uid| {
|
||||||
|
read_data
|
||||||
|
.id_maps
|
||||||
|
.uid_entity(uid)
|
||||||
|
.and_then(|e| read_data.groups.get(e))
|
||||||
|
.map_or(false, |owner_group| {
|
||||||
|
Some(owner_group) == read_data.groups.get(target)
|
||||||
|
})
|
||||||
|
|| *target_uid == uid
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ensure entity is within the aura radius
|
let allow_friendly_fire =
|
||||||
if target_pos.0.distance_squared(pos.0) < aura.radius.powi(2) {
|
combat::allow_friendly_fire(&read_data.entered_auras, entity, target);
|
||||||
// Ensure the entity is in the group we want to target
|
|
||||||
let same_group = |uid: Uid| {
|
|
||||||
read_data
|
|
||||||
.id_maps
|
|
||||||
.uid_entity(uid)
|
|
||||||
.and_then(|e| read_data.groups.get(e))
|
|
||||||
.map_or(false, |owner_group| {
|
|
||||||
Some(owner_group) == read_data.groups.get(target)
|
|
||||||
})
|
|
||||||
|| *target_uid == uid
|
|
||||||
};
|
|
||||||
|
|
||||||
let allow_friendly_fire = combat::allow_friendly_fire(
|
if !(allow_friendly_fire && entity != target
|
||||||
&read_data.entered_auras,
|
|| match aura.target {
|
||||||
entity,
|
AuraTarget::GroupOf(uid) => same_group(uid),
|
||||||
target,
|
AuraTarget::NotGroupOf(uid) => !same_group(uid),
|
||||||
);
|
AuraTarget::All => true,
|
||||||
|
})
|
||||||
if !(allow_friendly_fire && entity != target
|
{
|
||||||
|| match aura.target {
|
return;
|
||||||
AuraTarget::GroupOf(uid) => same_group(uid),
|
|
||||||
AuraTarget::NotGroupOf(uid) => !same_group(uid),
|
|
||||||
AuraTarget::All => true,
|
|
||||||
})
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let did_activate = activate_aura(
|
|
||||||
key,
|
|
||||||
aura,
|
|
||||||
*uid,
|
|
||||||
target,
|
|
||||||
health,
|
|
||||||
target_buffs,
|
|
||||||
stats,
|
|
||||||
allow_friendly_fire,
|
|
||||||
&read_data,
|
|
||||||
&mut emitters,
|
|
||||||
);
|
|
||||||
|
|
||||||
if did_activate {
|
|
||||||
if entered_auras
|
|
||||||
.auras
|
|
||||||
.get(aura.aura_kind.as_ref())
|
|
||||||
.map_or(true, |auras| !auras.contains(&(*uid, key)))
|
|
||||||
{
|
|
||||||
emitters.emit(AuraEvent {
|
|
||||||
entity: target,
|
|
||||||
aura_change: AuraChange::EnterAura(
|
|
||||||
*uid,
|
|
||||||
key,
|
|
||||||
*aura.aura_kind.as_ref(),
|
|
||||||
),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
active_auras.insert((*uid, *target_uid, key));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
);
|
let did_activate = activate_aura(
|
||||||
|
key,
|
||||||
|
aura,
|
||||||
|
entity,
|
||||||
|
*uid,
|
||||||
|
target,
|
||||||
|
health,
|
||||||
|
target_buffs,
|
||||||
|
allow_friendly_fire,
|
||||||
|
&read_data,
|
||||||
|
&mut emitters,
|
||||||
|
);
|
||||||
|
|
||||||
|
if did_activate {
|
||||||
|
if entered_auras
|
||||||
|
.auras
|
||||||
|
.get(aura.aura_kind.as_ref())
|
||||||
|
.map_or(true, |auras| !auras.contains(&(*uid, key)))
|
||||||
|
{
|
||||||
|
emitters.emit(AuraEvent {
|
||||||
|
entity: target,
|
||||||
|
aura_change: AuraChange::EnterAura(
|
||||||
|
*uid,
|
||||||
|
key,
|
||||||
|
*aura.aura_kind.as_ref(),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
active_auras.insert((*uid, *target_uid, key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if !expired_auras.is_empty() {
|
if !expired_auras.is_empty() {
|
||||||
emitters.emit(AuraEvent {
|
emitters.emit(AuraEvent {
|
||||||
@ -200,11 +195,11 @@ impl<'a> System<'a> for Sys {
|
|||||||
fn activate_aura(
|
fn activate_aura(
|
||||||
key: AuraKey,
|
key: AuraKey,
|
||||||
aura: &Aura,
|
aura: &Aura,
|
||||||
applier: Uid,
|
applier: EcsEntity,
|
||||||
|
applier_uid: Uid,
|
||||||
target: EcsEntity,
|
target: EcsEntity,
|
||||||
health: &Health,
|
health: &Health,
|
||||||
target_buffs: &Buffs,
|
target_buffs: &Buffs,
|
||||||
stats: Option<&Stats>,
|
|
||||||
allow_friendly_fire: bool,
|
allow_friendly_fire: bool,
|
||||||
read_data: &ReadData,
|
read_data: &ReadData,
|
||||||
emitters: &mut impl EmitExt<BuffEvent>,
|
emitters: &mut impl EmitExt<BuffEvent>,
|
||||||
@ -286,20 +281,25 @@ fn activate_aura(
|
|||||||
let emit_buff = !target_buffs.buffs.iter().any(|(_, buff)| {
|
let emit_buff = !target_buffs.buffs.iter().any(|(_, buff)| {
|
||||||
buff.cat_ids
|
buff.cat_ids
|
||||||
.iter()
|
.iter()
|
||||||
.any(|cat_id| matches!(cat_id, BuffCategory::FromActiveAura(uid, aura_key) if *aura_key == key && *uid == applier))
|
.any(|cat_id| matches!(cat_id, BuffCategory::FromActiveAura(uid, aura_key) if *aura_key == key && *uid == applier_uid))
|
||||||
&& buff.kind == kind
|
&& buff.kind == kind
|
||||||
&& buff.data.strength >= data.strength
|
&& buff.data.strength >= data.strength
|
||||||
});
|
});
|
||||||
if emit_buff {
|
if emit_buff {
|
||||||
|
let dest_info = DestInfo {
|
||||||
|
stats: read_data.stats.get(target),
|
||||||
|
mass: read_data.masses.get(target),
|
||||||
|
};
|
||||||
emitters.emit(BuffEvent {
|
emitters.emit(BuffEvent {
|
||||||
entity: target,
|
entity: target,
|
||||||
buff_change: BuffChange::Add(Buff::new(
|
buff_change: BuffChange::Add(Buff::new(
|
||||||
kind,
|
kind,
|
||||||
data,
|
data,
|
||||||
vec![category, BuffCategory::FromActiveAura(applier, key)],
|
vec![category, BuffCategory::FromActiveAura(applier_uid, key)],
|
||||||
source,
|
source,
|
||||||
*read_data.time,
|
*read_data.time,
|
||||||
stats,
|
dest_info,
|
||||||
|
read_data.masses.get(applier),
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@ use common::{
|
|||||||
comp::{
|
comp::{
|
||||||
agent::{Sound, SoundKind},
|
agent::{Sound, SoundKind},
|
||||||
aura::EnteredAuras,
|
aura::EnteredAuras,
|
||||||
Alignment, Beam, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Ori,
|
Alignment, Beam, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory,
|
||||||
Player, Pos, Scale, Stats,
|
Mass, Ori, Player, Pos, Scale, Stats,
|
||||||
},
|
},
|
||||||
event::{self, EmitExt, EventBus},
|
event::{self, EmitExt, EventBus},
|
||||||
event_emitters,
|
event_emitters,
|
||||||
@ -63,6 +63,7 @@ pub struct ReadData<'a> {
|
|||||||
entered_auras: ReadStorage<'a, EnteredAuras>,
|
entered_auras: ReadStorage<'a, EnteredAuras>,
|
||||||
outcomes: Read<'a, EventBus<Outcome>>,
|
outcomes: Read<'a, EventBus<Outcome>>,
|
||||||
events: ReadAttackEvents<'a>,
|
events: ReadAttackEvents<'a>,
|
||||||
|
masses: ReadStorage<'a, Mass>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This system is responsible for handling beams that heal or do damage
|
/// This system is responsible for handling beams that heal or do damage
|
||||||
@ -230,6 +231,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
combo: read_data.combos.get(entity),
|
combo: read_data.combos.get(entity),
|
||||||
inventory: read_data.inventories.get(entity),
|
inventory: read_data.inventories.get(entity),
|
||||||
stats: read_data.stats.get(entity),
|
stats: read_data.stats.get(entity),
|
||||||
|
mass: read_data.masses.get(entity),
|
||||||
});
|
});
|
||||||
|
|
||||||
let target_info = TargetInfo {
|
let target_info = TargetInfo {
|
||||||
@ -243,6 +245,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
char_state: read_data.character_states.get(target),
|
char_state: read_data.character_states.get(target),
|
||||||
energy: read_data.energies.get(target),
|
energy: read_data.energies.get(target),
|
||||||
buffs: read_data.buffs.get(target),
|
buffs: read_data.buffs.get(target),
|
||||||
|
mass: read_data.masses.get(target),
|
||||||
};
|
};
|
||||||
|
|
||||||
let target_dodging = read_data
|
let target_dodging = read_data
|
||||||
@ -263,7 +266,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
let precision_from_flank = combat::precision_mult_from_flank(
|
let precision_from_flank = combat::precision_mult_from_flank(
|
||||||
beam.bezier.ctrl - beam.bezier.start,
|
beam.bezier.ctrl - beam.bezier.start,
|
||||||
target_info.ori,
|
target_info.ori,
|
||||||
1.0,
|
Default::default(),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -6,12 +6,12 @@ use common::{
|
|||||||
body::{object, Body},
|
body::{object, Body},
|
||||||
buff::{
|
buff::{
|
||||||
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffKey, BuffKind, BuffSource,
|
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffKey, BuffKind, BuffSource,
|
||||||
Buffs,
|
Buffs, DestInfo,
|
||||||
},
|
},
|
||||||
fluid_dynamics::{Fluid, LiquidKind},
|
fluid_dynamics::{Fluid, LiquidKind},
|
||||||
item::MaterialStatManifest,
|
item::MaterialStatManifest,
|
||||||
Alignment, Energy, Group, Health, HealthChange, Inventory, LightEmitter, ModifierKind,
|
Alignment, Energy, Group, Health, HealthChange, Inventory, LightEmitter, Mass,
|
||||||
PhysicsState, Player, Pos, Stats,
|
ModifierKind, PhysicsState, Player, Pos, Stats,
|
||||||
},
|
},
|
||||||
event::{
|
event::{
|
||||||
BuffEvent, ChangeBodyEvent, CreateSpriteEvent, EmitExt, EnergyChangeEvent,
|
BuffEvent, ChangeBodyEvent, CreateSpriteEvent, EmitExt, EnergyChangeEvent,
|
||||||
@ -68,6 +68,7 @@ pub struct ReadData<'a> {
|
|||||||
alignments: ReadStorage<'a, Alignment>,
|
alignments: ReadStorage<'a, Alignment>,
|
||||||
players: ReadStorage<'a, Player>,
|
players: ReadStorage<'a, Player>,
|
||||||
uids: ReadStorage<'a, Uid>,
|
uids: ReadStorage<'a, Uid>,
|
||||||
|
masses: ReadStorage<'a, Mass>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -153,10 +154,16 @@ impl<'a> System<'a> for Sys {
|
|||||||
&read_data.energies,
|
&read_data.energies,
|
||||||
read_data.uids.maybe(),
|
read_data.uids.maybe(),
|
||||||
read_data.physics_states.maybe(),
|
read_data.physics_states.maybe(),
|
||||||
|
read_data.masses.maybe(),
|
||||||
)
|
)
|
||||||
.lend_join();
|
.lend_join();
|
||||||
buff_join.for_each(|comps| {
|
buff_join.for_each(|comps| {
|
||||||
let (entity, buff_comp, mut stat, body, health, energy, uid, physics_state) = comps;
|
let (entity, buff_comp, mut stat, body, health, energy, uid, physics_state, mass) =
|
||||||
|
comps;
|
||||||
|
let dest_info = DestInfo {
|
||||||
|
stats: Some(&stat),
|
||||||
|
mass,
|
||||||
|
};
|
||||||
// Apply buffs to entity based off of their current physics_state
|
// Apply buffs to entity based off of their current physics_state
|
||||||
if let Some(physics_state) = physics_state {
|
if let Some(physics_state) = physics_state {
|
||||||
// Set nearby entities on fire if burning
|
// Set nearby entities on fire if burning
|
||||||
@ -185,7 +192,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
vec![BuffCategory::Natural],
|
vec![BuffCategory::Natural],
|
||||||
source,
|
source,
|
||||||
*read_data.time,
|
*read_data.time,
|
||||||
None,
|
DestInfo {
|
||||||
|
// Can't mutably access stats, and for burning debuff stats
|
||||||
|
// has no effect (for now)
|
||||||
|
stats: None,
|
||||||
|
mass: read_data.masses.get(t_entity),
|
||||||
|
},
|
||||||
|
mass,
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -204,7 +217,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
Vec::new(),
|
Vec::new(),
|
||||||
BuffSource::World,
|
BuffSource::World,
|
||||||
*read_data.time,
|
*read_data.time,
|
||||||
Some(&stat),
|
dest_info,
|
||||||
|
None,
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -221,7 +235,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
Vec::new(),
|
Vec::new(),
|
||||||
BuffSource::World,
|
BuffSource::World,
|
||||||
*read_data.time,
|
*read_data.time,
|
||||||
Some(&stat),
|
dest_info,
|
||||||
|
None,
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -251,7 +266,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
Vec::new(),
|
Vec::new(),
|
||||||
BuffSource::World,
|
BuffSource::World,
|
||||||
*read_data.time,
|
*read_data.time,
|
||||||
Some(&stat),
|
dest_info,
|
||||||
|
None,
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -269,7 +285,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
Vec::new(),
|
Vec::new(),
|
||||||
BuffSource::World,
|
BuffSource::World,
|
||||||
*read_data.time,
|
*read_data.time,
|
||||||
Some(&stat),
|
dest_info,
|
||||||
|
None,
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -286,7 +303,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
Vec::new(),
|
Vec::new(),
|
||||||
BuffSource::World,
|
BuffSource::World,
|
||||||
*read_data.time,
|
*read_data.time,
|
||||||
Some(&stat),
|
dest_info,
|
||||||
|
None,
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -303,7 +321,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
Vec::new(),
|
Vec::new(),
|
||||||
BuffSource::World,
|
BuffSource::World,
|
||||||
*read_data.time,
|
*read_data.time,
|
||||||
Some(&stat),
|
dest_info,
|
||||||
|
None,
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
// When standing on IceSpike also apply Frozen
|
// When standing on IceSpike also apply Frozen
|
||||||
@ -315,7 +334,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
Vec::new(),
|
Vec::new(),
|
||||||
BuffSource::World,
|
BuffSource::World,
|
||||||
*read_data.time,
|
*read_data.time,
|
||||||
Some(&stat),
|
dest_info,
|
||||||
|
None,
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -332,7 +352,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
Vec::new(),
|
Vec::new(),
|
||||||
BuffSource::World,
|
BuffSource::World,
|
||||||
*read_data.time,
|
*read_data.time,
|
||||||
Some(&stat),
|
dest_info,
|
||||||
|
None,
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -353,7 +374,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
vec![BuffCategory::Natural],
|
vec![BuffCategory::Natural],
|
||||||
BuffSource::World,
|
BuffSource::World,
|
||||||
*read_data.time,
|
*read_data.time,
|
||||||
Some(&stat),
|
dest_info,
|
||||||
|
None,
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
} else if matches!(
|
} else if matches!(
|
||||||
@ -429,7 +451,8 @@ impl<'a> System<'a> for Sys {
|
|||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
buff.source,
|
buff.source,
|
||||||
*read_data.time,
|
*read_data.time,
|
||||||
Some(&stat),
|
dest_info,
|
||||||
|
None,
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,8 @@ use common::{
|
|||||||
agent::{Sound, SoundKind},
|
agent::{Sound, SoundKind},
|
||||||
aura::EnteredAuras,
|
aura::EnteredAuras,
|
||||||
melee::MultiTarget,
|
melee::MultiTarget,
|
||||||
Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Melee,
|
Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Mass,
|
||||||
Ori, Player, Pos, Scale, Stats,
|
Melee, Ori, Player, Pos, Scale, Stats,
|
||||||
},
|
},
|
||||||
event::{self, EmitExt, EventBus},
|
event::{self, EmitExt, EventBus},
|
||||||
event_emitters,
|
event_emitters,
|
||||||
@ -62,6 +62,7 @@ pub struct ReadData<'a> {
|
|||||||
buffs: ReadStorage<'a, Buffs>,
|
buffs: ReadStorage<'a, Buffs>,
|
||||||
entered_auras: ReadStorage<'a, EnteredAuras>,
|
entered_auras: ReadStorage<'a, EnteredAuras>,
|
||||||
events: ReadAttackEvents<'a>,
|
events: ReadAttackEvents<'a>,
|
||||||
|
masses: ReadStorage<'a, Mass>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This system is responsible for handling accepted inputs like moving or
|
/// This system is responsible for handling accepted inputs like moving or
|
||||||
@ -213,6 +214,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
combo: read_data.combos.get(attacker),
|
combo: read_data.combos.get(attacker),
|
||||||
inventory: read_data.inventories.get(attacker),
|
inventory: read_data.inventories.get(attacker),
|
||||||
stats: read_data.stats.get(attacker),
|
stats: read_data.stats.get(attacker),
|
||||||
|
mass: read_data.masses.get(attacker),
|
||||||
});
|
});
|
||||||
|
|
||||||
let target_ori = read_data.orientations.get(target);
|
let target_ori = read_data.orientations.get(target);
|
||||||
@ -228,6 +230,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
char_state: target_char_state,
|
char_state: target_char_state,
|
||||||
energy: read_data.energies.get(target),
|
energy: read_data.energies.get(target),
|
||||||
buffs: read_data.buffs.get(target),
|
buffs: read_data.buffs.get(target),
|
||||||
|
mass: read_data.masses.get(target),
|
||||||
};
|
};
|
||||||
|
|
||||||
// PvP check
|
// PvP check
|
||||||
@ -248,7 +251,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
.try_normalized()
|
.try_normalized()
|
||||||
.unwrap_or(ori.look_vec()),
|
.unwrap_or(ori.look_vec()),
|
||||||
target_ori,
|
target_ori,
|
||||||
melee_attack.precision_flank_multiplier,
|
melee_attack.precision_flank_multipliers,
|
||||||
melee_attack.precision_flank_invert,
|
melee_attack.precision_flank_invert,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ use common::{
|
|||||||
agent::{Sound, SoundKind},
|
agent::{Sound, SoundKind},
|
||||||
aura::EnteredAuras,
|
aura::EnteredAuras,
|
||||||
projectile, Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health,
|
projectile, Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health,
|
||||||
Inventory, Ori, PhysicsState, Player, Pos, Projectile, Stats, Vel,
|
Inventory, Mass, Ori, PhysicsState, Player, Pos, Projectile, Stats, Vel,
|
||||||
},
|
},
|
||||||
event::{
|
event::{
|
||||||
BonkEvent, BuffEvent, ComboChangeEvent, DeleteEvent, EmitExt, Emitter, EnergyChangeEvent,
|
BonkEvent, BuffEvent, ComboChangeEvent, DeleteEvent, EmitExt, Emitter, EnergyChangeEvent,
|
||||||
@ -73,6 +73,7 @@ pub struct ReadData<'a> {
|
|||||||
terrain: ReadExpect<'a, TerrainGrid>,
|
terrain: ReadExpect<'a, TerrainGrid>,
|
||||||
buffs: ReadStorage<'a, Buffs>,
|
buffs: ReadStorage<'a, Buffs>,
|
||||||
entered_auras: ReadStorage<'a, EnteredAuras>,
|
entered_auras: ReadStorage<'a, EnteredAuras>,
|
||||||
|
masses: ReadStorage<'a, Mass>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This system is responsible for handling projectile effect triggers
|
/// This system is responsible for handling projectile effect triggers
|
||||||
@ -340,6 +341,7 @@ fn dispatch_hit(
|
|||||||
combo: read_data.combos.get(entity),
|
combo: read_data.combos.get(entity),
|
||||||
inventory: read_data.inventories.get(entity),
|
inventory: read_data.inventories.get(entity),
|
||||||
stats: read_data.stats.get(entity),
|
stats: read_data.stats.get(entity),
|
||||||
|
mass: read_data.masses.get(entity),
|
||||||
});
|
});
|
||||||
|
|
||||||
let target_info = TargetInfo {
|
let target_info = TargetInfo {
|
||||||
@ -353,6 +355,7 @@ fn dispatch_hit(
|
|||||||
char_state: read_data.character_states.get(target),
|
char_state: read_data.character_states.get(target),
|
||||||
energy: read_data.energies.get(target),
|
energy: read_data.energies.get(target),
|
||||||
buffs: read_data.buffs.get(target),
|
buffs: read_data.buffs.get(target),
|
||||||
|
mass: read_data.masses.get(target),
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: Is it possible to have projectile without body??
|
// TODO: Is it possible to have projectile without body??
|
||||||
@ -389,8 +392,12 @@ fn dispatch_hit(
|
|||||||
.and_then(|cs| cs.attack_immunities())
|
.and_then(|cs| cs.attack_immunities())
|
||||||
.map_or(false, |i| i.projectiles);
|
.map_or(false, |i| i.projectiles);
|
||||||
|
|
||||||
let precision_from_flank =
|
let precision_from_flank = combat::precision_mult_from_flank(
|
||||||
combat::precision_mult_from_flank(*projectile_dir, target_info.ori, 1.0, false);
|
*projectile_dir,
|
||||||
|
target_info.ori,
|
||||||
|
Default::default(),
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
let precision_from_head = {
|
let precision_from_head = {
|
||||||
// This performs a cylinder and line segment intersection check. The cylinder is
|
// This performs a cylinder and line segment intersection check. The cylinder is
|
||||||
|
@ -4,7 +4,7 @@ use common::{
|
|||||||
agent::{Sound, SoundKind},
|
agent::{Sound, SoundKind},
|
||||||
aura::EnteredAuras,
|
aura::EnteredAuras,
|
||||||
shockwave::ShockwaveDodgeable,
|
shockwave::ShockwaveDodgeable,
|
||||||
Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Ori,
|
Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Mass, Ori,
|
||||||
PhysicsState, Player, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats,
|
PhysicsState, Player, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats,
|
||||||
},
|
},
|
||||||
event::{
|
event::{
|
||||||
@ -64,6 +64,7 @@ pub struct ReadData<'a> {
|
|||||||
character_states: ReadStorage<'a, CharacterState>,
|
character_states: ReadStorage<'a, CharacterState>,
|
||||||
buffs: ReadStorage<'a, Buffs>,
|
buffs: ReadStorage<'a, Buffs>,
|
||||||
entered_auras: ReadStorage<'a, EnteredAuras>,
|
entered_auras: ReadStorage<'a, EnteredAuras>,
|
||||||
|
masses: ReadStorage<'a, Mass>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This system is responsible for handling accepted inputs like moving or
|
/// This system is responsible for handling accepted inputs like moving or
|
||||||
@ -233,6 +234,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
combo: read_data.combos.get(entity),
|
combo: read_data.combos.get(entity),
|
||||||
inventory: read_data.inventories.get(entity),
|
inventory: read_data.inventories.get(entity),
|
||||||
stats: read_data.stats.get(entity),
|
stats: read_data.stats.get(entity),
|
||||||
|
mass: read_data.masses.get(entity),
|
||||||
});
|
});
|
||||||
|
|
||||||
let target_info = TargetInfo {
|
let target_info = TargetInfo {
|
||||||
@ -246,6 +248,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
char_state: read_data.character_states.get(target),
|
char_state: read_data.character_states.get(target),
|
||||||
energy: read_data.energies.get(target),
|
energy: read_data.energies.get(target),
|
||||||
buffs: read_data.buffs.get(target),
|
buffs: read_data.buffs.get(target),
|
||||||
|
mass: read_data.masses.get(target),
|
||||||
};
|
};
|
||||||
|
|
||||||
let target_dodging = read_data
|
let target_dodging = read_data
|
||||||
|
@ -30,7 +30,7 @@ use common::{
|
|||||||
comp::{
|
comp::{
|
||||||
self,
|
self,
|
||||||
aura::{AuraKindVariant, AuraTarget},
|
aura::{AuraKindVariant, AuraTarget},
|
||||||
buff::{Buff, BuffData, BuffKind, BuffSource, MiscBuffData},
|
buff::{Buff, BuffData, BuffKind, BuffSource, DestInfo, MiscBuffData},
|
||||||
inventory::{
|
inventory::{
|
||||||
item::{all_items_expect, tool::AbilityMap, MaterialStatManifest, Quality},
|
item::{all_items_expect, tool::AbilityMap, MaterialStatManifest, Quality},
|
||||||
slot::Slot,
|
slot::Slot,
|
||||||
@ -4552,7 +4552,8 @@ fn build_buff(
|
|||||||
| BuffKind::PotionSickness
|
| BuffKind::PotionSickness
|
||||||
| BuffKind::Heatstroke
|
| BuffKind::Heatstroke
|
||||||
| BuffKind::ScornfulTaunt
|
| BuffKind::ScornfulTaunt
|
||||||
| BuffKind::Rooted => {
|
| BuffKind::Rooted
|
||||||
|
| BuffKind::Winded => {
|
||||||
if buff_kind.is_simple() {
|
if buff_kind.is_simple() {
|
||||||
unreachable!("is_simple() above")
|
unreachable!("is_simple() above")
|
||||||
} else {
|
} else {
|
||||||
@ -4569,8 +4570,13 @@ fn cast_buff(buffkind: BuffKind, data: BuffData, server: &mut Server, target: Ec
|
|||||||
let ecs = &server.state.ecs();
|
let ecs = &server.state.ecs();
|
||||||
let mut buffs_all = ecs.write_storage::<comp::Buffs>();
|
let mut buffs_all = ecs.write_storage::<comp::Buffs>();
|
||||||
let stats = ecs.read_storage::<comp::Stats>();
|
let stats = ecs.read_storage::<comp::Stats>();
|
||||||
|
let masses = ecs.read_storage::<comp::Mass>();
|
||||||
let time = ecs.read_resource::<Time>();
|
let time = ecs.read_resource::<Time>();
|
||||||
if let Some(mut buffs) = buffs_all.get_mut(target) {
|
if let Some(mut buffs) = buffs_all.get_mut(target) {
|
||||||
|
let dest_info = DestInfo {
|
||||||
|
stats: stats.get(target),
|
||||||
|
mass: masses.get(target),
|
||||||
|
};
|
||||||
buffs.insert(
|
buffs.insert(
|
||||||
Buff::new(
|
Buff::new(
|
||||||
buffkind,
|
buffkind,
|
||||||
@ -4578,7 +4584,8 @@ fn cast_buff(buffkind: BuffKind, data: BuffData, server: &mut Server, target: Ec
|
|||||||
vec![],
|
vec![],
|
||||||
BuffSource::Command,
|
BuffSource::Command,
|
||||||
*time,
|
*time,
|
||||||
stats.get(target),
|
dest_info,
|
||||||
|
None,
|
||||||
),
|
),
|
||||||
*time,
|
*time,
|
||||||
);
|
);
|
||||||
|
@ -332,6 +332,7 @@ pub struct DestroyEventData<'a> {
|
|||||||
#[cfg(feature = "worldgen")]
|
#[cfg(feature = "worldgen")]
|
||||||
presences: ReadStorage<'a, Presence>,
|
presences: ReadStorage<'a, Presence>,
|
||||||
buff_events: Read<'a, EventBus<BuffEvent>>,
|
buff_events: Read<'a, EventBus<BuffEvent>>,
|
||||||
|
masses: ReadStorage<'a, comp::Mass>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Handle an entity dying. If it is a player, it will send a message to all
|
/// Handle an entity dying. If it is a player, it will send a message to all
|
||||||
@ -389,6 +390,7 @@ impl ServerEvent for DestroyEvent {
|
|||||||
let attacker_entity = ev.cause.by.and_then(|x| data.id_maps.uid_entity(x.uid()));
|
let attacker_entity = ev.cause.by.and_then(|x| data.id_maps.uid_entity(x.uid()));
|
||||||
let killed_uid = data.uids.get(ev.entity);
|
let killed_uid = data.uids.get(ev.entity);
|
||||||
let attacker_stats = attacker_entity.and_then(|e| data.stats.get(e));
|
let attacker_stats = attacker_entity.and_then(|e| data.stats.get(e));
|
||||||
|
let attacker_mass = attacker_entity.and_then(|e| data.masses.get(e));
|
||||||
for effect in killed_stats.effects_on_death.iter() {
|
for effect in killed_stats.effects_on_death.iter() {
|
||||||
match effect {
|
match effect {
|
||||||
DeathEffect::AttackerBuff {
|
DeathEffect::AttackerBuff {
|
||||||
@ -397,6 +399,10 @@ impl ServerEvent for DestroyEvent {
|
|||||||
duration,
|
duration,
|
||||||
} => {
|
} => {
|
||||||
if let Some(attacker) = attacker_entity {
|
if let Some(attacker) = attacker_entity {
|
||||||
|
let dest_info = buff::DestInfo {
|
||||||
|
stats: attacker_stats,
|
||||||
|
mass: attacker_mass,
|
||||||
|
};
|
||||||
buff_emitter.emit(BuffEvent {
|
buff_emitter.emit(BuffEvent {
|
||||||
entity: attacker,
|
entity: attacker,
|
||||||
buff_change: buff::BuffChange::Add(buff::Buff::new(
|
buff_change: buff::BuffChange::Add(buff::Buff::new(
|
||||||
@ -409,7 +415,8 @@ impl ServerEvent for DestroyEvent {
|
|||||||
BuffSource::World
|
BuffSource::World
|
||||||
},
|
},
|
||||||
*data.time,
|
*data.time,
|
||||||
attacker_stats,
|
dest_info,
|
||||||
|
data.masses.get(ev.entity),
|
||||||
)),
|
)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -976,80 +983,54 @@ event_emitters! {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerEvent for ExplosionEvent {
|
#[derive(SystemData)]
|
||||||
type SystemData<'a> = (
|
pub struct ExplosionData<'a> {
|
||||||
Entities<'a>,
|
entities: Entities<'a>,
|
||||||
Write<'a, BlockChange>,
|
block_change: Write<'a, BlockChange>,
|
||||||
Read<'a, Settings>,
|
settings: Read<'a, Settings>,
|
||||||
Read<'a, Time>,
|
time: Read<'a, Time>,
|
||||||
Read<'a, IdMaps>,
|
id_maps: Read<'a, IdMaps>,
|
||||||
Read<'a, CachedSpatialGrid>,
|
spatial_grid: Read<'a, CachedSpatialGrid>,
|
||||||
ReadExpect<'a, TerrainGrid>,
|
terrain: ReadExpect<'a, TerrainGrid>,
|
||||||
ReadExpect<'a, MaterialStatManifest>,
|
msm: ReadExpect<'a, MaterialStatManifest>,
|
||||||
ReadExplosionEvents<'a>,
|
event_busses: ReadExplosionEvents<'a>,
|
||||||
Read<'a, EventBus<Outcome>>,
|
outcomes: Read<'a, EventBus<Outcome>>,
|
||||||
ReadStorage<'a, Group>,
|
groups: ReadStorage<'a, Group>,
|
||||||
ReadStorage<'a, Auras>,
|
auras: ReadStorage<'a, Auras>,
|
||||||
ReadStorage<'a, Pos>,
|
positions: ReadStorage<'a, Pos>,
|
||||||
ReadStorage<'a, Player>,
|
players: ReadStorage<'a, Player>,
|
||||||
ReadStorage<'a, Energy>,
|
energies: ReadStorage<'a, Energy>,
|
||||||
ReadStorage<'a, comp::Combo>,
|
combos: ReadStorage<'a, comp::Combo>,
|
||||||
ReadStorage<'a, Inventory>,
|
inventories: ReadStorage<'a, Inventory>,
|
||||||
ReadStorage<'a, Alignment>,
|
alignments: ReadStorage<'a, Alignment>,
|
||||||
ReadStorage<'a, EnteredAuras>,
|
entered_auras: ReadStorage<'a, EnteredAuras>,
|
||||||
ReadStorage<'a, comp::Buffs>,
|
buffs: ReadStorage<'a, comp::Buffs>,
|
||||||
ReadStorage<'a, comp::Stats>,
|
stats: ReadStorage<'a, comp::Stats>,
|
||||||
ReadStorage<'a, Health>,
|
healths: ReadStorage<'a, Health>,
|
||||||
ReadStorage<'a, Body>,
|
bodies: ReadStorage<'a, Body>,
|
||||||
ReadStorage<'a, comp::Ori>,
|
orientations: ReadStorage<'a, comp::Ori>,
|
||||||
ReadStorage<'a, CharacterState>,
|
character_states: ReadStorage<'a, CharacterState>,
|
||||||
ReadStorage<'a, Uid>,
|
uids: ReadStorage<'a, Uid>,
|
||||||
);
|
masses: ReadStorage<'a, comp::Mass>,
|
||||||
|
}
|
||||||
|
|
||||||
fn handle(
|
impl ServerEvent for ExplosionEvent {
|
||||||
events: impl ExactSizeIterator<Item = Self>,
|
type SystemData<'a> = ExplosionData<'a>;
|
||||||
(
|
|
||||||
entities,
|
fn handle(events: impl ExactSizeIterator<Item = Self>, mut data: Self::SystemData<'_>) {
|
||||||
mut block_change,
|
let mut emitters = data.event_busses.get_emitters();
|
||||||
settings,
|
let mut outcome_emitter = data.outcomes.emitter();
|
||||||
time,
|
|
||||||
id_maps,
|
|
||||||
spatial_grid,
|
|
||||||
terrain,
|
|
||||||
msm,
|
|
||||||
event_busses,
|
|
||||||
outcomes,
|
|
||||||
groups,
|
|
||||||
auras,
|
|
||||||
positions,
|
|
||||||
players,
|
|
||||||
energies,
|
|
||||||
combos,
|
|
||||||
inventories,
|
|
||||||
alignments,
|
|
||||||
entered_auras,
|
|
||||||
buffs,
|
|
||||||
stats,
|
|
||||||
healths,
|
|
||||||
bodies,
|
|
||||||
orientations,
|
|
||||||
character_states,
|
|
||||||
uids,
|
|
||||||
): Self::SystemData<'_>,
|
|
||||||
) {
|
|
||||||
let mut emitters = event_busses.get_emitters();
|
|
||||||
let mut outcome_emitter = outcomes.emitter();
|
|
||||||
|
|
||||||
// TODO: Faster RNG?
|
// TODO: Faster RNG?
|
||||||
let mut rng = rand::thread_rng();
|
let mut rng = rand::thread_rng();
|
||||||
|
|
||||||
for ev in events {
|
for ev in events {
|
||||||
let owner_entity = ev.owner.and_then(|uid| id_maps.uid_entity(uid));
|
let owner_entity = ev.owner.and_then(|uid| data.id_maps.uid_entity(uid));
|
||||||
|
|
||||||
let explosion_volume = 6.25 * ev.explosion.radius;
|
let explosion_volume = 6.25 * ev.explosion.radius;
|
||||||
|
|
||||||
emitters.emit(SoundEvent {
|
emitters.emit(SoundEvent {
|
||||||
sound: Sound::new(SoundKind::Explosion, ev.pos, explosion_volume, time.0),
|
sound: Sound::new(SoundKind::Explosion, ev.pos, explosion_volume, data.time.0),
|
||||||
});
|
});
|
||||||
|
|
||||||
let outcome_power = ev.explosion.radius;
|
let outcome_power = ev.explosion.radius;
|
||||||
@ -1105,14 +1086,15 @@ impl ServerEvent for ExplosionEvent {
|
|||||||
const RAYS: usize = 500;
|
const RAYS: usize = 500;
|
||||||
|
|
||||||
// Prevent block colour changes within the radius of a safe zone aura
|
// Prevent block colour changes within the radius of a safe zone aura
|
||||||
if spatial_grid
|
if data
|
||||||
|
.spatial_grid
|
||||||
.0
|
.0
|
||||||
.in_circle_aabr(ev.pos.xy(), SAFE_ZONE_RADIUS)
|
.in_circle_aabr(ev.pos.xy(), SAFE_ZONE_RADIUS)
|
||||||
.filter_map(|entity| {
|
.filter_map(|entity| {
|
||||||
auras
|
data.auras
|
||||||
.get(entity)
|
.get(entity)
|
||||||
.and_then(|entity_auras| {
|
.and_then(|entity_auras| {
|
||||||
positions.get(entity).map(|pos| (entity_auras, pos))
|
data.positions.get(entity).map(|pos| (entity_auras, pos))
|
||||||
})
|
})
|
||||||
.and_then(|(entity_auras, pos)| {
|
.and_then(|(entity_auras, pos)| {
|
||||||
entity_auras
|
entity_auras
|
||||||
@ -1146,7 +1128,8 @@ impl ServerEvent for ExplosionEvent {
|
|||||||
)
|
)
|
||||||
.normalized();
|
.normalized();
|
||||||
|
|
||||||
let _ = terrain
|
let _ = data
|
||||||
|
.terrain
|
||||||
.ray(ev.pos, ev.pos + dir * color_range)
|
.ray(ev.pos, ev.pos + dir * color_range)
|
||||||
.until(|_| rng.gen::<f32>() < 0.05)
|
.until(|_| rng.gen::<f32>() < 0.05)
|
||||||
.for_each(|_: &Block, pos| touched_blocks.push(pos))
|
.for_each(|_: &Block, pos| touched_blocks.push(pos))
|
||||||
@ -1154,14 +1137,14 @@ impl ServerEvent for ExplosionEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for block_pos in touched_blocks {
|
for block_pos in touched_blocks {
|
||||||
if let Ok(block) = terrain.get(block_pos) {
|
if let Ok(block) = data.terrain.get(block_pos) {
|
||||||
if !matches!(block.kind(), BlockKind::Lava | BlockKind::GlowingRock)
|
if !matches!(block.kind(), BlockKind::Lava | BlockKind::GlowingRock)
|
||||||
&& (
|
&& (
|
||||||
// Check that owner is not player or explosion_burn_marks by
|
// Check that owner is not player or explosion_burn_marks by
|
||||||
// players
|
// players
|
||||||
// is enabled
|
// is enabled
|
||||||
owner_entity.map_or(true, |e| players.get(e).is_none())
|
owner_entity.map_or(true, |e| data.players.get(e).is_none())
|
||||||
|| settings.gameplay.explosion_burn_marks
|
|| data.settings.gameplay.explosion_burn_marks
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
let diff2 =
|
let diff2 =
|
||||||
@ -1184,7 +1167,7 @@ impl ServerEvent for ExplosionEvent {
|
|||||||
color[0] = (r as u8).max(30);
|
color[0] = (r as u8).max(30);
|
||||||
color[1] = (g as u8).max(30);
|
color[1] = (g as u8).max(30);
|
||||||
color[2] = (b as u8).max(30);
|
color[2] = (b as u8).max(30);
|
||||||
block_change
|
data.block_change
|
||||||
.set(block_pos, Block::new(block.kind(), color));
|
.set(block_pos, Block::new(block.kind(), color));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1212,7 +1195,8 @@ impl ServerEvent for ExplosionEvent {
|
|||||||
|
|
||||||
let from = ev.pos;
|
let from = ev.pos;
|
||||||
let to = ev.pos + dir * power;
|
let to = ev.pos + dir * power;
|
||||||
let _ = terrain
|
let _ = data
|
||||||
|
.terrain
|
||||||
.ray(from, to)
|
.ray(from, to)
|
||||||
.while_(|block: &Block| {
|
.while_(|block: &Block| {
|
||||||
ray_energy -= block.explode_power().unwrap_or(0.0)
|
ray_energy -= block.explode_power().unwrap_or(0.0)
|
||||||
@ -1228,7 +1212,7 @@ impl ServerEvent for ExplosionEvent {
|
|||||||
})
|
})
|
||||||
.for_each(|block: &Block, pos| {
|
.for_each(|block: &Block, pos| {
|
||||||
if block.explode_power().is_some() {
|
if block.explode_power().is_some() {
|
||||||
block_change.set(pos, block.into_vacant());
|
data.block_change.set(pos, block.into_vacant());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.cast();
|
.cast();
|
||||||
@ -1241,14 +1225,14 @@ impl ServerEvent for ExplosionEvent {
|
|||||||
health_b,
|
health_b,
|
||||||
(body_b_maybe, ori_b_maybe, char_state_b_maybe, uid_b),
|
(body_b_maybe, ori_b_maybe, char_state_b_maybe, uid_b),
|
||||||
) in (
|
) in (
|
||||||
&entities,
|
&data.entities,
|
||||||
&positions,
|
&data.positions,
|
||||||
&healths,
|
&data.healths,
|
||||||
(
|
(
|
||||||
bodies.maybe(),
|
data.bodies.maybe(),
|
||||||
orientations.maybe(),
|
data.orientations.maybe(),
|
||||||
character_states.maybe(),
|
data.character_states.maybe(),
|
||||||
&uids,
|
&data.uids,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.join()
|
.join()
|
||||||
@ -1271,7 +1255,8 @@ impl ServerEvent for ExplosionEvent {
|
|||||||
|
|
||||||
// Cast a ray from the explosion to the entity to check visibility
|
// Cast a ray from the explosion to the entity to check visibility
|
||||||
if strength > 0.0
|
if strength > 0.0
|
||||||
&& (terrain
|
&& (data
|
||||||
|
.terrain
|
||||||
.ray(ev.pos, pos_b.0)
|
.ray(ev.pos, pos_b.0)
|
||||||
.until(Block::is_opaque)
|
.until(Block::is_opaque)
|
||||||
.cast()
|
.cast()
|
||||||
@ -1282,8 +1267,8 @@ impl ServerEvent for ExplosionEvent {
|
|||||||
{
|
{
|
||||||
// See if entities are in the same group
|
// See if entities are in the same group
|
||||||
let same_group = owner_entity
|
let same_group = owner_entity
|
||||||
.and_then(|e| groups.get(e))
|
.and_then(|e| data.groups.get(e))
|
||||||
.map(|group_a| Some(group_a) == groups.get(entity_b))
|
.map(|group_a| Some(group_a) == data.groups.get(entity_b))
|
||||||
.unwrap_or(Some(entity_b) == owner_entity);
|
.unwrap_or(Some(entity_b) == owner_entity);
|
||||||
|
|
||||||
let target_group = if same_group {
|
let target_group = if same_group {
|
||||||
@ -1303,25 +1288,27 @@ impl ServerEvent for ExplosionEvent {
|
|||||||
combat::AttackerInfo {
|
combat::AttackerInfo {
|
||||||
entity,
|
entity,
|
||||||
uid,
|
uid,
|
||||||
group: groups.get(entity),
|
group: data.groups.get(entity),
|
||||||
energy: energies.get(entity),
|
energy: data.energies.get(entity),
|
||||||
combo: combos.get(entity),
|
combo: data.combos.get(entity),
|
||||||
inventory: inventories.get(entity),
|
inventory: data.inventories.get(entity),
|
||||||
stats: stats.get(entity),
|
stats: data.stats.get(entity),
|
||||||
|
mass: data.masses.get(entity),
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let target_info = combat::TargetInfo {
|
let target_info = combat::TargetInfo {
|
||||||
entity: entity_b,
|
entity: entity_b,
|
||||||
uid: *uid_b,
|
uid: *uid_b,
|
||||||
inventory: inventories.get(entity_b),
|
inventory: data.inventories.get(entity_b),
|
||||||
stats: stats.get(entity_b),
|
stats: data.stats.get(entity_b),
|
||||||
health: Some(health_b),
|
health: Some(health_b),
|
||||||
pos: pos_b.0,
|
pos: pos_b.0,
|
||||||
ori: ori_b_maybe,
|
ori: ori_b_maybe,
|
||||||
char_state: char_state_b_maybe,
|
char_state: char_state_b_maybe,
|
||||||
energy: energies.get(entity_b),
|
energy: data.energies.get(entity_b),
|
||||||
buffs: buffs.get(entity_b),
|
buffs: data.buffs.get(entity_b),
|
||||||
|
mass: data.masses.get(entity_b),
|
||||||
};
|
};
|
||||||
|
|
||||||
let target_dodging = char_state_b_maybe
|
let target_dodging = char_state_b_maybe
|
||||||
@ -1330,17 +1317,17 @@ impl ServerEvent for ExplosionEvent {
|
|||||||
let allow_friendly_fire =
|
let allow_friendly_fire =
|
||||||
owner_entity.is_some_and(|owner_entity| {
|
owner_entity.is_some_and(|owner_entity| {
|
||||||
combat::allow_friendly_fire(
|
combat::allow_friendly_fire(
|
||||||
&entered_auras,
|
&data.entered_auras,
|
||||||
owner_entity,
|
owner_entity,
|
||||||
entity_b,
|
entity_b,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
// PvP check
|
// PvP check
|
||||||
let permit_pvp = combat::permit_pvp(
|
let permit_pvp = combat::permit_pvp(
|
||||||
&alignments,
|
&data.alignments,
|
||||||
&players,
|
&data.players,
|
||||||
&entered_auras,
|
&data.entered_auras,
|
||||||
&id_maps,
|
&data.id_maps,
|
||||||
owner_entity,
|
owner_entity,
|
||||||
entity_b,
|
entity_b,
|
||||||
);
|
);
|
||||||
@ -1359,7 +1346,7 @@ impl ServerEvent for ExplosionEvent {
|
|||||||
attack_options,
|
attack_options,
|
||||||
strength,
|
strength,
|
||||||
combat::AttackSource::Explosion,
|
combat::AttackSource::Explosion,
|
||||||
*time,
|
*data.time,
|
||||||
&mut emitters,
|
&mut emitters,
|
||||||
|o| outcome_emitter.emit(o),
|
|o| outcome_emitter.emit(o),
|
||||||
&mut rng,
|
&mut rng,
|
||||||
@ -1370,7 +1357,7 @@ impl ServerEvent for ExplosionEvent {
|
|||||||
},
|
},
|
||||||
RadiusEffect::Entity(mut effect) => {
|
RadiusEffect::Entity(mut effect) => {
|
||||||
for (entity_b, pos_b, body_b_maybe) in
|
for (entity_b, pos_b, body_b_maybe) in
|
||||||
(&entities, &positions, bodies.maybe()).join()
|
(&data.entities, &data.positions, data.bodies.maybe()).join()
|
||||||
{
|
{
|
||||||
let strength = if let Some(body) = body_b_maybe {
|
let strength = if let Some(body) = body_b_maybe {
|
||||||
cylinder_sphere_strength(
|
cylinder_sphere_strength(
|
||||||
@ -1397,38 +1384,41 @@ impl ServerEvent for ExplosionEvent {
|
|||||||
// This can be changed later.
|
// This can be changed later.
|
||||||
let permit_pvp = || {
|
let permit_pvp = || {
|
||||||
combat::permit_pvp(
|
combat::permit_pvp(
|
||||||
&alignments,
|
&data.alignments,
|
||||||
&players,
|
&data.players,
|
||||||
&entered_auras,
|
&data.entered_auras,
|
||||||
&id_maps,
|
&data.id_maps,
|
||||||
owner_entity,
|
owner_entity,
|
||||||
entity_b,
|
entity_b,
|
||||||
) || owner_entity.map_or(true, |entity_a| entity_a == entity_b)
|
) || owner_entity.map_or(true, |entity_a| entity_a == entity_b)
|
||||||
};
|
};
|
||||||
if strength > 0.0 {
|
if strength > 0.0 {
|
||||||
let is_alive = healths.get(entity_b).map_or(true, |h| !h.is_dead);
|
let is_alive =
|
||||||
|
data.healths.get(entity_b).map_or(true, |h| !h.is_dead);
|
||||||
|
|
||||||
if is_alive {
|
if is_alive {
|
||||||
effect.modify_strength(strength);
|
effect.modify_strength(strength);
|
||||||
if !effect.is_harm() || permit_pvp() {
|
if !effect.is_harm() || permit_pvp() {
|
||||||
emit_effect_events(
|
emit_effect_events(
|
||||||
&mut emitters,
|
&mut emitters,
|
||||||
*time,
|
*data.time,
|
||||||
entity_b,
|
entity_b,
|
||||||
effect.clone(),
|
effect.clone(),
|
||||||
ev.owner.map(|owner| {
|
ev.owner.map(|owner| {
|
||||||
(
|
(
|
||||||
owner,
|
owner,
|
||||||
id_maps
|
data.id_maps
|
||||||
.uid_entity(owner)
|
.uid_entity(owner)
|
||||||
.and_then(|e| groups.get(e))
|
.and_then(|e| data.groups.get(e))
|
||||||
.copied(),
|
.copied(),
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
inventories.get(entity_b),
|
data.inventories.get(entity_b),
|
||||||
&msm,
|
&data.msm,
|
||||||
character_states.get(entity_b),
|
data.character_states.get(entity_b),
|
||||||
stats.get(entity_b),
|
data.stats.get(entity_b),
|
||||||
|
data.masses.get(entity_b),
|
||||||
|
owner_entity.and_then(|e| data.masses.get(e)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1451,6 +1441,8 @@ pub fn emit_effect_events(
|
|||||||
msm: &MaterialStatManifest,
|
msm: &MaterialStatManifest,
|
||||||
char_state: Option<&CharacterState>,
|
char_state: Option<&CharacterState>,
|
||||||
stats: Option<&Stats>,
|
stats: Option<&Stats>,
|
||||||
|
tgt_mass: Option<&comp::Mass>,
|
||||||
|
source_mass: Option<&comp::Mass>,
|
||||||
) {
|
) {
|
||||||
let damage_contributor = source.map(|(uid, group)| DamageContributor::new(uid, group));
|
let damage_contributor = source.map(|(uid, group)| DamageContributor::new(uid, group));
|
||||||
match effect {
|
match effect {
|
||||||
@ -1483,17 +1475,24 @@ pub fn emit_effect_events(
|
|||||||
);
|
);
|
||||||
emitters.emit(HealthChangeEvent { entity, change })
|
emitters.emit(HealthChangeEvent { entity, change })
|
||||||
},
|
},
|
||||||
common::effect::Effect::Buff(buff) => emitters.emit(BuffEvent {
|
common::effect::Effect::Buff(buff) => {
|
||||||
entity,
|
let dest_info = buff::DestInfo {
|
||||||
buff_change: comp::BuffChange::Add(comp::Buff::new(
|
|
||||||
buff.kind,
|
|
||||||
buff.data,
|
|
||||||
buff.cat_ids,
|
|
||||||
comp::BuffSource::Item,
|
|
||||||
time,
|
|
||||||
stats,
|
stats,
|
||||||
)),
|
mass: tgt_mass,
|
||||||
}),
|
};
|
||||||
|
emitters.emit(BuffEvent {
|
||||||
|
entity,
|
||||||
|
buff_change: comp::BuffChange::Add(comp::Buff::new(
|
||||||
|
buff.kind,
|
||||||
|
buff.data,
|
||||||
|
buff.cat_ids,
|
||||||
|
comp::BuffSource::Item,
|
||||||
|
time,
|
||||||
|
dest_info,
|
||||||
|
source_mass,
|
||||||
|
)),
|
||||||
|
});
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1740,11 +1739,12 @@ impl ServerEvent for ParryHookEvent {
|
|||||||
WriteStorage<'a, CharacterState>,
|
WriteStorage<'a, CharacterState>,
|
||||||
ReadStorage<'a, Uid>,
|
ReadStorage<'a, Uid>,
|
||||||
ReadStorage<'a, Stats>,
|
ReadStorage<'a, Stats>,
|
||||||
|
ReadStorage<'a, comp::Mass>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn handle(
|
fn handle(
|
||||||
events: impl ExactSizeIterator<Item = Self>,
|
events: impl ExactSizeIterator<Item = Self>,
|
||||||
(time, energy_change_events, buff_events, mut character_states, uids, stats): Self::SystemData<'_>,
|
(time, energy_change_events, buff_events, mut character_states, uids, stats, masses): Self::SystemData<'_>,
|
||||||
) {
|
) {
|
||||||
let mut energy_change_emitter = energy_change_events.emitter();
|
let mut energy_change_emitter = energy_change_events.emitter();
|
||||||
let mut buff_emitter = buff_events.emitter();
|
let mut buff_emitter = buff_events.emitter();
|
||||||
@ -1784,13 +1784,18 @@ impl ServerEvent for ParryHookEvent {
|
|||||||
} else {
|
} else {
|
||||||
BuffSource::World
|
BuffSource::World
|
||||||
};
|
};
|
||||||
|
let dest_info = buff::DestInfo {
|
||||||
|
stats: stats.get(attacker),
|
||||||
|
mass: masses.get(attacker),
|
||||||
|
};
|
||||||
let buff = buff::Buff::new(
|
let buff = buff::Buff::new(
|
||||||
BuffKind::Parried,
|
BuffKind::Parried,
|
||||||
data,
|
data,
|
||||||
vec![buff::BuffCategory::Physical],
|
vec![buff::BuffCategory::Physical],
|
||||||
source,
|
source,
|
||||||
*time,
|
*time,
|
||||||
stats.get(attacker),
|
dest_info,
|
||||||
|
masses.get(ev.defender),
|
||||||
);
|
);
|
||||||
buff_emitter.emit(BuffEvent {
|
buff_emitter.emit(BuffEvent {
|
||||||
entity: attacker,
|
entity: attacker,
|
||||||
|
@ -107,6 +107,7 @@ pub struct InventoryManipData<'a> {
|
|||||||
agents: ReadStorage<'a, comp::Agent>,
|
agents: ReadStorage<'a, comp::Agent>,
|
||||||
pets: ReadStorage<'a, comp::Pet>,
|
pets: ReadStorage<'a, comp::Pet>,
|
||||||
velocities: ReadStorage<'a, comp::Vel>,
|
velocities: ReadStorage<'a, comp::Vel>,
|
||||||
|
masses: ReadStorage<'a, comp::Mass>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServerEvent for InventoryManipEvent {
|
impl ServerEvent for InventoryManipEvent {
|
||||||
@ -663,6 +664,8 @@ impl ServerEvent for InventoryManipEvent {
|
|||||||
&data.msm,
|
&data.msm,
|
||||||
data.character_states.get(entity),
|
data.character_states.get(entity),
|
||||||
data.stats.get(entity),
|
data.stats.get(entity),
|
||||||
|
data.masses.get(entity),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -678,6 +681,8 @@ impl ServerEvent for InventoryManipEvent {
|
|||||||
&data.msm,
|
&data.msm,
|
||||||
data.character_states.get(entity),
|
data.character_states.get(entity),
|
||||||
data.stats.get(entity),
|
data.stats.get(entity),
|
||||||
|
data.masses.get(entity),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -692,6 +697,8 @@ impl ServerEvent for InventoryManipEvent {
|
|||||||
&data.msm,
|
&data.msm,
|
||||||
data.character_states.get(entity),
|
data.character_states.get(entity),
|
||||||
data.stats.get(entity),
|
data.stats.get(entity),
|
||||||
|
data.masses.get(entity),
|
||||||
|
None,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -16,16 +16,10 @@ use crate::{
|
|||||||
use common::{calendar::Calendar, resources::TimeOfDay, slowjob::SlowJobPool};
|
use common::{calendar::Calendar, resources::TimeOfDay, slowjob::SlowJobPool};
|
||||||
use common::{
|
use common::{
|
||||||
character::CharacterId,
|
character::CharacterId,
|
||||||
combat,
|
|
||||||
combat::DamageContributor,
|
|
||||||
comp::{
|
comp::{
|
||||||
self,
|
self, item::ItemKind, misc::PortalData, object, ChatType, Content, Group, Inventory,
|
||||||
item::{ItemKind, MaterialStatManifest},
|
LootOwner, Object, Player, Poise, Presence, PresenceKind, BASE_ABILITY_LIMIT,
|
||||||
misc::PortalData,
|
|
||||||
object, ChatType, Content, Group, Inventory, LootOwner, Object, Player, Poise, Presence,
|
|
||||||
PresenceKind, BASE_ABILITY_LIMIT,
|
|
||||||
},
|
},
|
||||||
effect::Effect,
|
|
||||||
link::{Is, Link, LinkHandle},
|
link::{Is, Link, LinkHandle},
|
||||||
mounting::{Mounting, Rider, VolumeMounting, VolumeRider},
|
mounting::{Mounting, Rider, VolumeMounting, VolumeRider},
|
||||||
resources::{Secs, Time},
|
resources::{Secs, Time},
|
||||||
@ -50,8 +44,6 @@ use tracing::{error, trace, warn};
|
|||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
pub trait StateExt {
|
pub trait StateExt {
|
||||||
/// Updates a component associated with the entity based on the `Effect`
|
|
||||||
fn apply_effect(&self, entity: EcsEntity, effect: Effect, source: Option<Uid>);
|
|
||||||
/// Build a non-player character
|
/// Build a non-player character
|
||||||
fn create_npc(
|
fn create_npc(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -177,109 +169,6 @@ pub trait StateExt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StateExt for State {
|
impl StateExt for State {
|
||||||
fn apply_effect(&self, entity: EcsEntity, effects: Effect, source: Option<Uid>) {
|
|
||||||
let msm = self.ecs().read_resource::<MaterialStatManifest>();
|
|
||||||
match effects {
|
|
||||||
Effect::Health(change) => {
|
|
||||||
self.ecs()
|
|
||||||
.write_storage::<comp::Health>()
|
|
||||||
.get_mut(entity)
|
|
||||||
.map(|mut health| health.change_by(change));
|
|
||||||
},
|
|
||||||
Effect::Damage(damage) => {
|
|
||||||
let inventories = self.ecs().read_storage::<Inventory>();
|
|
||||||
let stats = self.ecs().read_storage::<comp::Stats>();
|
|
||||||
let groups = self.ecs().read_storage::<Group>();
|
|
||||||
|
|
||||||
let damage_contributor = source.and_then(|uid| {
|
|
||||||
self.ecs().entity_from_uid(uid).map(|attacker_entity| {
|
|
||||||
DamageContributor::new(uid, groups.get(attacker_entity).cloned())
|
|
||||||
})
|
|
||||||
});
|
|
||||||
let time = self.ecs().read_resource::<Time>();
|
|
||||||
let change = damage.calculate_health_change(
|
|
||||||
combat::Damage::compute_damage_reduction(
|
|
||||||
Some(damage),
|
|
||||||
inventories.get(entity),
|
|
||||||
stats.get(entity),
|
|
||||||
&msm,
|
|
||||||
),
|
|
||||||
0.0,
|
|
||||||
damage_contributor,
|
|
||||||
None,
|
|
||||||
0.0,
|
|
||||||
1.0,
|
|
||||||
*time,
|
|
||||||
random(),
|
|
||||||
);
|
|
||||||
self.ecs()
|
|
||||||
.write_storage::<comp::Health>()
|
|
||||||
.get_mut(entity)
|
|
||||||
.map(|mut health| health.change_by(change));
|
|
||||||
},
|
|
||||||
Effect::Poise(poise) => {
|
|
||||||
let inventories = self.ecs().read_storage::<Inventory>();
|
|
||||||
let char_states = self.ecs().read_storage::<comp::CharacterState>();
|
|
||||||
let stats = self.ecs().read_storage::<comp::Stats>();
|
|
||||||
|
|
||||||
let change = Poise::apply_poise_reduction(
|
|
||||||
poise,
|
|
||||||
inventories.get(entity),
|
|
||||||
&msm,
|
|
||||||
char_states.get(entity),
|
|
||||||
stats.get(entity),
|
|
||||||
);
|
|
||||||
// Check to make sure the entity is not already stunned
|
|
||||||
if let Some(character_state) = self
|
|
||||||
.ecs()
|
|
||||||
.read_storage::<comp::CharacterState>()
|
|
||||||
.get(entity)
|
|
||||||
{
|
|
||||||
if !character_state.is_stunned() {
|
|
||||||
let groups = self.ecs().read_storage::<Group>();
|
|
||||||
let damage_contributor = source.and_then(|uid| {
|
|
||||||
self.ecs().entity_from_uid(uid).map(|attacker_entity| {
|
|
||||||
DamageContributor::new(uid, groups.get(attacker_entity).cloned())
|
|
||||||
})
|
|
||||||
});
|
|
||||||
let time = self.ecs().read_resource::<Time>();
|
|
||||||
let poise_change = comp::PoiseChange {
|
|
||||||
amount: change,
|
|
||||||
impulse: Vec3::zero(),
|
|
||||||
cause: None,
|
|
||||||
by: damage_contributor,
|
|
||||||
time: *time,
|
|
||||||
};
|
|
||||||
self.ecs()
|
|
||||||
.write_storage::<Poise>()
|
|
||||||
.get_mut(entity)
|
|
||||||
.map(|mut poise| poise.change(poise_change));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Effect::Buff(buff) => {
|
|
||||||
let time = self.ecs().read_resource::<Time>();
|
|
||||||
let stats = self.ecs().read_storage::<comp::Stats>();
|
|
||||||
self.ecs()
|
|
||||||
.write_storage::<comp::Buffs>()
|
|
||||||
.get_mut(entity)
|
|
||||||
.map(|mut buffs| {
|
|
||||||
buffs.insert(
|
|
||||||
comp::Buff::new(
|
|
||||||
buff.kind,
|
|
||||||
buff.data,
|
|
||||||
buff.cat_ids,
|
|
||||||
comp::BuffSource::Item,
|
|
||||||
*time,
|
|
||||||
stats.get(entity),
|
|
||||||
),
|
|
||||||
*time,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_npc(
|
fn create_npc(
|
||||||
&mut self,
|
&mut self,
|
||||||
pos: comp::Pos,
|
pos: comp::Pos,
|
||||||
|
@ -462,6 +462,27 @@ impl Animation for FinisherMeleeAnimation {
|
|||||||
next.control.orientation.rotate_z(move2 * 1.6);
|
next.control.orientation.rotate_z(move2 * 1.6);
|
||||||
next.control.position += Vec3::new(-16.0, 12.0, -8.0) * move2;
|
next.control.position += Vec3::new(-16.0, 12.0, -8.0) * move2;
|
||||||
},
|
},
|
||||||
|
Some("common.abilities.hammer.lung_pummel") => {
|
||||||
|
hammer_start(&mut next, s_a);
|
||||||
|
let (move1, move2, move3) = match stage_section {
|
||||||
|
Some(StageSection::Buildup) => (anim_time, 0.0, 0.0),
|
||||||
|
Some(StageSection::Action) => (1.0, anim_time, 0.0),
|
||||||
|
Some(StageSection::Recover) => (1.0, 1.0, anim_time),
|
||||||
|
_ => (0.0, 0.0, 0.0),
|
||||||
|
};
|
||||||
|
let pullback = 1.0 - move3;
|
||||||
|
let move1 = move1 * pullback;
|
||||||
|
let move2 = move2 * pullback;
|
||||||
|
|
||||||
|
twist_back(&mut next, move1, 1.9, 0.7, 0.3, 1.2);
|
||||||
|
next.control.orientation.rotate_x(move1 * 1.2);
|
||||||
|
next.control.orientation.rotate_z(move1 * 1.0);
|
||||||
|
next.control.position += Vec3::new(-12.0, 0.0, 0.0) * move1;
|
||||||
|
|
||||||
|
twist_forward(&mut next, move2, 3.4, 1.4, 0.9, 2.1);
|
||||||
|
next.control.orientation.rotate_z(move2 * -4.0);
|
||||||
|
next.control.position += Vec3::new(12.0, 0.0, 14.0) * move2;
|
||||||
|
},
|
||||||
_ => {},
|
_ => {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,7 +407,8 @@ fn get_buff_ident(buff: BuffKind) -> &'static str {
|
|||||||
| BuffKind::PotionSickness
|
| BuffKind::PotionSickness
|
||||||
| BuffKind::Polymorphed
|
| BuffKind::Polymorphed
|
||||||
| BuffKind::Heatstroke
|
| BuffKind::Heatstroke
|
||||||
| BuffKind::Rooted => {
|
| BuffKind::Rooted
|
||||||
|
| BuffKind::Winded => {
|
||||||
tracing::error!("Player was killed by a debuff that doesn't do damage!");
|
tracing::error!("Player was killed by a debuff that doesn't do damage!");
|
||||||
"mysterious"
|
"mysterious"
|
||||||
},
|
},
|
||||||
|
@ -90,7 +90,7 @@ fn maps_basic_melee() {
|
|||||||
multi_target: None,
|
multi_target: None,
|
||||||
simultaneous_hits: 1,
|
simultaneous_hits: 1,
|
||||||
custom_combo: None,
|
custom_combo: None,
|
||||||
precision_flank_multiplier: 1.0,
|
precision_flank_multipliers: Default::default(),
|
||||||
precision_flank_invert: false,
|
precision_flank_invert: false,
|
||||||
},
|
},
|
||||||
ori_modifier: 1.0,
|
ori_modifier: 1.0,
|
||||||
|
@ -326,6 +326,7 @@ image_ids! {
|
|||||||
hammer_spine_cracker: "voxygen.element.skills.hammer.spine_cracker",
|
hammer_spine_cracker: "voxygen.element.skills.hammer.spine_cracker",
|
||||||
hammer_breach: "voxygen.element.skills.hammer.breach",
|
hammer_breach: "voxygen.element.skills.hammer.breach",
|
||||||
hammer_pile_driver: "voxygen.element.skills.hammer.pile_driver",
|
hammer_pile_driver: "voxygen.element.skills.hammer.pile_driver",
|
||||||
|
hammer_lung_pummel: "voxygen.element.skills.hammer.lung_pummel",
|
||||||
// Skilltree Icons
|
// Skilltree Icons
|
||||||
health_plus_skill: "voxygen.element.skills.skilltree.health_plus",
|
health_plus_skill: "voxygen.element.skills.skilltree.health_plus",
|
||||||
energy_plus_skill: "voxygen.element.skills.skilltree.energy_plus",
|
energy_plus_skill: "voxygen.element.skills.skilltree.energy_plus",
|
||||||
@ -816,6 +817,7 @@ image_ids! {
|
|||||||
debuff_polymorphed: "voxygen.element.de_buffs.debuff_polymorphed",
|
debuff_polymorphed: "voxygen.element.de_buffs.debuff_polymorphed",
|
||||||
debuff_heatstroke_0: "voxygen.element.de_buffs.debuff_heatstroke_0",
|
debuff_heatstroke_0: "voxygen.element.de_buffs.debuff_heatstroke_0",
|
||||||
debuff_rooted: "voxygen.element.de_buffs.debuff_rooted",
|
debuff_rooted: "voxygen.element.de_buffs.debuff_rooted",
|
||||||
|
debuff_winded: "voxygen.element.de_buffs.debuff_winded",
|
||||||
|
|
||||||
// Animation Frames
|
// Animation Frames
|
||||||
// Buff Frame
|
// Buff Frame
|
||||||
|
@ -5276,6 +5276,7 @@ pub fn get_buff_image(buff: BuffKind, imgs: &Imgs) -> conrod_core::image::Id {
|
|||||||
BuffKind::Polymorphed => imgs.debuff_polymorphed,
|
BuffKind::Polymorphed => imgs.debuff_polymorphed,
|
||||||
BuffKind::Heatstroke => imgs.debuff_heatstroke_0,
|
BuffKind::Heatstroke => imgs.debuff_heatstroke_0,
|
||||||
BuffKind::Rooted => imgs.debuff_rooted,
|
BuffKind::Rooted => imgs.debuff_rooted,
|
||||||
|
BuffKind::Winded => imgs.debuff_winded,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,6 +211,8 @@ fn buff_key(buff: BuffKind) -> &'static str {
|
|||||||
BuffKind::PotionSickness => "buff-potionsickness",
|
BuffKind::PotionSickness => "buff-potionsickness",
|
||||||
BuffKind::Heatstroke => "buff-heatstroke",
|
BuffKind::Heatstroke => "buff-heatstroke",
|
||||||
BuffKind::Rooted => "buff-rooted",
|
BuffKind::Rooted => "buff-rooted",
|
||||||
|
|
||||||
|
BuffKind::Winded => "buff-winded",
|
||||||
// Neutral
|
// Neutral
|
||||||
BuffKind::Polymorphed => "buff-polymorphed",
|
BuffKind::Polymorphed => "buff-polymorphed",
|
||||||
}
|
}
|
||||||
@ -322,7 +324,8 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec<String> {
|
|||||||
| BuffKind::Berserk
|
| BuffKind::Berserk
|
||||||
| BuffKind::Heatstroke
|
| BuffKind::Heatstroke
|
||||||
| BuffKind::ScornfulTaunt
|
| BuffKind::ScornfulTaunt
|
||||||
| BuffKind::Rooted => Cow::Borrowed(""),
|
| BuffKind::Rooted
|
||||||
|
| BuffKind::Winded => Cow::Borrowed(""),
|
||||||
};
|
};
|
||||||
|
|
||||||
write!(&mut description, "{}", buff_desc).unwrap();
|
write!(&mut description, "{}", buff_desc).unwrap();
|
||||||
@ -374,7 +377,8 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec<String> {
|
|||||||
| BuffKind::Berserk
|
| BuffKind::Berserk
|
||||||
| BuffKind::Heatstroke
|
| BuffKind::Heatstroke
|
||||||
| BuffKind::ScornfulTaunt
|
| BuffKind::ScornfulTaunt
|
||||||
| BuffKind::Rooted => Cow::Borrowed(""),
|
| BuffKind::Rooted
|
||||||
|
| BuffKind::Winded => Cow::Borrowed(""),
|
||||||
}
|
}
|
||||||
} else if let BuffKind::Saturation
|
} else if let BuffKind::Saturation
|
||||||
| BuffKind::Regeneration
|
| BuffKind::Regeneration
|
||||||
@ -638,6 +642,7 @@ pub fn ability_image(imgs: &img_ids::Imgs, ability_id: &str) -> image::Id {
|
|||||||
"common.abilities.hammer.spine_cracker" => imgs.hammer_spine_cracker,
|
"common.abilities.hammer.spine_cracker" => imgs.hammer_spine_cracker,
|
||||||
"common.abilities.hammer.breach" => imgs.hammer_breach,
|
"common.abilities.hammer.breach" => imgs.hammer_breach,
|
||||||
"common.abilities.hammer.pile_driver" => imgs.hammer_pile_driver,
|
"common.abilities.hammer.pile_driver" => imgs.hammer_pile_driver,
|
||||||
|
"common.abilities.hammer.lung_pummel" => imgs.hammer_lung_pummel,
|
||||||
// Bow
|
// Bow
|
||||||
"common.abilities.bow.charged" => imgs.bow_m1,
|
"common.abilities.bow.charged" => imgs.bow_m1,
|
||||||
"common.abilities.bow.repeater" => imgs.bow_m2,
|
"common.abilities.bow.repeater" => imgs.bow_m2,
|
||||||
|
Loading…
Reference in New Issue
Block a user