Finish rooted debuff impl and Lung Pummel

This commit is contained in:
Sam 2024-02-25 13:42:12 -05:00
parent 76dc196996
commit 38c74bf182
31 changed files with 463 additions and 381 deletions

View File

@ -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"),

View File

@ -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,

View 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,
)

View File

@ -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

Binary file not shown.

BIN
assets/voxygen/element/skills/hammer/lung_pummel.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -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

View File

@ -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.

View File

@ -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() {

View File

@ -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,

View File

@ -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,

View File

@ -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,
) )
} }

View File

@ -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,
} }
} }

View File

@ -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,

View File

@ -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),
)), )),
}); });
}, },

View File

@ -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),
)), )),
}); });
} }

View File

@ -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,
); );

View File

@ -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,
)), )),
}); });
} }

View File

@ -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,
); );

View File

@ -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

View File

@ -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

View File

@ -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,
); );

View File

@ -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,

View File

@ -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,
); );
}, },
} }

View File

@ -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,

View File

@ -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;
},
_ => {}, _ => {},
} }

View File

@ -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"
}, },

View File

@ -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,

View File

@ -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

View File

@ -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,
} }
} }

View File

@ -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,