Added precision

This commit is contained in:
Sam 2023-10-22 17:00:09 -04:00
parent 62464dbe11
commit d3b3bca621
37 changed files with 193 additions and 58 deletions

View File

@ -86,7 +86,6 @@ common-rand_name = Выпадковае імя
common-stats-combat_rating = БР
common-stats-power = Моц
common-stats-speed = Хуткасць
common-stats-crit_chance = Крыт. шанец
common-stats-crit_mult = Крыт. множнік
common-stats-armor = Браня
common-stats-poise_res = Супраціўленне аглушэнню

View File

@ -86,7 +86,6 @@ common-rand_name = Nom Aleatori
common-stats-combat_rating = PC
common-stats-power = Potència
common-stats-speed = Velocitat
common-stats-crit_chance = Probabilitat de Crític
common-stats-crit_mult = Multiplicador de Crític
common-stats-armor = Armadura
common-stats-poise_res = Resistència a l'Atordiment

View File

@ -83,7 +83,6 @@ common-rand_name = Náhodné jméno
common-stats-combat_rating = CR
common-stats-power = Síla
common-stats-speed = Rychlost
common-stats-crit_chance = Kritická šance
common-stats-crit_mult = Krit Násobek
common-stats-armor = Zbroj
common-stats-poise_res = Odolnost omráčení

View File

@ -93,7 +93,6 @@ common-stats-speed = Schnelligkeit
common-stats-range = Reichweite
common-stats-energy_efficiency = Ausdauereffizienz
common-stats-buff_strength = Buff/Debuff Stärke
common-stats-crit_chance = Krit. Trefferchance
common-stats-crit_mult = Krit. Multiplikator
common-stats-armor = Rüstung
common-stats-poise_res = Betäubungsresistenz

View File

@ -97,7 +97,6 @@ common-stats-effect-power = Effect Power
common-stats-range = Range
common-stats-energy_efficiency = Energy Efficiency
common-stats-buff_strength = Buff/Debuff Strength
common-stats-crit_chance = Crit Chance
common-stats-crit_mult = Crit Mult
common-stats-armor = Armor
common-stats-poise_res = Stun Res

View File

@ -93,7 +93,6 @@ common-stats-speed = Velocidad
common-stats-range = Rango
common-stats-energy_efficiency = Eficiencia de energía
common-stats-buff_strength = Fuerza de Mejora/Debilitación
common-stats-crit_chance = Probabilidad de Crítico
common-stats-crit_mult = Multiplicador de Crítico
common-stats-armor = Armadura
common-stats-poise_res = Resistencia al aturdimiento

View File

@ -94,7 +94,6 @@ common-stats-effect-power = Potencia de estados alterados
common-stats-range = Rango
common-stats-energy_efficiency = Eficiencia de aguante
common-stats-buff_strength = Potencia de estados beneficiosos
common-stats-crit_chance = Probabilidad de daño crítico
common-stats-crit_mult = Multiplicador de crítico
common-stats-armor = Armadura
common-stats-poise_res = Resistencia al aturdimiento

View File

@ -93,7 +93,6 @@ common-stats-speed = Abiadura
common-stats-range = Maila
common-stats-energy_efficiency = Energia eraginkortasuna
common-stats-buff_strength = Buff/Debuff indarra
common-stats-crit_chance = Kritiko probabilitatea
common-stats-crit_mult = Kritiko biderkatzailea
common-stats-armor = Armadura
common-stats-poise_res = Orekarako gaitasuna

View File

@ -95,7 +95,6 @@ common-stats-speed = Vitesse
common-stats-range = Portée
common-stats-energy_efficiency = Efficacité du coût d'Endurance
common-stats-buff_strength = Montant de l'augmentation
common-stats-crit_chance = Chance de Crit
common-stats-crit_mult = Multiplicateur de Crit
common-stats-armor = Armure
common-stats-poise_res = Résistance à l'étourdissement

View File

@ -81,7 +81,6 @@ common-rand_name = Véletlenszerű név
common-stats-combat_rating = KÉ
common-stats-power = Erő
common-stats-speed = Gyorsaság
common-stats-crit_chance = Kritikus találat esélye
common-stats-crit_mult = Kritikus találat szorzója
common-stats-armor = Páncélzat
common-stats-poise_res = Megszédíthetőség

View File

