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,
|
Body, BuffKind, Health, HealthChange, HealthSource, Inventory, Stats,
|
||||||
},
|
},
|
||||||
effect,
|
effect,
|
||||||
|
event::ServerEvent,
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
util::Dir,
|
util::Dir,
|
||||||
};
|
};
|
||||||
|
use rand::{thread_rng, Rng};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use specs::Entity as EcsEntity;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||||
@ -28,8 +31,8 @@ pub enum GroupTarget {
|
|||||||
pub struct Attack {
|
pub struct Attack {
|
||||||
damages: Vec<DamageComponent>,
|
damages: Vec<DamageComponent>,
|
||||||
effects: Vec<EffectComponent>,
|
effects: Vec<EffectComponent>,
|
||||||
pub crit_chance: f32,
|
crit_chance: f32,
|
||||||
pub crit_multiplier: f32,
|
crit_multiplier: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Attack {
|
impl Default for Attack {
|
||||||
@ -60,12 +63,47 @@ impl Attack {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn damages(&self) -> impl Iterator<Item = &DamageComponent> {
|
pub fn apply_attack(
|
||||||
self.damages.iter()
|
&self,
|
||||||
}
|
target_group: GroupTarget,
|
||||||
|
target_entity: EcsEntity,
|
||||||
pub fn effects(&self) -> impl Iterator<Item = &EffectComponent> {
|
inventory: Option<&Inventory>,
|
||||||
self.effects.iter()
|
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.effects.push(effect);
|
||||||
self
|
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)]
|
#[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 mut damage = self.value;
|
||||||
let damage_reduction = inventory.map_or(0.0, |inv| Damage::compute_damage_reduction(inv));
|
let damage_reduction = inventory.map_or(0.0, |inv| Damage::compute_damage_reduction(inv));
|
||||||
match self.source {
|
match self.source {
|
||||||
DamageSource::Melee => {
|
DamageSource::Melee => {
|
||||||
// Critical hit
|
// Critical hit
|
||||||
let mut critdamage = 0.0;
|
let mut critdamage = 0.0;
|
||||||
if rand::random() {
|
if is_crit {
|
||||||
critdamage = damage * 0.3;
|
critdamage = damage * (crit_mult - 1.0);
|
||||||
}
|
}
|
||||||
// Armor
|
// Armor
|
||||||
damage *= 1.0 - damage_reduction;
|
damage *= 1.0 - damage_reduction;
|
||||||
@ -194,8 +226,8 @@ impl Damage {
|
|||||||
},
|
},
|
||||||
DamageSource::Projectile => {
|
DamageSource::Projectile => {
|
||||||
// Critical hit
|
// Critical hit
|
||||||
if rand::random() {
|
if is_crit {
|
||||||
damage *= 1.2;
|
damage *= crit_mult;
|
||||||
}
|
}
|
||||||
// Armor
|
// Armor
|
||||||
damage *= 1.0 - damage_reduction;
|
damage *= 1.0 - damage_reduction;
|
||||||
|
@ -103,7 +103,7 @@ impl CharacterBehavior for Data {
|
|||||||
});
|
});
|
||||||
let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
||||||
.with_effect(knockback);
|
.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
|
// Hit attempt
|
||||||
data.updater.insert(data.entity, MeleeAttack {
|
data.updater.insert(data.entity, MeleeAttack {
|
||||||
|
@ -175,7 +175,7 @@ impl CharacterBehavior for Data {
|
|||||||
});
|
});
|
||||||
let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
||||||
.with_effect(knockback);
|
.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
|
// Hit attempt
|
||||||
data.updater.insert(data.entity, MeleeAttack {
|
data.updater.insert(data.entity, MeleeAttack {
|
||||||
|
@ -193,8 +193,8 @@ impl CharacterBehavior for Data {
|
|||||||
});
|
});
|
||||||
let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
||||||
.with_effect(knockback);
|
.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 {
|
data.updater.insert(data.entity, MeleeAttack {
|
||||||
attack,
|
attack,
|
||||||
range: self.static_data.stage_data[stage_index].range,
|
range: self.static_data.stage_data[stage_index].range,
|
||||||
|
@ -153,7 +153,7 @@ impl CharacterBehavior for Data {
|
|||||||
let damage =
|
let damage =
|
||||||
DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
||||||
.with_effect(knockback);
|
.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 {
|
data.updater.insert(data.entity, MeleeAttack {
|
||||||
attack,
|
attack,
|
||||||
|
@ -158,7 +158,7 @@ impl CharacterBehavior for Data {
|
|||||||
});
|
});
|
||||||
let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
||||||
.with_effect(knockback);
|
.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
|
// Hit attempt, when animation plays
|
||||||
data.updater.insert(data.entity, MeleeAttack {
|
data.updater.insert(data.entity, MeleeAttack {
|
||||||
|
@ -126,7 +126,7 @@ impl CharacterBehavior for Data {
|
|||||||
});
|
});
|
||||||
let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
let damage = DamageComponent::new(damage, Some(GroupTarget::OutOfGroup))
|
||||||
.with_effect(knockback);
|
.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
|
// Hit attempt
|
||||||
data.updater.insert(data.entity, MeleeAttack {
|
data.updater.insert(data.entity, MeleeAttack {
|
||||||
|
@ -166,7 +166,13 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Modify damage
|
// 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 {
|
match target {
|
||||||
Some(GroupTarget::OutOfGroup) => {
|
Some(GroupTarget::OutOfGroup) => {
|
||||||
server_emitter.emit(ServerEvent::Damage { entity: b, change });
|
server_emitter.emit(ServerEvent::Damage { entity: b, change });
|
||||||
|
@ -16,15 +16,7 @@ pub mod state;
|
|||||||
mod stats;
|
mod stats;
|
||||||
|
|
||||||
// External
|
// External
|
||||||
use common::{
|
use specs::DispatcherBuilder;
|
||||||
combat::{Attack, AttackEffect, GroupTarget},
|
|
||||||
comp::Inventory,
|
|
||||||
event::ServerEvent,
|
|
||||||
uid::Uid,
|
|
||||||
util::Dir,
|
|
||||||
};
|
|
||||||
use rand::{thread_rng, Rng};
|
|
||||||
use specs::{DispatcherBuilder, Entity as EcsEntity};
|
|
||||||
|
|
||||||
// System names
|
// System names
|
||||||
pub const CHARACTER_BEHAVIOR_SYS: &str = "character_behavior_sys";
|
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(melee::Sys, MELEE_SYS, &[PROJECTILE_SYS]);
|
||||||
dispatch_builder.add(aura::Sys, AURAS_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::{
|
use common::{
|
||||||
comp::{buff, group, Body, CharacterState, Health, Inventory, MeleeAttack, Ori, Pos, Scale},
|
comp::{buff, group, Body, CharacterState, Health, Inventory, MeleeAttack, Ori, Pos, Scale},
|
||||||
event::{EventBus, LocalEvent, ServerEvent},
|
event::{EventBus, LocalEvent, ServerEvent},
|
||||||
@ -76,7 +75,15 @@ impl<'a> System<'a> for Sys {
|
|||||||
attack.applied = true;
|
attack.applied = true;
|
||||||
|
|
||||||
// Go through all other entities
|
// 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,
|
&entities,
|
||||||
&positions,
|
&positions,
|
||||||
scales.maybe(),
|
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 dir = Dir::new((pos_b.0 - pos.0).try_normalized().unwrap_or(*ori.0));
|
||||||
|
|
||||||
let server_events = apply_attack(
|
let server_events =
|
||||||
&attack.attack,
|
attack
|
||||||
target_group,
|
.attack
|
||||||
b,
|
.apply_attack(target_group, b, inventory_b_maybe, *uid, dir);
|
||||||
inventory_b_maybe,
|
|
||||||
*uid,
|
|
||||||
dir,
|
|
||||||
);
|
|
||||||
|
|
||||||
for event in server_events {
|
for event in server_events {
|
||||||
server_emitter.emit(event);
|
server_emitter.emit(event);
|
||||||
|
@ -118,8 +118,12 @@ impl<'a> System<'a> for Sys {
|
|||||||
uid_allocator.retrieve_entity_internal(other.into())
|
uid_allocator.retrieve_entity_internal(other.into())
|
||||||
{
|
{
|
||||||
let other_entity_inventory = inventories.get(other_entity);
|
let other_entity_inventory = inventories.get(other_entity);
|
||||||
let change =
|
let change = damage.modify_damage(
|
||||||
damage.modify_damage(other_entity_inventory, projectile.owner);
|
other_entity_inventory,
|
||||||
|
projectile.owner,
|
||||||
|
false,
|
||||||
|
0.0,
|
||||||
|
);
|
||||||
server_emitter.emit(ServerEvent::Damage {
|
server_emitter.emit(ServerEvent::Damage {
|
||||||
entity: other_entity,
|
entity: other_entity,
|
||||||
change,
|
change,
|
||||||
|
@ -199,8 +199,9 @@ impl<'a> System<'a> for Sys {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let owner_uid = shockwave.owner.unwrap_or(*uid);
|
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 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 });
|
server_emitter.emit(ServerEvent::Damage { entity: b, change });
|
||||||
shockwave_hit_list.hit_entities.push(*uid_b);
|
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,
|
source: DamageSource::Falling,
|
||||||
value: falldmg,
|
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);
|
health.change_by(change);
|
||||||
}
|
}
|
||||||
// Handle poise change
|
// Handle poise change
|
||||||
|
@ -90,7 +90,7 @@ impl StateExt for State {
|
|||||||
},
|
},
|
||||||
Effect::Damage(damage) => {
|
Effect::Damage(damage) => {
|
||||||
let inventories = self.ecs().read_storage::<Inventory>();
|
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()
|
self.ecs()
|
||||||
.write_storage::<comp::Health>()
|
.write_storage::<comp::Health>()
|
||||||
.get_mut(entity)
|
.get_mut(entity)
|
||||||
|
Loading…
Reference in New Issue
Block a user