diff --git a/common/src/combat.rs b/common/src/combat.rs index e8fb6270da..bc73dc91bb 100644 --- a/common/src/combat.rs +++ b/common/src/combat.rs @@ -469,11 +469,10 @@ impl Attack { } } -/// Checks if we should allow negative effects from one player to another -// FIXME: handle pets? -// This code works only with players. -// You still can kill someone's pet and -// you still can be killed by someone's pet +/// Says if we should allow negative effects from one player to another +/// +/// NOTE: this function doesn't handle pets or friendly-fire, you will need to +/// figure it out on call-side. pub fn may_harm(attacker: Option<&Player>, target: Option<&Player>) -> bool { attacker .zip(target) diff --git a/common/src/comp/agent.rs b/common/src/comp/agent.rs index 72cd5bc641..3760b03544 100644 --- a/common/src/comp/agent.rs +++ b/common/src/comp/agent.rs @@ -3,10 +3,10 @@ use crate::{ path::Chaser, rtsim::RtSimController, trade::{PendingTrade, ReducedInventory, SiteId, SitePrices, TradeId, TradeResult}, - uid::Uid, + uid::{Uid, UidAllocator}, }; use serde::Deserialize; -use specs::{Component, Entity as EcsEntity}; +use specs::{saveload::MarkerAllocator, Component, Entity as EcsEntity}; use specs_idvs::IdvStorage; use std::{collections::VecDeque, fmt}; use strum::IntoEnumIterator; @@ -34,6 +34,15 @@ pub enum Alignment { Passive, } +// Helper function to get owner +pub fn owner_of(alignment: Option, uid_allocator: &UidAllocator) -> Option { + if let Some(Alignment::Owned(uid)) = alignment { + uid_allocator.retrieve_entity_internal(uid.into()) + } else { + None + } +} + #[derive(Copy, Clone, Debug, PartialEq)] pub enum Mark { Merchant, diff --git a/common/systems/src/aura.rs b/common/systems/src/aura.rs index f9201b3d8d..31264e647a 100644 --- a/common/systems/src/aura.rs +++ b/common/systems/src/aura.rs @@ -1,10 +1,11 @@ use common::{ combat, comp::{ + agent::owner_of, aura::{AuraChange, AuraKey, AuraKind, AuraTarget}, buff::{Buff, BuffCategory, BuffChange, BuffSource}, group::Group, - Aura, Auras, BuffKind, Buffs, CharacterState, Health, Player, Pos, + Alignment, Aura, Auras, BuffKind, Buffs, CharacterState, Health, Player, Pos, }, event::{Emitter, EventBus, ServerEvent}, resources::DeltaTime, @@ -27,6 +28,7 @@ pub struct ReadData<'a> { cached_spatial_grid: Read<'a, common::CachedSpatialGrid>, positions: ReadStorage<'a, Pos>, char_states: ReadStorage<'a, CharacterState>, + alignments: ReadStorage<'a, Alignment>, healths: ReadStorage<'a, Health>, groups: ReadStorage<'a, Group>, uids: ReadStorage<'a, Uid>, @@ -174,7 +176,7 @@ fn activate_aura( // TODO: this check will disable friendly fire with PvE switch. // // Which means that you can't apply debuffs on you and your group - // even if it's intented mechanics. + // even if it's intented mechanic. // // Not that we have this for now, but think about this // when we will add this. @@ -185,11 +187,19 @@ fn activate_aura( }, _ => None, }; - owner.map_or(true, |attacker| { - let attacker = read_data.players.get(attacker); - let target = read_data.players.get(target); - combat::may_harm(attacker, target) - }) + let owner_if_pet = |entity| { + // Return owner entity if pet, + // or just return entity back otherwise + owner_of( + read_data.alignments.get(entity).copied(), + &read_data.uid_allocator, + ) + .unwrap_or(entity) + }; + combat::may_harm( + owner.and_then(|owner| read_data.players.get(owner_if_pet(owner))), + read_data.players.get(owner_if_pet(target)), + ) }; conditions_held && (kind.is_buff() || may_harm()) diff --git a/common/systems/src/beam.rs b/common/systems/src/beam.rs index 86854bd814..8b2403c3a4 100644 --- a/common/systems/src/beam.rs +++ b/common/systems/src/beam.rs @@ -1,9 +1,9 @@ use common::{ combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo}, comp::{ - agent::{Sound, SoundKind}, - Beam, BeamSegment, Body, CharacterState, Combo, Energy, Group, Health, HealthSource, - Inventory, Ori, Player, Pos, Scale, Stats, + agent::{owner_of, Sound, SoundKind}, + Alignment, Beam, BeamSegment, Body, CharacterState, Combo, Energy, Group, Health, + HealthSource, Inventory, Ori, Player, Pos, Scale, Stats, }, event::{EventBus, ServerEvent}, outcome::Outcome, @@ -36,6 +36,7 @@ pub struct ReadData<'a> { uids: ReadStorage<'a, Uid>, positions: ReadStorage<'a, Pos>, orientations: ReadStorage<'a, Ori>, + alignments: ReadStorage<'a, Alignment>, scales: ReadStorage<'a, Scale>, bodies: ReadStorage<'a, Body>, healths: ReadStorage<'a, Health>, @@ -214,9 +215,19 @@ impl<'a> System<'a> for Sys { }; + // PvP check + let owner_if_pet = |entity| { + // Return owner entity if pet, + // or just return entity back otherwise + owner_of( + read_data.alignments.get(entity).copied(), + &read_data.uid_allocator, + ) + .unwrap_or(entity) + }; let may_harm = combat::may_harm( - beam_owner.and_then(|owner| read_data.players.get(owner)), - read_data.players.get(target), + beam_owner.and_then(|owner| read_data.players.get(owner_if_pet(owner))), + read_data.players.get(owner_if_pet(target)), ); let attack_options = AttackOptions { // No luck with dodging beams diff --git a/common/systems/src/melee.rs b/common/systems/src/melee.rs index 433f6b48c5..005efad509 100644 --- a/common/systems/src/melee.rs +++ b/common/systems/src/melee.rs @@ -1,14 +1,14 @@ use common::{ combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo}, comp::{ - agent::{Sound, SoundKind}, - Body, CharacterState, Combo, Energy, Group, Health, Inventory, Melee, Ori, Player, Pos, - Scale, Stats, + agent::{owner_of, Sound, SoundKind}, + Alignment, Body, CharacterState, Combo, Energy, Group, Health, Inventory, Melee, Ori, + Player, Pos, Scale, Stats, }, event::{EventBus, ServerEvent}, outcome::Outcome, resources::Time, - uid::Uid, + uid::{Uid, UidAllocator}, util::Dir, GroupTarget, }; @@ -21,11 +21,13 @@ use vek::*; #[derive(SystemData)] pub struct ReadData<'a> { time: Read<'a, Time>, + uid_allocator: Read<'a, UidAllocator>, entities: Entities<'a>, players: ReadStorage<'a, Player>, uids: ReadStorage<'a, Uid>, positions: ReadStorage<'a, Pos>, orientations: ReadStorage<'a, Ori>, + alignments: ReadStorage<'a, Alignment>, scales: ReadStorage<'a, Scale>, bodies: ReadStorage<'a, Body>, healths: ReadStorage<'a, Health>, @@ -162,9 +164,19 @@ impl<'a> System<'a> for Sys { char_state: read_data.char_states.get(target), }; + // PvP check + let owner_if_pet = |entity| { + // Return owner entity if pet, + // or just return entity back otherwise + owner_of( + read_data.alignments.get(entity).copied(), + &read_data.uid_allocator, + ) + .unwrap_or(entity) + }; let may_harm = combat::may_harm( - read_data.players.get(attacker), - read_data.players.get(target), + read_data.players.get(owner_if_pet(attacker)), + read_data.players.get(owner_if_pet(target)), ); let attack_options = AttackOptions { diff --git a/common/systems/src/projectile.rs b/common/systems/src/projectile.rs index 44f7fe66cd..f27ac95466 100644 --- a/common/systems/src/projectile.rs +++ b/common/systems/src/projectile.rs @@ -1,9 +1,9 @@ use common::{ combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo}, comp::{ - agent::{Sound, SoundKind}, - projectile, Body, CharacterState, Combo, Energy, Group, Health, HealthSource, Inventory, - Ori, PhysicsState, Player, Pos, Projectile, Stats, Vel, + agent::{owner_of, Sound, SoundKind}, + projectile, Alignment, Body, CharacterState, Combo, Energy, Group, Health, HealthSource, + Inventory, Ori, PhysicsState, Player, Pos, Projectile, Stats, Vel, }, event::{Emitter, EventBus, ServerEvent}, outcome::Outcome, @@ -31,6 +31,7 @@ pub struct ReadData<'a> { server_bus: Read<'a, EventBus>, uids: ReadStorage<'a, Uid>, positions: ReadStorage<'a, Pos>, + alignments: ReadStorage<'a, Alignment>, physics_states: ReadStorage<'a, PhysicsState>, velocities: ReadStorage<'a, Vel>, inventories: ReadStorage<'a, Inventory>, @@ -299,9 +300,19 @@ fn dispatch_hit( }); } + // PvP check + let owner_if_pet = |entity| { + // Return owner entity if pet, + // or just return entity back otherwise + owner_of( + read_data.alignments.get(entity).copied(), + &read_data.uid_allocator, + ) + .unwrap_or(entity) + }; let may_harm = combat::may_harm( - owner.and_then(|owner| read_data.players.get(owner)), - read_data.players.get(target), + owner.and_then(|owner| read_data.players.get(owner_if_pet(owner))), + read_data.players.get(owner_if_pet(target)), ); let attack_options = AttackOptions { diff --git a/common/systems/src/shockwave.rs b/common/systems/src/shockwave.rs index 86ad74eca7..75fd5db924 100644 --- a/common/systems/src/shockwave.rs +++ b/common/systems/src/shockwave.rs @@ -1,9 +1,9 @@ use common::{ combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo}, comp::{ - agent::{Sound, SoundKind}, - Body, CharacterState, Combo, Energy, Group, Health, HealthSource, Inventory, Ori, - PhysicsState, Player, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats, + agent::{owner_of, Sound, SoundKind}, + Alignment, Body, CharacterState, Combo, Energy, Group, Health, HealthSource, Inventory, + Ori, PhysicsState, Player, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats, }, event::{EventBus, ServerEvent}, outcome::Outcome, @@ -31,6 +31,7 @@ pub struct ReadData<'a> { uids: ReadStorage<'a, Uid>, positions: ReadStorage<'a, Pos>, orientations: ReadStorage<'a, Ori>, + alignments: ReadStorage<'a, Alignment>, scales: ReadStorage<'a, Scale>, bodies: ReadStorage<'a, Body>, healths: ReadStorage<'a, Health>, @@ -209,9 +210,20 @@ impl<'a> System<'a> for Sys { char_state: read_data.character_states.get(target), }; + // PvP check + let owner_if_pet = |entity| { + // Return owner entity if pet, + // or just return entity back otherwise + owner_of( + read_data.alignments.get(entity).copied(), + &read_data.uid_allocator, + ) + .unwrap_or(entity) + }; let may_harm = combat::may_harm( - shockwave_owner.and_then(|owner| read_data.players.get(owner)), - read_data.players.get(target), + shockwave_owner + .and_then(|owner| read_data.players.get(owner_if_pet(owner))), + read_data.players.get(owner_if_pet(target)), ); let attack_options = AttackOptions { // Trying roll during earthquake isn't the best idea diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 56978c7380..5d81428093 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -1,7 +1,7 @@ use crate::{ client::Client, comp::{ - agent::{Agent, AgentEvent, Sound, SoundKind}, + agent::{owner_of, Agent, AgentEvent, Sound, SoundKind}, biped_large, bird_large, quadruped_low, quadruped_medium, quadruped_small, skills::SkillGroupKind, theropod, BuffKind, BuffSource, PhysicsState, @@ -873,6 +873,8 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o let energies = &ecs.read_storage::(); let combos = &ecs.read_storage::(); let inventories = &ecs.read_storage::(); + let alignments = &ecs.read_storage::(); + let uid_allocator = &ecs.read_resource::(); let players = &ecs.read_storage::(); for ( entity_b, @@ -942,9 +944,16 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o char_state: char_state_b_maybe, }; + // PvP check + let owner_if_pet = |entity| { + // Return owner entity if pet, + // or just return entity back otherwise + owner_of(alignments.get(entity).copied(), uid_allocator) + .unwrap_or(entity) + }; let may_harm = combat::may_harm( - owner_entity.and_then(|owner| players.get(owner)), - players.get(entity_b), + owner_entity.and_then(|owner| players.get(owner_if_pet(owner))), + players.get(owner_if_pet(entity_b)), ); let attack_options = combat::AttackOptions { // cool guyz maybe don't look at explosions @@ -968,6 +977,8 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o } }, RadiusEffect::Entity(mut effect) => { + let alignments = &ecs.read_storage::(); + let uid_allocator = &ecs.read_resource::(); let players = &ecs.read_storage::(); for (entity_b, pos_b, body_b_maybe) in ( &ecs.entities(), @@ -994,12 +1005,17 @@ pub fn handle_explosion(server: &Server, pos: Vec3, explosion: Explosion, o // you want to harm yourself. // // This can be changed later. + // PvP check + let owner_if_pet = |entity| { + // Return owner entity if pet, + // or just return entity back otherwise + owner_of(alignments.get(entity).copied(), uid_allocator).unwrap_or(entity) + }; let may_harm = || { - owner_entity.map_or(false, |attacker| { - let attacker_player = players.get(attacker); - let target_player = players.get(entity_b); - combat::may_harm(attacker_player, target_player) || attacker == entity_b - }) + combat::may_harm( + owner_entity.and_then(|owner| players.get(owner_if_pet(owner))), + players.get(owner_if_pet(entity_b)), + ) || owner_entity.map_or(true, |entity_a| entity_a == entity_b) }; if strength > 0.0 { let is_alive = ecs