diff --git a/common/systems/src/projectile.rs b/common/systems/src/projectile.rs index a183306bbd..941e6f6254 100644 --- a/common/systems/src/projectile.rs +++ b/common/systems/src/projectile.rs @@ -5,7 +5,7 @@ use common::{ projectile, Body, CharacterState, Combo, Energy, Group, Health, HealthSource, Inventory, Ori, PhysicsState, Player, Pos, Projectile, Stats, Vel, }, - event::{EventBus, ServerEvent}, + event::{Emitter, EventBus, ServerEvent}, outcome::Outcome, resources::{DeltaTime, Time}, uid::{Uid, UidAllocator}, @@ -15,8 +15,8 @@ use common::{ use common_ecs::{Job, Origin, Phase, System}; use rand::{thread_rng, Rng}; use specs::{ - saveload::MarkerAllocator, shred::ResourceId, Entities, Join, Read, ReadStorage, SystemData, - World, Write, WriteStorage, + saveload::MarkerAllocator, shred::ResourceId, Entities, Entity as EcsEntity, Join, Read, + ReadStorage, SystemData, World, Write, WriteStorage, }; use std::time::Duration; use vek::*; @@ -115,106 +115,36 @@ impl<'a> System<'a> for Sys { } let projectile = &mut *projectile; - // FIXME: this code is highway to hell, resolve this + + let entity_of = + |uid: Uid| read_data.uid_allocator.retrieve_entity_internal(uid.into()); for effect in projectile.hit_entity.drain(..) { - match effect { - projectile::Effect::Attack(attack) => { - if let Some(target) = read_data - .uid_allocator - .retrieve_entity_internal(other.into()) - { - if let (Some(pos), Some(ori)) = - (read_data.positions.get(target), orientations.get(entity)) - { - let dir = ori.look_dir(); + let owner = projectile.owner.and_then(entity_of); + let projectile_info = ProjectileInfo { + entity, + effect, + owner_uid: projectile.owner, + owner, + ori: orientations.get(entity), + pos, + }; - let owner_entity = projectile.owner.and_then(|u| { - read_data.uid_allocator.retrieve_entity_internal(u.into()) - }); + let target = entity_of(other); + let projectile_target_info = ProjectileTargetInfo { + uid: other, + entity: target, + target_group, + ori: target.and_then(|target| orientations.get(target)), + }; - let attacker_info = - owner_entity.zip(projectile.owner).map(|(entity, uid)| { - AttackerInfo { - entity, - uid, - energy: read_data.energies.get(entity), - combo: read_data.combos.get(entity), - inventory: read_data.inventories.get(entity), - } - }); - - let target_info = TargetInfo { - entity: target, - uid: other, - inventory: read_data.inventories.get(target), - stats: read_data.stats.get(target), - health: read_data.healths.get(target), - pos: pos.0, - ori: orientations.get(target), - char_state: read_data.character_states.get(target), - }; - - if let Some(&body) = read_data.bodies.get(entity) { - outcomes.push(Outcome::ProjectileHit { - pos: pos.0, - body, - vel: read_data - .velocities - .get(entity) - .map_or(Vec3::zero(), |v| v.0), - source: projectile.owner, - target: read_data.uids.get(target).copied(), - }); - } - - let avoid_harm = combat::avoid_player_harm( - owner_entity.and_then(|owner| read_data.players.get(owner)), - read_data.players.get(target), - ); - - let attack_options = AttackOptions { - // They say witchers can dodge arrows, - // but we don't have witchers - target_dodging: false, - avoid_harm, - target_group, - }; - attack.apply_attack( - attacker_info, - target_info, - dir, - attack_options, - 1.0, - AttackSource::Projectile, - |e| server_emitter.emit(e), - |o| outcomes.push(o), - ); - } - } - }, - projectile::Effect::Explode(e) => { - server_emitter.emit(ServerEvent::Explosion { - pos: pos.0, - explosion: e, - owner: projectile.owner, - }); - }, - projectile::Effect::Vanish => { - server_emitter.emit(ServerEvent::Destroy { - entity, - cause: HealthSource::World, - }); - projectile_vanished = true; - }, - projectile::Effect::Possess => { - if other != projectile.owner.unwrap() { - if let Some(owner) = projectile.owner { - server_emitter.emit(ServerEvent::Possess(owner, other)); - } - } - }, - _ => {}, - } + dispatch_hit( + projectile_info, + projectile_target_info, + &read_data, + &mut projectile_vanished, + &mut outcomes, + &mut server_emitter, + ); } if projectile_vanished { @@ -266,3 +196,141 @@ impl<'a> System<'a> for Sys { } } } + +struct ProjectileInfo<'a> { + entity: EcsEntity, + effect: projectile::Effect, + owner_uid: Option, + owner: Option, + ori: Option<&'a Ori>, + pos: &'a Pos, +} + +struct ProjectileTargetInfo<'a> { + uid: Uid, + entity: Option, + target_group: GroupTarget, + ori: Option<&'a Ori>, +} + +fn dispatch_hit( + projectile_info: ProjectileInfo, + projectile_target_info: ProjectileTargetInfo, + read_data: &ReadData, + projectile_vanished: &mut bool, + outcomes: &mut Vec, + server_emitter: &mut Emitter, +) { + match projectile_info.effect { + projectile::Effect::Attack(attack) => { + let target_uid = projectile_target_info.uid; + let target = if let Some(entity) = projectile_target_info.entity { + entity + } else { + return; + }; + + let (target_pos, projectile_dir) = { + let target_pos = read_data.positions.get(target); + let projectile_ori = projectile_info.ori; + match target_pos.zip(projectile_ori) { + Some((tgt_pos, proj_ori)) => { + let Pos(tgt_pos) = tgt_pos; + (*tgt_pos, proj_ori.look_dir()) + }, + None => return, + } + }; + + let owner = projectile_info.owner; + let projectile_entity = projectile_info.entity; + + let attacker_info = + owner + .zip(projectile_info.owner_uid) + .map(|(entity, uid)| AttackerInfo { + entity, + uid, + energy: read_data.energies.get(entity), + combo: read_data.combos.get(entity), + inventory: read_data.inventories.get(entity), + }); + + let target_info = TargetInfo { + entity: target, + uid: target_uid, + inventory: read_data.inventories.get(target), + stats: read_data.stats.get(target), + health: read_data.healths.get(target), + pos: target_pos, + ori: projectile_target_info.ori, + char_state: read_data.character_states.get(target), + }; + + // TODO: Is it possible to have projectile without body?? + if let Some(&body) = read_data.bodies.get(projectile_entity) { + outcomes.push(Outcome::ProjectileHit { + pos: target_pos, + body, + vel: read_data + .velocities + .get(projectile_entity) + .map_or(Vec3::zero(), |v| v.0), + source: projectile_info.owner_uid, + target: read_data.uids.get(target).copied(), + }); + } + + let avoid_harm = combat::avoid_player_harm( + owner.and_then(|owner| read_data.players.get(owner)), + read_data.players.get(target), + ); + + let attack_options = AttackOptions { + // They say witchers can dodge arrows, + // but we don't have witchers + target_dodging: false, + avoid_harm, + target_group: projectile_target_info.target_group, + }; + + attack.apply_attack( + attacker_info, + target_info, + projectile_dir, + attack_options, + 1.0, + AttackSource::Projectile, + |e| server_emitter.emit(e), + |o| outcomes.push(o), + ); + }, + projectile::Effect::Explode(e) => { + let Pos(pos) = *projectile_info.pos; + let owner_uid = projectile_info.owner_uid; + server_emitter.emit(ServerEvent::Explosion { + pos, + explosion: e, + owner: owner_uid, + }); + }, + projectile::Effect::Vanish => { + let entity = projectile_info.entity; + server_emitter.emit(ServerEvent::Destroy { + entity, + cause: HealthSource::World, + }); + *projectile_vanished = true; + }, + projectile::Effect::Possess => { + let target_uid = projectile_target_info.uid; + let owner_uid = projectile_info.owner_uid; + if let Some(owner_uid) = owner_uid { + if target_uid != owner_uid { + server_emitter.emit(ServerEvent::Possess(owner_uid, target_uid)); + } + } + }, + projectile::Effect::Stick => {}, + } +}