mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Critical hits are now functional.
This commit is contained in:
parent
2690e9caa1
commit
c3408c084c
@ -12,10 +12,13 @@ use crate::{
|
||||
Body, BuffKind, Health, HealthChange, HealthSource, Inventory, Stats,
|
||||
},
|
||||
effect,
|
||||
event::ServerEvent,
|
||||
uid::Uid,
|
||||
util::Dir,
|
||||
};
|
||||
use rand::{thread_rng, Rng};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use specs::Entity as EcsEntity;
|
||||
use vek::*;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
@ -28,8 +31,8 @@ pub enum GroupTarget {
|
||||
pub struct Attack {
|
||||
damages: Vec<DamageComponent>,
|
||||
effects: Vec<EffectComponent>,
|
||||
pub crit_chance: f32,
|
||||
pub crit_multiplier: f32,
|
||||
crit_chance: f32,
|
||||
crit_multiplier: f32,
|
||||
}
|
||||
|
||||
impl Default for Attack {
|
||||
@ -60,12 +63,47 @@ impl Attack {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn damages(&self) -> impl Iterator<Item = &DamageComponent> {
|
||||
self.damages.iter()
|
||||
}
|
||||
|
||||
pub fn effects(&self) -> impl Iterator<Item = &EffectComponent> {
|
||||
self.effects.iter()
|
||||
pub fn apply_attack(
|
||||
&self,
|
||||
target_group: GroupTarget,
|
||||
target_entity: EcsEntity,
|
||||
inventory: Option<&Inventory>,
|
||||
uid: Uid,
|
||||
dir: Dir,
|
||||
) -> Vec<ServerEvent> {
|
||||
let is_crit = thread_rng().gen::<f32>() < self.crit_chance;
|
||||
let mut accumulated_damage = 0.0;
|
||||
let mut server_events = Vec::new();
|
||||
for damage in self
|
||||
.damages
|
||||
.iter()
|
||||
.filter(|d| d.target.map_or(true, |t| t == target_group))
|
||||
{
|
||||
let change =
|
||||
damage
|
||||
.damage
|
||||
.modify_damage(inventory, Some(uid), is_crit, self.crit_multiplier);
|
||||
if change.amount != 0 {
|
||||
server_events.push(ServerEvent::Damage {
|
||||
entity: target_entity,
|
||||
change,
|
||||
});
|
||||
for effect in damage.effects.iter() {
|
||||
match effect {
|
||||
AttackEffect::Knockback(kb) => {
|
||||
let impulse = kb.calculate_impulse(dir);
|
||||
if !impulse.is_approx_zero() {
|
||||
server_events.push(ServerEvent::Knockback {
|
||||
entity: target_entity,
|
||||
impulse,
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
server_events
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,18 +127,6 @@ impl DamageComponent {
|
||||
self.effects.push(effect);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn target(&self) -> Option<GroupTarget> {
|
||||
self.target
|
||||
}
|
||||
|
||||
pub fn damage(&self) -> Damage {
|
||||
self.damage
|
||||
}
|
||||
|
||||
pub fn effects(&self) -> impl Iterator<Item = &AttackEffect> {
|
||||
self.effects.iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@ -166,15 +192,21 @@ impl Damage {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn modify_damage(self, inventory: Option<&Inventory>, uid: Option<Uid>) -> HealthChange {
|
||||
pub fn modify_damage(
|
||||
self,
|
||||
inventory: Option<&Inventory>,
|
||||
uid: Option<Uid>,
|
||||
is_crit: bool,
|
||||
crit_mult: f32,
|
||||
) -> HealthChange {
|
||||
let mut damage = self.value;
|
||||
let damage_reduction = inventory.map_or(0.0, |inv| Damage::compute_damage_reduction(inv));
|
||||
match self.source {
|
||||
DamageSource::Melee => {
|
||||
// Critical hit
|
||||
let mut critdamage = 0.0;
|
||||
if rand::random() {
|
||||
critdamage = damage * 0.3;
|
||||
if is_crit {
|
||||
critdamage = damage * (crit_mult - 1.0);
|
||||
}
|
||||
// Armor
|
||||
damage *= 1.0 - damage_reduction;
|
||||
@ -194,8 +226,8 @@ impl Damage {
|
||||
},
|
||||
DamageSource::Projectile => {
|
||||
// Critical hit
|
||||
if rand::random() {
|
||||
damage *= 1.2;
|
||||
if is_crit {
|
||||
damage *= crit_mult;
|
||||
}
|
||||
// Armor
|
||||
damage *= 1.0 - damage_reduction;
|
||||
|
@ -103,7 +103,7 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
||||
.with_effect(knockback);
|
||||
let attack = Attack::default().with_damage(damage);
|
||||
let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3);
|
||||
|
||||
// Hit attempt
|
||||
data.updater.insert(data.entity, MeleeAttack {
|
||||
|
@ -175,7 +175,7 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
||||
.with_effect(knockback);
|
||||
let attack = Attack::default().with_damage(damage);
|
||||
let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3);
|
||||
|
||||
// Hit attempt
|
||||
data.updater.insert(data.entity, MeleeAttack {
|
||||
|
@ -193,8 +193,8 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
||||
.with_effect(knockback);
|
||||
let attack = Attack::default().with_damage(damage);
|
||||
|
||||
let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3);
|
||||
|
||||
data.updater.insert(data.entity, MeleeAttack {
|
||||
attack,
|
||||
range: self.static_data.stage_data[stage_index].range,
|
||||
|
@ -153,7 +153,7 @@ impl CharacterBehavior for Data {
|
||||
let damage =
|
||||
DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
||||
.with_effect(knockback);
|
||||
let attack = Attack::default().with_damage(damage);
|
||||
let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3);
|
||||
|
||||
data.updater.insert(data.entity, MeleeAttack {
|
||||
attack,
|
||||
|
@ -158,7 +158,7 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
||||
.with_effect(knockback);
|
||||
let attack = Attack::default().with_damage(damage);
|
||||
let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3);
|
||||
|
||||
// Hit attempt, when animation plays
|
||||
data.updater.insert(data.entity, MeleeAttack {
|
||||
|
@ -126,7 +126,7 @@ impl CharacterBehavior for Data {
|
||||
});
|
||||
let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
||||
.with_effect(knockback);
|
||||
let attack = Attack::default().with_damage(damage);
|
||||
let attack = Attack::default().with_damage(damage).with_crit(0.5, 1.3);
|
||||
|
||||
// Hit attempt
|
||||
data.updater.insert(data.entity, MeleeAttack {
|
||||
|
@ -166,7 +166,13 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
|
||||
// Modify damage
|
||||
let change = damage.modify_damage(inventories.get(b), beam_segment.owner);
|
||||
let change = damage.modify_damage(
|
||||
inventories.get(b),
|
||||
beam_segment.owner,
|
||||
false,
|
||||
0.0,
|
||||
);
|
||||
|
||||
match target {
|
||||
Some(GroupTarget::OutOfGroup) => {
|
||||
server_emitter.emit(ServerEvent::Damage { entity: b, change });
|
||||
|
@ -16,15 +16,7 @@ pub mod state;
|
||||
mod stats;
|
||||
|
||||
// External
|
||||
use common::{
|
||||
combat::{Attack, AttackEffect, GroupTarget},
|
||||
comp::Inventory,
|
||||
event::ServerEvent,
|
||||
uid::Uid,
|
||||
util::Dir,
|
||||
};
|
||||
use rand::{thread_rng, Rng};
|
||||
use specs::{DispatcherBuilder, Entity as EcsEntity};
|
||||
use specs::DispatcherBuilder;
|
||||
|
||||
// System names
|
||||
pub const CHARACTER_BEHAVIOR_SYS: &str = "character_behavior_sys";
|
||||
@ -56,39 +48,3 @@ pub fn add_local_systems(dispatch_builder: &mut DispatcherBuilder) {
|
||||
dispatch_builder.add(melee::Sys, MELEE_SYS, &[PROJECTILE_SYS]);
|
||||
dispatch_builder.add(aura::Sys, AURAS_SYS, &[]);
|
||||
}
|
||||
|
||||
pub fn apply_attack(
|
||||
attack: &Attack,
|
||||
target_group: GroupTarget,
|
||||
target_entity: EcsEntity,
|
||||
inventory: Option<&Inventory>,
|
||||
uid: Uid,
|
||||
dir: Dir,
|
||||
) -> Vec<ServerEvent> {
|
||||
let is_crit = thread_rng().gen::<f32>() < attack.crit_chance;
|
||||
let mut accumulated_damage = 0.0;
|
||||
let mut server_events = Vec::new();
|
||||
for damage in attack
|
||||
.damages()
|
||||
.filter(|d| d.target().map_or(true, |t| t == target_group))
|
||||
{
|
||||
let change = damage.damage().modify_damage(inventory, Some(uid));
|
||||
if change.amount != 0 {
|
||||
server_events.push(ServerEvent::Damage { entity: target_entity, change });
|
||||
for effect in damage.effects() {
|
||||
match effect {
|
||||
AttackEffect::Knockback(kb) => {
|
||||
let impulse = kb.calculate_impulse(dir);
|
||||
if !impulse.is_approx_zero() {
|
||||
server_events.push(ServerEvent::Knockback {
|
||||
entity: target_entity,
|
||||
impulse,
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
server_events
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
use crate::apply_attack;
|
||||
use common::{
|
||||
comp::{buff, group, Body, CharacterState, Health, Inventory, MeleeAttack, Ori, Pos, Scale},
|
||||
event::{EventBus, LocalEvent, ServerEvent},
|
||||
@ -76,7 +75,15 @@ impl<'a> System<'a> for Sys {
|
||||
attack.applied = true;
|
||||
|
||||
// Go through all other entities
|
||||
for (b, pos_b, scale_b_maybe, health_b, body_b, char_state_b_maybe, inventory_b_maybe) in (
|
||||
for (
|
||||
b,
|
||||
pos_b,
|
||||
scale_b_maybe,
|
||||
health_b,
|
||||
body_b,
|
||||
char_state_b_maybe,
|
||||
inventory_b_maybe,
|
||||
) in (
|
||||
&entities,
|
||||
&positions,
|
||||
scales.maybe(),
|
||||
@ -122,14 +129,10 @@ impl<'a> System<'a> for Sys {
|
||||
|
||||
let dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0));
|
||||
|
||||
let server_events = apply_attack(
|
||||
&attack.attack,
|
||||
target_group,
|
||||
b,
|
||||
inventory_b_maybe,
|
||||
*uid,
|
||||
dir,
|
||||
);
|
||||
let server_events =
|
||||
attack
|
||||
.attack
|
||||
.apply_attack(target_group, b, inventory_b_maybe, *uid, dir);
|
||||
|
||||
for event in server_events {
|
||||
server_emitter.emit(event);
|
||||
|
@ -118,8 +118,12 @@ impl<'a> System<'a> for Sys {
|
||||
uid_allocator.retrieve_entity_internal(other.into())
|
||||
{
|
||||
let other_entity_inventory = inventories.get(other_entity);
|
||||
let change =
|
||||
damage.modify_damage(other_entity_inventory, projectile.owner);
|
||||
let change = damage.modify_damage(
|
||||
other_entity_inventory,
|
||||
projectile.owner,
|
||||
false,
|
||||
0.0,
|
||||
);
|
||||
server_emitter.emit(ServerEvent::Damage {
|
||||
entity: other_entity,
|
||||
change,
|
||||
|
@ -199,8 +199,9 @@ impl<'a> System<'a> for Sys {
|
||||
}
|
||||
|
||||
let owner_uid = shockwave.owner.unwrap_or(*uid);
|
||||
let change = damage.modify_damage(inventories.get(b), Some(owner_uid));
|
||||
let poise_change = poise_damage.modify_poise_damage(inventories.get(b));
|
||||
let change =
|
||||
damage.modify_damage(inventories.get(b), Some(owner_uid), false, 0.0);
|
||||
|
||||
server_emitter.emit(ServerEvent::Damage { entity: b, change });
|
||||
shockwave_hit_list.hit_entities.push(*uid_b);
|
||||
|
@ -509,7 +509,7 @@ pub fn handle_land_on_ground(server: &Server, entity: EcsEntity, vel: Vec3<f32>)
|
||||
source: DamageSource::Falling,
|
||||
value: falldmg,
|
||||
};
|
||||
let change = damage.modify_damage(inventories.get(entity), None);
|
||||
let change = damage.modify_damage(inventories.get(entity), None, false, 0.0);
|
||||
health.change_by(change);
|
||||
}
|
||||
// Handle poise change
|
||||
|
@ -90,7 +90,7 @@ impl StateExt for State {
|
||||
},
|
||||
Effect::Damage(damage) => {
|
||||
let inventories = self.ecs().read_storage::<Inventory>();
|
||||
let change = damage.modify_damage(inventories.get(entity), source);
|
||||
let change = damage.modify_damage(inventories.get(entity), source, false, 0.0);
|
||||
self.ecs()
|
||||
.write_storage::<comp::Health>()
|
||||
.get_mut(entity)
|
||||
|
Loading…
Reference in New Issue
Block a user