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(LungPummel), "common.abilities.hammer.lung_pummel"),
|
||||
Simple(Hammer(LungPummel), "common.abilities.hammer.lung_pummel"),
|
||||
// Simple(Hammer(HelmCrusher), "common.abilities.hammer.helm_crusher"),
|
||||
// Simple(Hammer(Rampart), "common.abilities.hammer.rampart"),
|
||||
// Simple(Hammer(Tenacity), "common.abilities.hammer.tenacity"),
|
||||
|
@ -14,7 +14,7 @@ BasicMelee(
|
||||
range: 3.0,
|
||||
angle: 15.0,
|
||||
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,
|
||||
),
|
||||
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,
|
||||
angle: 15.0,
|
||||
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,
|
||||
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
|
||||
buff-rooted = Rooted
|
||||
.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
|
||||
buff-text-over_seconds = over { $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
|
||||
.desc =
|
||||
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::ScornfulTaunt => "scornful_taunt",
|
||||
BuffKind::Rooted => "rooted",
|
||||
BuffKind::Winded => "winded",
|
||||
};
|
||||
let mut buff_parser = HashMap::new();
|
||||
for kind in BuffKind::iter() {
|
||||
|
@ -2,7 +2,7 @@ use crate::{
|
||||
comp::{
|
||||
ability::Capability,
|
||||
aura::{AuraKindVariant, EnteredAuras},
|
||||
buff::{Buff, BuffChange, BuffData, BuffKind, BuffSource},
|
||||
buff::{Buff, BuffChange, BuffData, BuffKind, BuffSource, DestInfo},
|
||||
inventory::{
|
||||
item::{
|
||||
armor::Protection,
|
||||
@ -13,7 +13,7 @@ use crate::{
|
||||
},
|
||||
skillset::SkillGroupKind,
|
||||
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::{
|
||||
BuffEvent, ComboChangeEvent, EmitExt, EnergyChangeEvent, EntityAttackedHookEvent,
|
||||
@ -72,6 +72,7 @@ pub struct AttackerInfo<'a> {
|
||||
pub combo: Option<&'a Combo>,
|
||||
pub inventory: Option<&'a Inventory>,
|
||||
pub stats: Option<&'a Stats>,
|
||||
pub mass: Option<&'a Mass>,
|
||||
}
|
||||
|
||||
pub struct TargetInfo<'a> {
|
||||
@ -85,6 +86,7 @@ pub struct TargetInfo<'a> {
|
||||
pub char_state: Option<&'a CharacterState>,
|
||||
pub energy: Option<&'a Energy>,
|
||||
pub buffs: Option<&'a Buffs>,
|
||||
pub mass: Option<&'a Mass>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
@ -464,8 +466,8 @@ impl Attack {
|
||||
entity: target.entity,
|
||||
buff_change: BuffChange::Add(b.to_buff(
|
||||
time,
|
||||
attacker.map(|a| a.uid),
|
||||
target.stats,
|
||||
attacker,
|
||||
target,
|
||||
applied_damage,
|
||||
strength_modifier,
|
||||
)),
|
||||
@ -697,8 +699,8 @@ impl Attack {
|
||||
entity: target.entity,
|
||||
buff_change: BuffChange::Add(b.to_buff(
|
||||
time,
|
||||
attacker.map(|a| a.uid),
|
||||
target.stats,
|
||||
attacker,
|
||||
target,
|
||||
accumulated_damage,
|
||||
strength_modifier,
|
||||
)),
|
||||
@ -1322,17 +1324,21 @@ impl CombatBuff {
|
||||
fn to_buff(
|
||||
self,
|
||||
time: Time,
|
||||
uid: Option<Uid>,
|
||||
tgt_stats: Option<&Stats>,
|
||||
attacker_info: Option<AttackerInfo>,
|
||||
target_info: &TargetInfo,
|
||||
damage: f32,
|
||||
strength_modifier: f32,
|
||||
) -> Buff {
|
||||
// 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 }
|
||||
} else {
|
||||
BuffSource::Unknown
|
||||
};
|
||||
let dest_info = DestInfo {
|
||||
stats: target_info.stats,
|
||||
mass: target_info.mass,
|
||||
};
|
||||
Buff::new(
|
||||
self.kind,
|
||||
BuffData::new(
|
||||
@ -1342,7 +1348,8 @@ impl CombatBuff {
|
||||
Vec::new(),
|
||||
source,
|
||||
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(
|
||||
attack_dir: Vec3<f32>,
|
||||
target_ori: Option<&Ori>,
|
||||
precision_flank_multiplier: f32,
|
||||
precision_flank_multipliers: FlankMults,
|
||||
precision_flank_invert: bool,
|
||||
) -> Option<f32> {
|
||||
let angle = target_ori.map(|t_ori| {
|
||||
@ -1657,16 +1664,38 @@ pub fn precision_mult_from_flank(
|
||||
})
|
||||
});
|
||||
match angle {
|
||||
Some(angle) if angle < FULL_FLANK_ANGLE => {
|
||||
Some(MAX_BACK_FLANK_PRECISION * precision_flank_multiplier)
|
||||
},
|
||||
Some(angle) if angle < FULL_FLANK_ANGLE => Some(
|
||||
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(MAX_SIDE_FLANK_PRECISION * precision_flank_multiplier)
|
||||
Some(MAX_SIDE_FLANK_PRECISION * precision_flank_multipliers.side)
|
||||
},
|
||||
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 {
|
||||
match char_state {
|
||||
CharacterState::BasicBlock(data) => data.static_data.block_strength,
|
||||
|
@ -1113,7 +1113,7 @@ impl Default for CharacterAbility {
|
||||
attack_effect: None,
|
||||
simultaneous_hits: 1,
|
||||
custom_combo: None,
|
||||
precision_flank_multiplier: 1.0,
|
||||
precision_flank_multipliers: Default::default(),
|
||||
precision_flank_invert: false,
|
||||
},
|
||||
ori_modifier: 1.0,
|
||||
|
@ -4,7 +4,7 @@ use crate::{
|
||||
AttackEffect, CombatBuff, CombatBuffStrength, CombatEffect, CombatRequirement,
|
||||
DamagedEffect, DeathEffect,
|
||||
},
|
||||
comp::{aura::AuraKey, Stats},
|
||||
comp::{aura::AuraKey, Mass, Stats},
|
||||
resources::{Secs, Time},
|
||||
uid::Uid,
|
||||
};
|
||||
@ -185,6 +185,11 @@ pub enum BuffKind {
|
||||
/// is higher than the strength allows for, duration gets reduced using a
|
||||
/// mutiplier from the ratio of masses.
|
||||
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
|
||||
/// Changed into another body.
|
||||
Polymorphed,
|
||||
@ -248,7 +253,8 @@ impl BuffKind {
|
||||
| BuffKind::Parried
|
||||
| BuffKind::PotionSickness
|
||||
| BuffKind::Heatstroke
|
||||
| BuffKind::Rooted => BuffDescriptor::SimpleNegative,
|
||||
| BuffKind::Rooted
|
||||
| BuffKind::Winded => BuffDescriptor::SimpleNegative,
|
||||
BuffKind::Polymorphed => BuffDescriptor::Complex,
|
||||
}
|
||||
}
|
||||
@ -286,7 +292,10 @@ impl BuffKind {
|
||||
|
||||
pub fn effects(&self, data: &BuffData, stats: Option<&Stats>) -> Vec<BuffEffect> {
|
||||
// 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_scaling2 = |a| a / (a + 1.0);
|
||||
let instance = rand::random();
|
||||
match self {
|
||||
BuffKind::Bleeding => vec![BuffEffect::HealthChangeOverTime {
|
||||
@ -489,6 +498,10 @@ impl BuffKind {
|
||||
}),
|
||||
],
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
#[allow(clippy::single_match)]
|
||||
match self {
|
||||
BuffKind::Rooted => {
|
||||
let source_mass = 50.0;
|
||||
let dest_mass = 50.0_f64;
|
||||
let source_mass = source_mass.map_or(50.0, |m| m.0 as f64);
|
||||
let dest_mass = dest_mass.map_or(50.0, |m| m.0 as f64);
|
||||
let ratio = (source_mass / dest_mass).min(1.0);
|
||||
data.duration = data.duration.map(|dur| Secs(dur.0 * ratio));
|
||||
},
|
||||
@ -710,10 +728,12 @@ impl Buff {
|
||||
cat_ids: Vec<BuffCategory>,
|
||||
source: BuffSource,
|
||||
time: Time,
|
||||
stats: Option<&Stats>,
|
||||
dest_info: DestInfo,
|
||||
// Create source_info if we need more parameters from source
|
||||
source_mass: Option<&Mass>,
|
||||
) -> Self {
|
||||
let data = kind.modify_data(data);
|
||||
let effects = kind.effects(&data, stats);
|
||||
let data = kind.modify_data(data, source_mass, dest_info.mass);
|
||||
let effects = kind.effects(&data, dest_info.stats);
|
||||
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 end_time = if cat_ids
|
||||
@ -958,6 +978,12 @@ impl Component for Buffs {
|
||||
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)]
|
||||
pub mod tests {
|
||||
use crate::comp::buff::*;
|
||||
@ -973,6 +999,7 @@ pub mod tests {
|
||||
Vec::new(),
|
||||
BuffSource::Unknown,
|
||||
time,
|
||||
DestInfo::default(),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
use crate::{
|
||||
combat::{
|
||||
Attack, AttackDamage, AttackEffect, CombatBuff, CombatBuffStrength, CombatEffect,
|
||||
CombatRequirement, Damage, DamageKind, DamageSource, GroupTarget, Knockback, KnockbackDir,
|
||||
CombatRequirement, Damage, DamageKind, DamageSource, FlankMults, GroupTarget, Knockback,
|
||||
KnockbackDir,
|
||||
},
|
||||
comp::{
|
||||
buff::BuffKind,
|
||||
@ -23,7 +24,7 @@ pub struct Melee {
|
||||
pub multi_target: Option<MultiTarget>,
|
||||
pub break_block: Option<(Vec3<i32>, Option<ToolKind>)>,
|
||||
pub simultaneous_hits: u32,
|
||||
pub precision_flank_multiplier: f32,
|
||||
pub precision_flank_multipliers: FlankMults,
|
||||
pub precision_flank_invert: bool,
|
||||
}
|
||||
|
||||
@ -53,7 +54,6 @@ impl Component for Melee {
|
||||
}
|
||||
|
||||
fn default_simultaneous_hits() -> u32 { 1 }
|
||||
fn default_precision_flank_multiplier() -> f32 { 1.0 }
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Scaled {
|
||||
@ -80,8 +80,8 @@ pub struct MeleeConstructor {
|
||||
#[serde(default = "default_simultaneous_hits")]
|
||||
pub simultaneous_hits: u32,
|
||||
pub custom_combo: Option<CustomCombo>,
|
||||
#[serde(default = "default_precision_flank_multiplier")]
|
||||
pub precision_flank_multiplier: f32,
|
||||
#[serde(default)]
|
||||
pub precision_flank_multipliers: FlankMults,
|
||||
#[serde(default)]
|
||||
pub precision_flank_invert: bool,
|
||||
}
|
||||
@ -393,7 +393,7 @@ impl MeleeConstructor {
|
||||
multi_target: self.multi_target,
|
||||
break_block: None,
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
comp::{
|
||||
buff::{Buff, BuffCategory, BuffChange, BuffData, BuffKind, BuffSource},
|
||||
buff::{Buff, BuffCategory, BuffChange, BuffData, BuffKind, BuffSource, DestInfo},
|
||||
character_state::OutputEvents,
|
||||
CharacterState, StateUpdate,
|
||||
},
|
||||
@ -116,6 +116,11 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
}
|
||||
|
||||
let dest_info = DestInfo {
|
||||
stats: Some(data.stats),
|
||||
mass: Some(data.mass),
|
||||
};
|
||||
|
||||
// Creates buff
|
||||
let buff = Buff::new(
|
||||
self.static_data.buff_kind,
|
||||
@ -126,7 +131,8 @@ impl CharacterBehavior for Data {
|
||||
buff_cat_ids,
|
||||
BuffSource::Character { by: *data.uid },
|
||||
*data.time,
|
||||
Some(data.stats),
|
||||
dest_info,
|
||||
Some(data.mass),
|
||||
);
|
||||
output_events.emit_server(BuffEvent {
|
||||
entity: data.entity,
|
||||
|
@ -3,7 +3,7 @@ use crate::{
|
||||
comp::{
|
||||
ability::{AbilityInitEvent, AbilityMeta, Capability, SpecifiedAbility, Stance},
|
||||
arthropod, biped_large, biped_small, bird_medium,
|
||||
buff::{Buff, BuffCategory, BuffChange, BuffData, BuffSource},
|
||||
buff::{Buff, BuffCategory, BuffChange, BuffData, BuffSource, DestInfo},
|
||||
character_state::OutputEvents,
|
||||
controller::InventoryManip,
|
||||
golem,
|
||||
@ -1332,6 +1332,10 @@ fn handle_ability(
|
||||
strength,
|
||||
duration,
|
||||
} => {
|
||||
let dest_info = DestInfo {
|
||||
stats: Some(data.stats),
|
||||
mass: Some(data.mass),
|
||||
};
|
||||
output_events.emit_server(BuffEvent {
|
||||
entity: data.entity,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
@ -1340,7 +1344,8 @@ fn handle_ability(
|
||||
vec![BuffCategory::SelfBuff],
|
||||
BuffSource::Character { by: *data.uid },
|
||||
*data.time,
|
||||
Some(data.stats),
|
||||
dest_info,
|
||||
Some(data.mass),
|
||||
)),
|
||||
});
|
||||
},
|
||||
|
@ -4,9 +4,9 @@ use common::{
|
||||
combat,
|
||||
comp::{
|
||||
aura::{AuraChange, AuraKey, AuraKind, AuraTarget, EnteredAuras},
|
||||
buff::{Buff, BuffCategory, BuffChange, BuffSource},
|
||||
buff::{Buff, BuffCategory, BuffChange, BuffSource, DestInfo},
|
||||
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_emitters,
|
||||
@ -41,6 +41,7 @@ pub struct ReadData<'a> {
|
||||
buffs: ReadStorage<'a, Buffs>,
|
||||
auras: ReadStorage<'a, Auras>,
|
||||
entered_auras: ReadStorage<'a, EnteredAuras>,
|
||||
masses: ReadStorage<'a, Mass>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -86,80 +87,74 @@ impl<'a> System<'a> for Sys {
|
||||
read_data.healths.get(target)?,
|
||||
read_data.uids.get(target)?,
|
||||
read_data.entered_auras.get(target)?,
|
||||
read_data.stats.get(target),
|
||||
))
|
||||
})
|
||||
});
|
||||
target_iter.for_each(
|
||||
|(target, target_pos, health, target_uid, entered_auras, stats)| {
|
||||
let target_buffs = match read_data.buffs.get(target) {
|
||||
Some(buff) => buff,
|
||||
None => return,
|
||||
target_iter.for_each(|(target, target_pos, health, target_uid, entered_auras)| {
|
||||
let target_buffs = match read_data.buffs.get(target) {
|
||||
Some(buff) => buff,
|
||||
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
|
||||
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
|
||||
};
|
||||
let allow_friendly_fire =
|
||||
combat::allow_friendly_fire(&read_data.entered_auras, entity, target);
|
||||
|
||||
let allow_friendly_fire = combat::allow_friendly_fire(
|
||||
&read_data.entered_auras,
|
||||
entity,
|
||||
target,
|
||||
);
|
||||
|
||||
if !(allow_friendly_fire && entity != target
|
||||
|| match aura.target {
|
||||
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));
|
||||
}
|
||||
if !(allow_friendly_fire && entity != target
|
||||
|| match aura.target {
|
||||
AuraTarget::GroupOf(uid) => same_group(uid),
|
||||
AuraTarget::NotGroupOf(uid) => !same_group(uid),
|
||||
AuraTarget::All => true,
|
||||
})
|
||||
{
|
||||
return;
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
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() {
|
||||
emitters.emit(AuraEvent {
|
||||
@ -200,11 +195,11 @@ impl<'a> System<'a> for Sys {
|
||||
fn activate_aura(
|
||||
key: AuraKey,
|
||||
aura: &Aura,
|
||||
applier: Uid,
|
||||
applier: EcsEntity,
|
||||
applier_uid: Uid,
|
||||
target: EcsEntity,
|
||||
health: &Health,
|
||||
target_buffs: &Buffs,
|
||||
stats: Option<&Stats>,
|
||||
allow_friendly_fire: bool,
|
||||
read_data: &ReadData,
|
||||
emitters: &mut impl EmitExt<BuffEvent>,
|
||||
@ -286,20 +281,25 @@ fn activate_aura(
|
||||
let emit_buff = !target_buffs.buffs.iter().any(|(_, buff)| {
|
||||
buff.cat_ids
|
||||
.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.data.strength >= data.strength
|
||||
});
|
||||
if emit_buff {
|
||||
let dest_info = DestInfo {
|
||||
stats: read_data.stats.get(target),
|
||||
mass: read_data.masses.get(target),
|
||||
};
|
||||
emitters.emit(BuffEvent {
|
||||
entity: target,
|
||||
buff_change: BuffChange::Add(Buff::new(
|
||||
kind,
|
||||
data,
|
||||
vec![category, BuffCategory::FromActiveAura(applier, key)],
|
||||
vec![category, BuffCategory::FromActiveAura(applier_uid, key)],
|
||||
source,
|
||||
*read_data.time,
|
||||
stats,
|
||||
dest_info,
|
||||
read_data.masses.get(applier),
|
||||
)),
|
||||
});
|
||||
}
|
||||
|
@ -3,8 +3,8 @@ use common::{
|
||||
comp::{
|
||||
agent::{Sound, SoundKind},
|
||||
aura::EnteredAuras,
|
||||
Alignment, Beam, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Ori,
|
||||
Player, Pos, Scale, Stats,
|
||||
Alignment, Beam, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory,
|
||||
Mass, Ori, Player, Pos, Scale, Stats,
|
||||
},
|
||||
event::{self, EmitExt, EventBus},
|
||||
event_emitters,
|
||||
@ -63,6 +63,7 @@ pub struct ReadData<'a> {
|
||||
entered_auras: ReadStorage<'a, EnteredAuras>,
|
||||
outcomes: Read<'a, EventBus<Outcome>>,
|
||||
events: ReadAttackEvents<'a>,
|
||||
masses: ReadStorage<'a, Mass>,
|
||||
}
|
||||
|
||||
/// 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),
|
||||
inventory: read_data.inventories.get(entity),
|
||||
stats: read_data.stats.get(entity),
|
||||
mass: read_data.masses.get(entity),
|
||||
});
|
||||
|
||||
let target_info = TargetInfo {
|
||||
@ -243,6 +245,7 @@ impl<'a> System<'a> for Sys {
|
||||
char_state: read_data.character_states.get(target),
|
||||
energy: read_data.energies.get(target),
|
||||
buffs: read_data.buffs.get(target),
|
||||
mass: read_data.masses.get(target),
|
||||
};
|
||||
|
||||
let target_dodging = read_data
|
||||
@ -263,7 +266,7 @@ impl<'a> System<'a> for Sys {
|
||||
let precision_from_flank = combat::precision_mult_from_flank(
|
||||
beam.bezier.ctrl - beam.bezier.start,
|
||||
target_info.ori,
|
||||
1.0,
|
||||
Default::default(),
|
||||
false,
|
||||
);
|
||||
|
||||
|
@ -6,12 +6,12 @@ use common::{
|
||||
body::{object, Body},
|
||||
buff::{
|
||||
Buff, BuffCategory, BuffChange, BuffData, BuffEffect, BuffKey, BuffKind, BuffSource,
|
||||
Buffs,
|
||||
Buffs, DestInfo,
|
||||
},
|
||||
fluid_dynamics::{Fluid, LiquidKind},
|
||||
item::MaterialStatManifest,
|
||||
Alignment, Energy, Group, Health, HealthChange, Inventory, LightEmitter, ModifierKind,
|
||||
PhysicsState, Player, Pos, Stats,
|
||||
Alignment, Energy, Group, Health, HealthChange, Inventory, LightEmitter, Mass,
|
||||
ModifierKind, PhysicsState, Player, Pos, Stats,
|
||||
},
|
||||
event::{
|
||||
BuffEvent, ChangeBodyEvent, CreateSpriteEvent, EmitExt, EnergyChangeEvent,
|
||||
@ -68,6 +68,7 @@ pub struct ReadData<'a> {
|
||||
alignments: ReadStorage<'a, Alignment>,
|
||||
players: ReadStorage<'a, Player>,
|
||||
uids: ReadStorage<'a, Uid>,
|
||||
masses: ReadStorage<'a, Mass>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@ -153,10 +154,16 @@ impl<'a> System<'a> for Sys {
|
||||
&read_data.energies,
|
||||
read_data.uids.maybe(),
|
||||
read_data.physics_states.maybe(),
|
||||
read_data.masses.maybe(),
|
||||
)
|
||||
.lend_join();
|
||||
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
|
||||
if let Some(physics_state) = physics_state {
|
||||
// Set nearby entities on fire if burning
|
||||
@ -185,7 +192,13 @@ impl<'a> System<'a> for Sys {
|
||||
vec![BuffCategory::Natural],
|
||||
source,
|
||||
*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(),
|
||||
BuffSource::World,
|
||||
*read_data.time,
|
||||
Some(&stat),
|
||||
dest_info,
|
||||
None,
|
||||
)),
|
||||
});
|
||||
}
|
||||
@ -221,7 +235,8 @@ impl<'a> System<'a> for Sys {
|
||||
Vec::new(),
|
||||
BuffSource::World,
|
||||
*read_data.time,
|
||||
Some(&stat),
|
||||
dest_info,
|
||||
None,
|
||||
)),
|
||||
});
|
||||
}
|
||||
@ -251,7 +266,8 @@ impl<'a> System<'a> for Sys {
|
||||
Vec::new(),
|
||||
BuffSource::World,
|
||||
*read_data.time,
|
||||
Some(&stat),
|
||||
dest_info,
|
||||
None,
|
||||
)),
|
||||
});
|
||||
}
|
||||
@ -269,7 +285,8 @@ impl<'a> System<'a> for Sys {
|
||||
Vec::new(),
|
||||
BuffSource::World,
|
||||
*read_data.time,
|
||||
Some(&stat),
|
||||
dest_info,
|
||||
None,
|
||||
)),
|
||||
});
|
||||
}
|
||||
@ -286,7 +303,8 @@ impl<'a> System<'a> for Sys {
|
||||
Vec::new(),
|
||||
BuffSource::World,
|
||||
*read_data.time,
|
||||
Some(&stat),
|
||||
dest_info,
|
||||
None,
|
||||
)),
|
||||
});
|
||||
}
|
||||
@ -303,7 +321,8 @@ impl<'a> System<'a> for Sys {
|
||||
Vec::new(),
|
||||
BuffSource::World,
|
||||
*read_data.time,
|
||||
Some(&stat),
|
||||
dest_info,
|
||||
None,
|
||||
)),
|
||||
});
|
||||
// When standing on IceSpike also apply Frozen
|
||||
@ -315,7 +334,8 @@ impl<'a> System<'a> for Sys {
|
||||
Vec::new(),
|
||||
BuffSource::World,
|
||||
*read_data.time,
|
||||
Some(&stat),
|
||||
dest_info,
|
||||
None,
|
||||
)),
|
||||
});
|
||||
}
|
||||
@ -332,7 +352,8 @@ impl<'a> System<'a> for Sys {
|
||||
Vec::new(),
|
||||
BuffSource::World,
|
||||
*read_data.time,
|
||||
Some(&stat),
|
||||
dest_info,
|
||||
None,
|
||||
)),
|
||||
});
|
||||
}
|
||||
@ -353,7 +374,8 @@ impl<'a> System<'a> for Sys {
|
||||
vec![BuffCategory::Natural],
|
||||
BuffSource::World,
|
||||
*read_data.time,
|
||||
Some(&stat),
|
||||
dest_info,
|
||||
None,
|
||||
)),
|
||||
});
|
||||
} else if matches!(
|
||||
@ -429,7 +451,8 @@ impl<'a> System<'a> for Sys {
|
||||
.collect::<Vec<_>>(),
|
||||
buff.source,
|
||||
*read_data.time,
|
||||
Some(&stat),
|
||||
dest_info,
|
||||
None,
|
||||
)),
|
||||
});
|
||||
}
|
||||
|
@ -4,8 +4,8 @@ use common::{
|
||||
agent::{Sound, SoundKind},
|
||||
aura::EnteredAuras,
|
||||
melee::MultiTarget,
|
||||
Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Melee,
|
||||
Ori, Player, Pos, Scale, Stats,
|
||||
Alignment, Body, Buffs, CharacterState, Combo, Energy, Group, Health, Inventory, Mass,
|
||||
Melee, Ori, Player, Pos, Scale, Stats,
|
||||
},
|
||||
event::{self, EmitExt, EventBus},
|
||||
event_emitters,
|
||||
@ -62,6 +62,7 @@ pub struct ReadData<'a> {
|
||||
buffs: ReadStorage<'a, Buffs>,
|
||||
entered_auras: ReadStorage<'a, EnteredAuras>,
|
||||
events: ReadAttackEvents<'a>,
|
||||
masses: ReadStorage<'a, Mass>,
|
||||
}
|
||||
|
||||
/// 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),
|
||||
inventory: read_data.inventories.get(attacker),
|
||||
stats: read_data.stats.get(attacker),
|
||||
mass: read_data.masses.get(attacker),
|
||||
});
|
||||
|
||||
let target_ori = read_data.orientations.get(target);
|
||||
@ -228,6 +230,7 @@ impl<'a> System<'a> for Sys {
|
||||
char_state: target_char_state,
|
||||
energy: read_data.energies.get(target),
|
||||
buffs: read_data.buffs.get(target),
|
||||
mass: read_data.masses.get(target),
|
||||
};
|
||||
|
||||
// PvP check
|
||||
@ -248,7 +251,7 @@ impl<'a> System<'a> for Sys {
|
||||
.try_normalized()
|
||||
.unwrap_or(ori.look_vec()),
|
||||
target_ori,
|
||||
melee_attack.precision_flank_multiplier,
|
||||
melee_attack.precision_flank_multipliers,
|
||||
melee_attack.precision_flank_invert,
|
||||
);
|
||||
|
||||
|
@ -4,7 +4,7 @@ use common::{
|
||||
agent::{Sound, SoundKind},
|
||||
aura::EnteredAuras,
|
||||
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::{
|
||||
BonkEvent, BuffEvent, ComboChangeEvent, DeleteEvent, EmitExt, Emitter, EnergyChangeEvent,
|
||||
@ -73,6 +73,7 @@ pub struct ReadData<'a> {
|
||||
terrain: ReadExpect<'a, TerrainGrid>,
|
||||
buffs: ReadStorage<'a, Buffs>,
|
||||
entered_auras: ReadStorage<'a, EnteredAuras>,
|
||||
masses: ReadStorage<'a, Mass>,
|
||||
}
|
||||
|
||||
/// This system is responsible for handling projectile effect triggers
|
||||
@ -340,6 +341,7 @@ fn dispatch_hit(
|
||||
combo: read_data.combos.get(entity),
|
||||
inventory: read_data.inventories.get(entity),
|
||||
stats: read_data.stats.get(entity),
|
||||
mass: read_data.masses.get(entity),
|
||||
});
|
||||
|
||||
let target_info = TargetInfo {
|
||||
@ -353,6 +355,7 @@ fn dispatch_hit(
|
||||
char_state: read_data.character_states.get(target),
|
||||
energy: read_data.energies.get(target),
|
||||
buffs: read_data.buffs.get(target),
|
||||
mass: read_data.masses.get(target),
|
||||
};
|
||||
|
||||
// TODO: Is it possible to have projectile without body??
|
||||
@ -389,8 +392,12 @@ fn dispatch_hit(
|
||||
.and_then(|cs| cs.attack_immunities())
|
||||
.map_or(false, |i| i.projectiles);
|
||||
|
||||
let precision_from_flank =
|
||||
combat::precision_mult_from_flank(*projectile_dir, target_info.ori, 1.0, false);
|
||||
let precision_from_flank = combat::precision_mult_from_flank(
|
||||
*projectile_dir,
|
||||
target_info.ori,
|
||||
Default::default(),
|
||||
false,
|
||||
);
|
||||
|
||||
let precision_from_head = {
|
||||
// This performs a cylinder and line segment intersection check. The cylinder is
|
||||
|
@ -4,7 +4,7 @@ use common::{
|
||||
agent::{Sound, SoundKind},
|
||||
aura::EnteredAuras,
|
||||
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,
|
||||
},
|
||||
event::{
|
||||
@ -64,6 +64,7 @@ pub struct ReadData<'a> {
|
||||
character_states: ReadStorage<'a, CharacterState>,
|
||||
buffs: ReadStorage<'a, Buffs>,
|
||||
entered_auras: ReadStorage<'a, EnteredAuras>,
|
||||
masses: ReadStorage<'a, Mass>,
|
||||
}
|
||||
|
||||
/// 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),
|
||||
inventory: read_data.inventories.get(entity),
|
||||
stats: read_data.stats.get(entity),
|
||||
mass: read_data.masses.get(entity),
|
||||
});
|
||||
|
||||
let target_info = TargetInfo {
|
||||
@ -246,6 +248,7 @@ impl<'a> System<'a> for Sys {
|
||||
char_state: read_data.character_states.get(target),
|
||||
energy: read_data.energies.get(target),
|
||||
buffs: read_data.buffs.get(target),
|
||||
mass: read_data.masses.get(target),
|
||||
};
|
||||
|
||||
let target_dodging = read_data
|
||||
|
@ -30,7 +30,7 @@ use common::{
|
||||
comp::{
|
||||
self,
|
||||
aura::{AuraKindVariant, AuraTarget},
|
||||
buff::{Buff, BuffData, BuffKind, BuffSource, MiscBuffData},
|
||||
buff::{Buff, BuffData, BuffKind, BuffSource, DestInfo, MiscBuffData},
|
||||
inventory::{
|
||||
item::{all_items_expect, tool::AbilityMap, MaterialStatManifest, Quality},
|
||||
slot::Slot,
|
||||
@ -4552,7 +4552,8 @@ fn build_buff(
|
||||
| BuffKind::PotionSickness
|
||||
| BuffKind::Heatstroke
|
||||
| BuffKind::ScornfulTaunt
|
||||
| BuffKind::Rooted => {
|
||||
| BuffKind::Rooted
|
||||
| BuffKind::Winded => {
|
||||
if buff_kind.is_simple() {
|
||||
unreachable!("is_simple() above")
|
||||
} else {
|
||||
@ -4569,8 +4570,13 @@ fn cast_buff(buffkind: BuffKind, data: BuffData, server: &mut Server, target: Ec
|
||||
let ecs = &server.state.ecs();
|
||||
let mut buffs_all = ecs.write_storage::<comp::Buffs>();
|
||||
let stats = ecs.read_storage::<comp::Stats>();
|
||||
let masses = ecs.read_storage::<comp::Mass>();
|
||||
let time = ecs.read_resource::<Time>();
|
||||
if let Some(mut buffs) = buffs_all.get_mut(target) {
|
||||
let dest_info = DestInfo {
|
||||
stats: stats.get(target),
|
||||
mass: masses.get(target),
|
||||
};
|
||||
buffs.insert(
|
||||
Buff::new(
|
||||
buffkind,
|
||||
@ -4578,7 +4584,8 @@ fn cast_buff(buffkind: BuffKind, data: BuffData, server: &mut Server, target: Ec
|
||||
vec![],
|
||||
BuffSource::Command,
|
||||
*time,
|
||||
stats.get(target),
|
||||
dest_info,
|
||||
None,
|
||||
),
|
||||
*time,
|
||||
);
|
||||
|
@ -332,6 +332,7 @@ pub struct DestroyEventData<'a> {
|
||||
#[cfg(feature = "worldgen")]
|
||||
presences: ReadStorage<'a, Presence>,
|
||||
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
|
||||
@ -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 killed_uid = data.uids.get(ev.entity);
|
||||
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() {
|
||||
match effect {
|
||||
DeathEffect::AttackerBuff {
|
||||
@ -397,6 +399,10 @@ impl ServerEvent for DestroyEvent {
|
||||
duration,
|
||||
} => {
|
||||
if let Some(attacker) = attacker_entity {
|
||||
let dest_info = buff::DestInfo {
|
||||
stats: attacker_stats,
|
||||
mass: attacker_mass,
|
||||
};
|
||||
buff_emitter.emit(BuffEvent {
|
||||
entity: attacker,
|
||||
buff_change: buff::BuffChange::Add(buff::Buff::new(
|
||||
@ -409,7 +415,8 @@ impl ServerEvent for DestroyEvent {
|
||||
BuffSource::World
|
||||
},
|
||||
*data.time,
|
||||
attacker_stats,
|
||||
dest_info,
|
||||
data.masses.get(ev.entity),
|
||||
)),
|
||||
});
|
||||
}
|
||||
@ -976,80 +983,54 @@ event_emitters! {
|
||||
}
|
||||
}
|
||||
|
||||
impl ServerEvent for ExplosionEvent {
|
||||
type SystemData<'a> = (
|
||||
Entities<'a>,
|
||||
Write<'a, BlockChange>,
|
||||
Read<'a, Settings>,
|
||||
Read<'a, Time>,
|
||||
Read<'a, IdMaps>,
|
||||
Read<'a, CachedSpatialGrid>,
|
||||
ReadExpect<'a, TerrainGrid>,
|
||||
ReadExpect<'a, MaterialStatManifest>,
|
||||
ReadExplosionEvents<'a>,
|
||||
Read<'a, EventBus<Outcome>>,
|
||||
ReadStorage<'a, Group>,
|
||||
ReadStorage<'a, Auras>,
|
||||
ReadStorage<'a, Pos>,
|
||||
ReadStorage<'a, Player>,
|
||||
ReadStorage<'a, Energy>,
|
||||
ReadStorage<'a, comp::Combo>,
|
||||
ReadStorage<'a, Inventory>,
|
||||
ReadStorage<'a, Alignment>,
|
||||
ReadStorage<'a, EnteredAuras>,
|
||||
ReadStorage<'a, comp::Buffs>,
|
||||
ReadStorage<'a, comp::Stats>,
|
||||
ReadStorage<'a, Health>,
|
||||
ReadStorage<'a, Body>,
|
||||
ReadStorage<'a, comp::Ori>,
|
||||
ReadStorage<'a, CharacterState>,
|
||||
ReadStorage<'a, Uid>,
|
||||
);
|
||||
#[derive(SystemData)]
|
||||
pub struct ExplosionData<'a> {
|
||||
entities: Entities<'a>,
|
||||
block_change: Write<'a, BlockChange>,
|
||||
settings: Read<'a, Settings>,
|
||||
time: Read<'a, Time>,
|
||||
id_maps: Read<'a, IdMaps>,
|
||||
spatial_grid: Read<'a, CachedSpatialGrid>,
|
||||
terrain: ReadExpect<'a, TerrainGrid>,
|
||||
msm: ReadExpect<'a, MaterialStatManifest>,
|
||||
event_busses: ReadExplosionEvents<'a>,
|
||||
outcomes: Read<'a, EventBus<Outcome>>,
|
||||
groups: ReadStorage<'a, Group>,
|
||||
auras: ReadStorage<'a, Auras>,
|
||||
positions: ReadStorage<'a, Pos>,
|
||||
players: ReadStorage<'a, Player>,
|
||||
energies: ReadStorage<'a, Energy>,
|
||||
combos: ReadStorage<'a, comp::Combo>,
|
||||
inventories: ReadStorage<'a, Inventory>,
|
||||
alignments: ReadStorage<'a, Alignment>,
|
||||
entered_auras: ReadStorage<'a, EnteredAuras>,
|
||||
buffs: ReadStorage<'a, comp::Buffs>,
|
||||
stats: ReadStorage<'a, comp::Stats>,
|
||||
healths: ReadStorage<'a, Health>,
|
||||
bodies: ReadStorage<'a, Body>,
|
||||
orientations: ReadStorage<'a, comp::Ori>,
|
||||
character_states: ReadStorage<'a, CharacterState>,
|
||||
uids: ReadStorage<'a, Uid>,
|
||||
masses: ReadStorage<'a, comp::Mass>,
|
||||
}
|
||||
|
||||
fn handle(
|
||||
events: impl ExactSizeIterator<Item = Self>,
|
||||
(
|
||||
entities,
|
||||
mut block_change,
|
||||
settings,
|
||||
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();
|
||||
impl ServerEvent for ExplosionEvent {
|
||||
type SystemData<'a> = ExplosionData<'a>;
|
||||
|
||||
fn handle(events: impl ExactSizeIterator<Item = Self>, mut data: Self::SystemData<'_>) {
|
||||
let mut emitters = data.event_busses.get_emitters();
|
||||
let mut outcome_emitter = data.outcomes.emitter();
|
||||
|
||||
// TODO: Faster RNG?
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
@ -1105,14 +1086,15 @@ impl ServerEvent for ExplosionEvent {
|
||||
const RAYS: usize = 500;
|
||||
|
||||
// Prevent block colour changes within the radius of a safe zone aura
|
||||
if spatial_grid
|
||||
if data
|
||||
.spatial_grid
|
||||
.0
|
||||
.in_circle_aabr(ev.pos.xy(), SAFE_ZONE_RADIUS)
|
||||
.filter_map(|entity| {
|
||||
auras
|
||||
data.auras
|
||||
.get(entity)
|
||||
.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)| {
|
||||
entity_auras
|
||||
@ -1146,7 +1128,8 @@ impl ServerEvent for ExplosionEvent {
|
||||
)
|
||||
.normalized();
|
||||
|
||||
let _ = terrain
|
||||
let _ = data
|
||||
.terrain
|
||||
.ray(ev.pos, ev.pos + dir * color_range)
|
||||
.until(|_| rng.gen::<f32>() < 0.05)
|
||||
.for_each(|_: &Block, pos| touched_blocks.push(pos))
|
||||
@ -1154,14 +1137,14 @@ impl ServerEvent for ExplosionEvent {
|
||||
}
|
||||
|
||||
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)
|
||||
&& (
|
||||
// Check that owner is not player or explosion_burn_marks by
|
||||
// players
|
||||
// is enabled
|
||||
owner_entity.map_or(true, |e| players.get(e).is_none())
|
||||
|| settings.gameplay.explosion_burn_marks
|
||||
owner_entity.map_or(true, |e| data.players.get(e).is_none())
|
||||
|| data.settings.gameplay.explosion_burn_marks
|
||||
)
|
||||
{
|
||||
let diff2 =
|
||||
@ -1184,7 +1167,7 @@ impl ServerEvent for ExplosionEvent {
|
||||
color[0] = (r as u8).max(30);
|
||||
color[1] = (g as u8).max(30);
|
||||
color[2] = (b as u8).max(30);
|
||||
block_change
|
||||
data.block_change
|
||||
.set(block_pos, Block::new(block.kind(), color));
|
||||
}
|
||||
}
|
||||
@ -1212,7 +1195,8 @@ impl ServerEvent for ExplosionEvent {
|
||||
|
||||
let from = ev.pos;
|
||||
let to = ev.pos + dir * power;
|
||||
let _ = terrain
|
||||
let _ = data
|
||||
.terrain
|
||||
.ray(from, to)
|
||||
.while_(|block: &Block| {
|
||||
ray_energy -= block.explode_power().unwrap_or(0.0)
|
||||
@ -1228,7 +1212,7 @@ impl ServerEvent for ExplosionEvent {
|
||||
})
|
||||
.for_each(|block: &Block, pos| {
|
||||
if block.explode_power().is_some() {
|
||||
block_change.set(pos, block.into_vacant());
|
||||
data.block_change.set(pos, block.into_vacant());
|
||||
}
|
||||
})
|
||||
.cast();
|
||||
@ -1241,14 +1225,14 @@ impl ServerEvent for ExplosionEvent {
|
||||
health_b,
|
||||
(body_b_maybe, ori_b_maybe, char_state_b_maybe, uid_b),
|
||||
) in (
|
||||
&entities,
|
||||
&positions,
|
||||
&healths,
|
||||
&data.entities,
|
||||
&data.positions,
|
||||
&data.healths,
|
||||
(
|
||||
bodies.maybe(),
|
||||
orientations.maybe(),
|
||||
character_states.maybe(),
|
||||
&uids,
|
||||
data.bodies.maybe(),
|
||||
data.orientations.maybe(),
|
||||
data.character_states.maybe(),
|
||||
&data.uids,
|
||||
),
|
||||
)
|
||||
.join()
|
||||
@ -1271,7 +1255,8 @@ impl ServerEvent for ExplosionEvent {
|
||||
|
||||
// Cast a ray from the explosion to the entity to check visibility
|
||||
if strength > 0.0
|
||||
&& (terrain
|
||||
&& (data
|
||||
.terrain
|
||||
.ray(ev.pos, pos_b.0)
|
||||
.until(Block::is_opaque)
|
||||
.cast()
|
||||
@ -1282,8 +1267,8 @@ impl ServerEvent for ExplosionEvent {
|
||||
{
|
||||
// See if entities are in the same group
|
||||
let same_group = owner_entity
|
||||
.and_then(|e| groups.get(e))
|
||||
.map(|group_a| Some(group_a) == groups.get(entity_b))
|
||||
.and_then(|e| data.groups.get(e))
|
||||
.map(|group_a| Some(group_a) == data.groups.get(entity_b))
|
||||
.unwrap_or(Some(entity_b) == owner_entity);
|
||||
|
||||
let target_group = if same_group {
|
||||
@ -1303,25 +1288,27 @@ impl ServerEvent for ExplosionEvent {
|
||||
combat::AttackerInfo {
|
||||
entity,
|
||||
uid,
|
||||
group: groups.get(entity),
|
||||
energy: energies.get(entity),
|
||||
combo: combos.get(entity),
|
||||
inventory: inventories.get(entity),
|
||||
stats: stats.get(entity),
|
||||
group: data.groups.get(entity),
|
||||
energy: data.energies.get(entity),
|
||||
combo: data.combos.get(entity),
|
||||
inventory: data.inventories.get(entity),
|
||||
stats: data.stats.get(entity),
|
||||
mass: data.masses.get(entity),
|
||||
}
|
||||
});
|
||||
|
||||
let target_info = combat::TargetInfo {
|
||||
entity: entity_b,
|
||||
uid: *uid_b,
|
||||
inventory: inventories.get(entity_b),
|
||||
stats: stats.get(entity_b),
|
||||
inventory: data.inventories.get(entity_b),
|
||||
stats: data.stats.get(entity_b),
|
||||
health: Some(health_b),
|
||||
pos: pos_b.0,
|
||||
ori: ori_b_maybe,
|
||||
char_state: char_state_b_maybe,
|
||||
energy: energies.get(entity_b),
|
||||
buffs: buffs.get(entity_b),
|
||||
energy: data.energies.get(entity_b),
|
||||
buffs: data.buffs.get(entity_b),
|
||||
mass: data.masses.get(entity_b),
|
||||
};
|
||||
|
||||
let target_dodging = char_state_b_maybe
|
||||
@ -1330,17 +1317,17 @@ impl ServerEvent for ExplosionEvent {
|
||||
let allow_friendly_fire =
|
||||
owner_entity.is_some_and(|owner_entity| {
|
||||
combat::allow_friendly_fire(
|
||||
&entered_auras,
|
||||
&data.entered_auras,
|
||||
owner_entity,
|
||||
entity_b,
|
||||
)
|
||||
});
|
||||
// PvP check
|
||||
let permit_pvp = combat::permit_pvp(
|
||||
&alignments,
|
||||
&players,
|
||||
&entered_auras,
|
||||
&id_maps,
|
||||
&data.alignments,
|
||||
&data.players,
|
||||
&data.entered_auras,
|
||||
&data.id_maps,
|
||||
owner_entity,
|
||||
entity_b,
|
||||
);
|
||||
@ -1359,7 +1346,7 @@ impl ServerEvent for ExplosionEvent {
|
||||
attack_options,
|
||||
strength,
|
||||
combat::AttackSource::Explosion,
|
||||
*time,
|
||||
*data.time,
|
||||
&mut emitters,
|
||||
|o| outcome_emitter.emit(o),
|
||||
&mut rng,
|
||||
@ -1370,7 +1357,7 @@ impl ServerEvent for ExplosionEvent {
|
||||
},
|
||||
RadiusEffect::Entity(mut effect) => {
|
||||
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 {
|
||||
cylinder_sphere_strength(
|
||||
@ -1397,38 +1384,41 @@ impl ServerEvent for ExplosionEvent {
|
||||
// This can be changed later.
|
||||
let permit_pvp = || {
|
||||
combat::permit_pvp(
|
||||
&alignments,
|
||||
&players,
|
||||
&entered_auras,
|
||||
&id_maps,
|
||||
&data.alignments,
|
||||
&data.players,
|
||||
&data.entered_auras,
|
||||
&data.id_maps,
|
||||
owner_entity,
|
||||
entity_b,
|
||||
) || owner_entity.map_or(true, |entity_a| entity_a == entity_b)
|
||||
};
|
||||
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 {
|
||||
effect.modify_strength(strength);
|
||||
if !effect.is_harm() || permit_pvp() {
|
||||
emit_effect_events(
|
||||
&mut emitters,
|
||||
*time,
|
||||
*data.time,
|
||||
entity_b,
|
||||
effect.clone(),
|
||||
ev.owner.map(|owner| {
|
||||
(
|
||||
owner,
|
||||
id_maps
|
||||
data.id_maps
|
||||
.uid_entity(owner)
|
||||
.and_then(|e| groups.get(e))
|
||||
.and_then(|e| data.groups.get(e))
|
||||
.copied(),
|
||||
)
|
||||
}),
|
||||
inventories.get(entity_b),
|
||||
&msm,
|
||||
character_states.get(entity_b),
|
||||
stats.get(entity_b),
|
||||
data.inventories.get(entity_b),
|
||||
&data.msm,
|
||||
data.character_states.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,
|
||||
char_state: Option<&CharacterState>,
|
||||
stats: Option<&Stats>,
|
||||
tgt_mass: Option<&comp::Mass>,
|
||||
source_mass: Option<&comp::Mass>,
|
||||
) {
|
||||
let damage_contributor = source.map(|(uid, group)| DamageContributor::new(uid, group));
|
||||
match effect {
|
||||
@ -1483,17 +1475,24 @@ pub fn emit_effect_events(
|
||||
);
|
||||
emitters.emit(HealthChangeEvent { entity, change })
|
||||
},
|
||||
common::effect::Effect::Buff(buff) => emitters.emit(BuffEvent {
|
||||
entity,
|
||||
buff_change: comp::BuffChange::Add(comp::Buff::new(
|
||||
buff.kind,
|
||||
buff.data,
|
||||
buff.cat_ids,
|
||||
comp::BuffSource::Item,
|
||||
time,
|
||||
common::effect::Effect::Buff(buff) => {
|
||||
let dest_info = buff::DestInfo {
|
||||
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>,
|
||||
ReadStorage<'a, Uid>,
|
||||
ReadStorage<'a, Stats>,
|
||||
ReadStorage<'a, comp::Mass>,
|
||||
);
|
||||
|
||||
fn handle(
|
||||
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 buff_emitter = buff_events.emitter();
|
||||
@ -1784,13 +1784,18 @@ impl ServerEvent for ParryHookEvent {
|
||||
} else {
|
||||
BuffSource::World
|
||||
};
|
||||
let dest_info = buff::DestInfo {
|
||||
stats: stats.get(attacker),
|
||||
mass: masses.get(attacker),
|
||||
};
|
||||
let buff = buff::Buff::new(
|
||||
BuffKind::Parried,
|
||||
data,
|
||||
vec![buff::BuffCategory::Physical],
|
||||
source,
|
||||
*time,
|
||||
stats.get(attacker),
|
||||
dest_info,
|
||||
masses.get(ev.defender),
|
||||
);
|
||||
buff_emitter.emit(BuffEvent {
|
||||
entity: attacker,
|
||||
|
@ -107,6 +107,7 @@ pub struct InventoryManipData<'a> {
|
||||
agents: ReadStorage<'a, comp::Agent>,
|
||||
pets: ReadStorage<'a, comp::Pet>,
|
||||
velocities: ReadStorage<'a, comp::Vel>,
|
||||
masses: ReadStorage<'a, comp::Mass>,
|
||||
}
|
||||
|
||||
impl ServerEvent for InventoryManipEvent {
|
||||
@ -663,6 +664,8 @@ impl ServerEvent for InventoryManipEvent {
|
||||
&data.msm,
|
||||
data.character_states.get(entity),
|
||||
data.stats.get(entity),
|
||||
data.masses.get(entity),
|
||||
None,
|
||||
);
|
||||
}
|
||||
},
|
||||
@ -678,6 +681,8 @@ impl ServerEvent for InventoryManipEvent {
|
||||
&data.msm,
|
||||
data.character_states.get(entity),
|
||||
data.stats.get(entity),
|
||||
data.masses.get(entity),
|
||||
None,
|
||||
);
|
||||
}
|
||||
},
|
||||
@ -692,6 +697,8 @@ impl ServerEvent for InventoryManipEvent {
|
||||
&data.msm,
|
||||
data.character_states.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::{
|
||||
character::CharacterId,
|
||||
combat,
|
||||
combat::DamageContributor,
|
||||
comp::{
|
||||
self,
|
||||
item::{ItemKind, MaterialStatManifest},
|
||||
misc::PortalData,
|
||||
object, ChatType, Content, Group, Inventory, LootOwner, Object, Player, Poise, Presence,
|
||||
PresenceKind, BASE_ABILITY_LIMIT,
|
||||
self, item::ItemKind, misc::PortalData, object, ChatType, Content, Group, Inventory,
|
||||
LootOwner, Object, Player, Poise, Presence, PresenceKind, BASE_ABILITY_LIMIT,
|
||||
},
|
||||
effect::Effect,
|
||||
link::{Is, Link, LinkHandle},
|
||||
mounting::{Mounting, Rider, VolumeMounting, VolumeRider},
|
||||
resources::{Secs, Time},
|
||||
@ -50,8 +44,6 @@ use tracing::{error, trace, warn};
|
||||
use vek::*;
|
||||
|
||||
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
|
||||
fn create_npc(
|
||||
&mut self,
|
||||
@ -177,109 +169,6 @@ pub trait StateExt {
|
||||
}
|
||||
|
||||
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(
|
||||
&mut self,
|
||||
pos: comp::Pos,
|
||||
|
@ -462,6 +462,27 @@ impl Animation for FinisherMeleeAnimation {
|
||||
next.control.orientation.rotate_z(move2 * 1.6);
|
||||
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::Polymorphed
|
||||
| BuffKind::Heatstroke
|
||||
| BuffKind::Rooted => {
|
||||
| BuffKind::Rooted
|
||||
| BuffKind::Winded => {
|
||||
tracing::error!("Player was killed by a debuff that doesn't do damage!");
|
||||
"mysterious"
|
||||
},
|
||||
|
@ -90,7 +90,7 @@ fn maps_basic_melee() {
|
||||
multi_target: None,
|
||||
simultaneous_hits: 1,
|
||||
custom_combo: None,
|
||||
precision_flank_multiplier: 1.0,
|
||||
precision_flank_multipliers: Default::default(),
|
||||
precision_flank_invert: false,
|
||||
},
|
||||
ori_modifier: 1.0,
|
||||
|
@ -326,6 +326,7 @@ image_ids! {
|
||||
hammer_spine_cracker: "voxygen.element.skills.hammer.spine_cracker",
|
||||
hammer_breach: "voxygen.element.skills.hammer.breach",
|
||||
hammer_pile_driver: "voxygen.element.skills.hammer.pile_driver",
|
||||
hammer_lung_pummel: "voxygen.element.skills.hammer.lung_pummel",
|
||||
// Skilltree Icons
|
||||
health_plus_skill: "voxygen.element.skills.skilltree.health_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_heatstroke_0: "voxygen.element.de_buffs.debuff_heatstroke_0",
|
||||
debuff_rooted: "voxygen.element.de_buffs.debuff_rooted",
|
||||
debuff_winded: "voxygen.element.de_buffs.debuff_winded",
|
||||
|
||||
// Animation Frames
|
||||
// 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::Heatstroke => imgs.debuff_heatstroke_0,
|
||||
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::Heatstroke => "buff-heatstroke",
|
||||
BuffKind::Rooted => "buff-rooted",
|
||||
|
||||
BuffKind::Winded => "buff-winded",
|
||||
// Neutral
|
||||
BuffKind::Polymorphed => "buff-polymorphed",
|
||||
}
|
||||
@ -322,7 +324,8 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec<String> {
|
||||
| BuffKind::Berserk
|
||||
| BuffKind::Heatstroke
|
||||
| BuffKind::ScornfulTaunt
|
||||
| BuffKind::Rooted => Cow::Borrowed(""),
|
||||
| BuffKind::Rooted
|
||||
| BuffKind::Winded => Cow::Borrowed(""),
|
||||
};
|
||||
|
||||
write!(&mut description, "{}", buff_desc).unwrap();
|
||||
@ -374,7 +377,8 @@ pub fn consumable_desc(effects: &Effects, i18n: &Localization) -> Vec<String> {
|
||||
| BuffKind::Berserk
|
||||
| BuffKind::Heatstroke
|
||||
| BuffKind::ScornfulTaunt
|
||||
| BuffKind::Rooted => Cow::Borrowed(""),
|
||||
| BuffKind::Rooted
|
||||
| BuffKind::Winded => Cow::Borrowed(""),
|
||||
}
|
||||
} else if let BuffKind::Saturation
|
||||
| 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.breach" => imgs.hammer_breach,
|
||||
"common.abilities.hammer.pile_driver" => imgs.hammer_pile_driver,
|
||||
"common.abilities.hammer.lung_pummel" => imgs.hammer_lung_pummel,
|
||||
// Bow
|
||||
"common.abilities.bow.charged" => imgs.bow_m1,
|
||||
"common.abilities.bow.repeater" => imgs.bow_m2,
|
||||
|
Loading…
Reference in New Issue
Block a user