@ -97,7 +97,6 @@ common-stats-effect-power = Potenza effetto
common-stats-range = Intervallo
common-stats-energy_efficiency = Efficienza energia
common-stats-buff_strength = Quantità di aumento/diminuzione
common-stats-crit_chance = Probabilità di critico
common-stats-crit_mult = Moltiplicatore del critico
common-stats-armor = Armatura
common-stats-poise_res = Resistenza allo stordimento

View File

@ -77,7 +77,6 @@ common-rand_appearance = ランダムに見た目を選択
common-rand_name = ランダムに名前を選ぶ
common-stats-power = Power
common-stats-speed = Speed
common-stats-crit_chance = Crit Chance
common-stats-crit_mult = Crit Mult
common-stats-armor = Armor
common-stats-poise_res = Poise res

View File

@ -93,7 +93,6 @@ common-stats-speed = 속도
common-stats-range = 사거리
common-stats-energy_efficiency = 기력 효율
common-stats-buff_strength = 힘 버프/디버프
common-stats-crit_chance = 치명타 확률
common-stats-crit_mult = 치명타 배수
common-stats-armor = 방어력
common-stats-poise_res = 기절 저항

View File

@ -93,7 +93,6 @@ common-stats-speed = Prędkość
common-stats-range = Zasięg
common-stats-energy_efficiency = Efektywność Energii
common-stats-buff_strength = Siła Efektów Wzmacniających
common-stats-crit_chance = % na cios kryt.
common-stats-crit_mult = Mnożnik ciosu kryt.
common-stats-armor = Obrona
common-stats-poise_res = Odp. na ogłuszenie

View File

@ -96,7 +96,6 @@ common-stats-effect-power = Poder de Efeito
common-stats-range = Alcance
common-stats-energy_efficiency = Eficiência Energética
common-stats-buff_strength = Buff/Debuff de força
common-stats-crit_chance = Chance de Crítico
common-stats-crit_mult = Multiplicador de Crítico
common-stats-armor = Armadura
common-stats-poise_res = Resistência a Atordoamento

View File

@ -90,7 +90,6 @@ common-stats-speed = Viteză
common-stats-range = Distanță
common-stats-energy_efficiency = Eficiența energiei
common-stats-buff_strength = Buff/Debuff Strength
common-stats-crit_chance = Șansă Crit
common-stats-crit_mult = Multiplicator Crit
common-stats-armor = Armură
common-stats-poise_res = Stun Res

View File

@ -93,7 +93,6 @@ common-stats-speed = Скорость
common-stats-range = Дистанция
common-stats-energy_efficiency = Энергоэффективность
common-stats-buff_strength = Увеличение силы
common-stats-crit_chance = Шанс крита
common-stats-crit_mult = Множитель крита
common-stats-armor = Броня
common-stats-poise_res = Оглушение

View File

@ -83,7 +83,6 @@ common-rand_name = Насумично име
common-stats-combat_rating = CR
common-stats-power = Снага
common-stats-speed = Брзина
common-stats-crit_chance = Крит Шанса
common-stats-crit_mult = Мулти Крит
common-stats-armor = Оклоп
common-stats-poise_res = Отпорност на Омаму

View File

@ -97,7 +97,6 @@ common-stats-effect-power = Effektstyrka
common-stats-range = Räckvidd
common-stats-energy_efficiency = Energieffektivitet
common-stats-buff_strength = Buff/Debuff-styrka
common-stats-crit_chance = Kritisk chans
common-stats-crit_mult = Kritisk multi
common-stats-armor = Rustning
common-stats-poise_res = Motståndskraft

View File

@ -93,7 +93,6 @@ common-stats-speed = ความเร็ว
common-stats-range = ระยะโจมตี
common-stats-energy_efficiency = ประสิทธิภาพพลังงาน
common-stats-buff_strength = เสริมพลังความแข็งแกร่ง
common-stats-crit_chance = โอกาสคริติคอล
common-stats-crit_mult = ความรุนแรงคริติคอล
common-stats-armor = เกราะ
common-stats-poise_res = ความคงทน

View File

@ -83,7 +83,6 @@ common-rand_appearance = Rastgele görünüm
common-stats-combat_rating = DP
common-stats-power = Güç
common-stats-speed = Hız
common-stats-crit_chance = Kritik Şansı
common-stats-crit_mult = Kritik Çarpanı
common-stats-armor = Zırh
common-stats-energy_max = Maksimum Enerji

