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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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