Critical hits are now functional.

This commit is contained in:
Sam 2021-01-25 22:53:52 -05:00
parent 2690e9caa1
commit c3408c084c
14 changed files with 95 additions and 93 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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