View File

@ -97,7 +97,6 @@ common-stats-effect-power = Сила ефекту
common-stats-range = Дистанція
common-stats-energy_efficiency = Енергоощадливість
common-stats-buff_strength = Сила бафу/дебафу
common-stats-crit_chance = Крит. шанс
common-stats-crit_mult = Крит. множник
common-stats-armor = Броня
common-stats-poise_res = Супротив приголомшенню

View File

@ -81,7 +81,6 @@ common-rand_name = Tên ngẫu nhiên
common-stats-combat_rating = CR
common-stats-power = Sức Mạnh
common-stats-speed = Tốc Độ
common-stats-crit_chance = Tỉ Lệ Chí Mạng
common-stats-armor = Giáp
common-stats-energy_max = Năng Lượng Tối Đa
common-stats-energy_reward = Thưởng Năng Lượng

View File

@ -95,7 +95,6 @@ common-stats-effect-power = 效果威力
common-stats-range = 范围
common-stats-energy_efficiency = 耐力消耗
common-stats-buff_strength = 增幅
common-stats-crit_chance = 暴击率
common-stats-crit_mult = 暴击倍率
common-stats-armor = 护甲
common-stats-poise_res = 韧性

View File

@ -44,6 +44,12 @@ pub enum AttackSource {
Explosion,
}
pub const FULL_FLANK_ANGLE: f32 = std::f32::consts::PI / 4.0;
pub const PARTIAL_FLANK_ANGLE: f32 = std::f32::consts::PI * 3.0 / 4.0;
// NOTE: Do we want to change this to be a configurable parameter on body?
pub const PROJECTILE_HEADSHOT_PROPORTION: f32 = 0.1;
pub const BEAM_DURATION_PRECISION: f32 = 2.5;
#[derive(Copy, Clone)]
pub struct AttackerInfo<'a> {
pub entity: EcsEntity,
@ -73,6 +79,7 @@ pub struct AttackOptions {
pub target_dodging: bool,
pub may_harm: bool,
pub target_group: GroupTarget,
pub precision_mult: Option<f32>,
}
#[derive(Clone, Debug, Serialize, Deserialize)] // TODO: Yeet clone derive
@ -202,6 +209,7 @@ impl Attack {
target_dodging,
may_harm,
target_group,
precision_mult,
} = options;
// target == OutOfGroup is basic heuristic that this
@ -217,7 +225,10 @@ impl Attack {
matches!(attack_effect.target, Some(GroupTarget::OutOfGroup))
&& (target_dodging || !may_harm)
};
let is_crit = false;
let precision_mult = attacker
.and_then(|a| a.stats)
.and_then(|s| s.precision_multiplier_override)
.or(precision_mult);
let mut is_applied = false;
let mut accumulated_damage = 0.0;
let damage_modifier = attacker
@ -244,7 +255,7 @@ impl Attack {
let change = damage.damage.calculate_health_change(
damage_reduction,
attacker.map(|x| x.into()),
is_crit,
precision_mult,
self.crit_multiplier,
strength_modifier * damage_modifier,
time,
@ -273,7 +284,7 @@ impl Attack {
by: attacker.map(|x| x.into()),
cause: Some(damage.damage.source),
time,
crit: is_crit,
crit: precision_mult.is_some(),
instance: damage_instance,
};
emit(ServerEvent::HealthChange {
@ -324,7 +335,7 @@ impl Attack {
by: attacker.map(|x| x.into()),
cause: Some(damage.damage.source),
instance: damage_instance,
crit: is_crit,
crit: precision_mult.is_some(),
time,
};
emit(ServerEvent::HealthChange {
@ -1014,18 +1025,14 @@ impl Damage {
self,
damage_reduction: f32,
damage_contributor: Option<DamageContributor>,
is_crit: bool,
precision_mult: Option<f32>,
crit_mult: f32,
damage_modifier: f32,
time: Time,
instance: u64,
) -> HealthChange {
let mut damage = self.value * damage_modifier;
let critdamage = if is_crit {
damage * (crit_mult - 1.0)
} else {
0.0
};
let critdamage = damage * precision_mult.unwrap_or(0.0) * (crit_mult - 1.0);
match self.source {
DamageSource::Melee
| DamageSource::Projectile
@ -1042,7 +1049,7 @@ impl Damage {
by: damage_contributor,
cause: Some(self.source),
time,
crit: is_crit,
crit: precision_mult.is_some(),
instance,
}
},

View File

@ -1,4 +1,5 @@
use crate::{combat::Attack, resources::Secs};
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
use specs::{Component, DerefFlaggedStorage, Entity as EcsEntity};
use vek::*;
@ -14,6 +15,16 @@ pub struct Beam {
pub bezier: QuadraticBezier3<f32>,
#[serde(skip)]
pub hit_entities: Vec<EcsEntity>,
#[serde(skip)]
pub hit_durations: HashMap<EcsEntity, u32>,
}
impl Beam {
pub fn hit_entities_and_durations(
&mut self,
) -> (&Vec<EcsEntity>, &mut HashMap<EcsEntity, u32>) {
(&self.hit_entities, &mut self.hit_durations)
}
}
impl Component for Beam {

View File

@ -333,10 +333,7 @@ impl BuffKind {
BuffKind::Hastened => vec![
BuffEffect::MovementSpeed(1.0 + data.strength),
BuffEffect::AttackSpeed(1.0 + data.strength),
BuffEffect::CriticalChance {
kind: ModifierKind::Multiplicative,
val: 0.0,
},
BuffEffect::PrecisionOverride(0.0),
],
BuffKind::Fortitude => vec![
BuffEffect::PoiseReduction(nn_scaling(data.strength)),
@ -388,10 +385,7 @@ impl BuffKind {
AttackEffect::new(None, CombatEffect::Lifesteal(data.strength))
.with_requirement(CombatRequirement::TargetHasBuff(BuffKind::Bleeding)),
)],
BuffKind::ImminentCritical => vec![BuffEffect::CriticalChance {
kind: ModifierKind::Additive,
val: 1.0,
}],
BuffKind::ImminentCritical => vec![BuffEffect::PrecisionOverride(1.0)],
BuffKind::Fury => vec![BuffEffect::AttackEffect(
AttackEffect::new(None, CombatEffect::Combo(data.strength.round() as i32))
.with_requirement(CombatRequirement::AnyDamage),
@ -558,11 +552,8 @@ pub enum BuffEffect {
},
/// Modifier to the amount of damage dealt with attacks
AttackDamage(f32),
/// Multiplies crit chance of attacks
CriticalChance {
kind: ModifierKind,
val: f32,
},
/// Overrides the precision multiplier applied to an attack
PrecisionOverride(f32),
/// Changes body.
BodyChange(Body),
BuffImmunity(BuffKind),

View File

@ -134,7 +134,7 @@ impl PoiseState {
}
/// Returns the multiplier on poise damage to health damage for when the
/// target is in a poise state
/// target is in a poise state, also is used for precision
pub fn damage_multiplier(&self) -> f32 {
match self {
Self::Interrupted => 0.1,

View File

@ -63,7 +63,7 @@ pub struct Stats {
pub max_energy_modifiers: StatsModifier,
pub poise_damage_modifier: f32,
pub attack_damage_modifier: f32,
pub crit_chance_modifier: StatsModifier,
pub precision_multiplier_override: Option<f32>,
pub swim_speed_modifier: f32,
/// This adds effects to any attacks that the entity makes
pub effects_on_attack: Vec<AttackEffect>,
@ -90,7 +90,7 @@ impl Stats {
max_energy_modifiers: StatsModifier::default(),
poise_damage_modifier: 1.0,
attack_damage_modifier: 1.0,
crit_chance_modifier: StatsModifier::default(),
precision_multiplier_override: None,
swim_speed_modifier: 1.0,
effects_on_attack: Vec::new(),
mitigations_penetration: 0.0,

View File

@ -17,6 +17,7 @@ use crate::{
terrain::Block,
util::Dir,
};
use hashbrown::HashMap;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use vek::*;
@ -132,6 +133,7 @@ impl CharacterBehavior for Data {
duration: self.static_data.beam_duration,
tick_dur: Secs(1.0 / self.static_data.tick_rate as f64),
hit_entities: Vec::new(),
hit_durations: HashMap::new(),
specifier: self.static_data.specifier,
bezier: QuadraticBezier3 {
start: data.pos.0,

View File

@ -73,6 +73,11 @@ impl<'a> System<'a> for Sys {
.for_each(|(pos, ori, char_state, mut beam)| {
// Clear hit entities list if list should be cleared
if read_data.time.0 % beam.tick_dur.0 < read_data.dt.0 as f64 {
let (hit_entities, hit_durations) = beam.hit_entities_and_durations();
hit_durations.retain(|e, _| hit_entities.contains(e));
for entity in hit_entities {
*hit_durations.entry(*entity).or_insert(0) += 1;
}
beam.hit_entities.clear();
}
// Update start, end, and control positions of beam bezier
@ -227,10 +232,45 @@ impl<'a> System<'a> for Sys {
Some(entity),
target,
);
let precision_from_flank = {
let beam_dir = beam.bezier.ctrl - beam.bezier.start;
let angle = target_info.ori.map_or(std::f32::consts::PI, |t_ori| {
t_ori.look_dir().angle_between(beam_dir)
});
if angle < combat::FULL_FLANK_ANGLE {
Some(1.0)
} else if angle < combat::PARTIAL_FLANK_ANGLE {
Some(0.5)
} else {
None
}
};
let precision_from_time = {
if let Some(ticks) = beam.hit_durations.get(&target) {
let dur = *ticks as f32 * beam.tick_dur.0 as f32;
let mult =
(dur / combat::BEAM_DURATION_PRECISION).clamp(0.0, 1.0);
Some(mult)
} else {
None
}
};
// Is there a more idiomatic way to do this (taking the max of 2
// options)?
let precision_mult = precision_from_flank
.map(|flank| {
precision_from_time.map_or(flank, |head: f32| head.max(flank))
})
.or(precision_from_time);
let attack_options = AttackOptions {
target_dodging,
may_harm,
target_group,
precision_mult,
};
beam.attack.apply_attack(

View File

@ -674,9 +674,12 @@ fn execute_effect(
BuffEffect::AttackDamage(dam) => {
stat.attack_damage_modifier *= *dam;
},
BuffEffect::CriticalChance { kind, val } => match kind {
ModifierKind::Additive => stat.crit_chance_modifier.add_mod += val,
ModifierKind::Multiplicative => stat.crit_chance_modifier.mult_mod *= val,
BuffEffect::PrecisionOverride(val) => {
// Use lower of precision multiplier overrides
stat.precision_multiplier_override = stat
.precision_multiplier_override
.map(|mult| mult.min(*val))
.or(Some(*val));
},
BuffEffect::BodyChange(b) => {
// For when an entity is under the effects of multiple de/buffs that change the

View File

@ -196,6 +196,8 @@ impl<'a> System<'a> for Sys {
stats: read_data.stats.get(attacker),
});
let target_ori = read_data.orientations.get(target);
let target_char_state = read_data.char_states.get(target);
let target_info = TargetInfo {
entity: target,
uid: *uid_b,
@ -203,8 +205,8 @@ impl<'a> System<'a> for Sys {
stats: read_data.stats.get(target),
health: read_data.healths.get(target),
pos: pos_b.0,
ori: read_data.orientations.get(target),
char_state: read_data.char_states.get(target),
ori: target_ori,
char_state: target_char_state,
energy: read_data.energies.get(target),
buffs: read_data.buffs.get(target),
};
@ -218,10 +220,39 @@ impl<'a> System<'a> for Sys {
target,
);
let precision_from_flank = {
let angle = target_ori.map_or(std::f32::consts::PI, |t_ori| {
t_ori.look_dir().angle_between(*ori.look_dir())
});
if angle < combat::FULL_FLANK_ANGLE {
Some(1.0)
} else if angle < combat::PARTIAL_FLANK_ANGLE {
Some(0.5)
} else {
None
}
};
let precision_from_poise = {
if let Some(CharacterState::Stunned(data)) = target_char_state {
Some(data.static_data.poise_state.damage_multiplier())
} else {
None
}
};
// Is there a more idiomatic way to do this (taking the max of 2 options)?
let precision_mult = precision_from_flank
.map(|flank| {
precision_from_poise.map_or(flank, |head: f32| head.max(flank))
})
.or(precision_from_poise);
let attack_options = AttackOptions {
target_dodging,
may_harm,
target_group,
precision_mult,
};
let strength =

View File

@ -157,6 +157,7 @@ impl<'a> System<'a> for Sys {
owner,
ori: orientations.get(entity),
pos,
vel,
};
let target = entity_of(other);
@ -247,6 +248,7 @@ struct ProjectileInfo<'a> {
owner: Option<EcsEntity>,
ori: Option<&'a Ori>,
pos: &'a Pos,
vel: &'a Vel,
}
struct ProjectileTargetInfo<'a> {
@ -343,10 +345,76 @@ fn dispatch_hit(
.get(target)
.and_then(|cs| cs.attack_immunities())
.map_or(false, |i| i.projectiles);
let precision_from_flank = {
let angle = target_info.ori.map_or(std::f32::consts::PI, |t_ori| {
t_ori.look_dir().angle_between(*projectile_dir)
});
if angle < combat::FULL_FLANK_ANGLE {
Some(1.0)
} else if angle < combat::PARTIAL_FLANK_ANGLE {
Some(0.5)
} else {
None
}
};
let precision_from_head = {
let curr_pos = projectile_info.pos.0;
let last_pos = projectile_info.pos.0 - projectile_info.vel.0 * read_data.dt.0;
let vel = projectile_info.vel.0;
let (target_height, target_radius) = read_data
.bodies
.get(target)
.map_or((0.0, 0.0), |b| (b.height(), b.max_radius()));
let head_top_pos = target_pos.with_z(target_pos.z + target_height);
let head_bottom_pos = head_top_pos.with_z(
head_top_pos.z - target_height * combat::PROJECTILE_HEADSHOT_PROPORTION,
);
let headshot = if (curr_pos.z < head_bottom_pos.z && last_pos.z < head_bottom_pos.z)
|| (curr_pos.z > head_top_pos.z && last_pos.z > head_top_pos.z)
{
false
} else if curr_pos.z > head_top_pos.z
|| curr_pos.z < head_bottom_pos.z
|| last_pos.z > head_top_pos.z
|| last_pos.z < head_bottom_pos.z
{
let proj_top_intersection = {
let t = (head_top_pos.z - last_pos.z) / vel.z;
last_pos + vel * t
};
let proj_bottom_intersection = {
let t = (head_bottom_pos.z - last_pos.z) / vel.z;
last_pos + vel * t
};
head_top_pos.distance_squared(proj_top_intersection) < target_radius.powi(2)
|| head_bottom_pos.distance_squared(proj_bottom_intersection)
< target_radius.powi(2)
} else {
let trajectory = LineSegment3 {
start: last_pos,
end: curr_pos,
};
let head_middle_pos = head_bottom_pos.with_z(
head_bottom_pos.z
+ target_height * combat::PROJECTILE_HEADSHOT_PROPORTION * 0.5,
);
trajectory.distance_to_point(head_middle_pos) < target_radius
};
if headshot { Some(1.0) } else { None }
};
// Is there a more idiomatic way to do this (taking the max of 2 options)?
let precision_mult = precision_from_flank
.map(|flank| precision_from_head.map_or(flank, |head: f32| head.max(flank)))
.or(precision_from_head);
let attack_options = AttackOptions {
target_dodging,
may_harm,
target_group: projectile_target_info.target_group,
precision_mult,
};
attack.apply_attack(

View File

@ -234,10 +234,13 @@ impl<'a> System<'a> for Sys {
shockwave_owner,
target,
);
// Shockwaves aren't precise, and thus cannot be a precise strike
let precision_mult = None;
let attack_options = AttackOptions {
target_dodging,
may_harm,
target_group,
precision_mult,
};
shockwave.properties.attack.apply_attack(

View File

@ -670,7 +670,7 @@ pub fn handle_land_on_ground(
let change = damage.calculate_health_change(
damage_reduction,
None,
false,
None,
0.0,
1.0,
*time,
@ -1049,10 +1049,13 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
// PvP check
let may_harm =
combat::may_harm(alignments, players, id_maps, owner_entity, entity_b);
// Explosions aren't precise, and thus cannot be a precise strike
let precision_mult = None;
let attack_options = combat::AttackOptions {
target_dodging,
may_harm,
target_group,
precision_mult,
};
let time = server.state.ecs().read_resource::<Time>();

View File

@ -196,7 +196,7 @@ impl StateExt for State {
&msm,
),
damage_contributor,
false,
None,
0.0,
1.0,
*time,