Deduplicate pvp-checks

This commit is contained in:
juliancoffee
2021-08-27 21:49:58 +03:00
parent 2e79c61123
commit 338e81de10
10 changed files with 101 additions and 107 deletions

View File

@ -323,6 +323,7 @@ impl ChatCommand {
true for overwrite to alter an existing ban..", true for overwrite to alter an existing ban..",
Some(Moderator), Some(Moderator),
), ),
#[rustfmt::skip]
ChatCommand::BattleMode => cmd( ChatCommand::BattleMode => cmd(
vec![Enum( vec![Enum(
"battle mode", "battle mode",

View File

@ -12,13 +12,13 @@ use crate::{
}, },
poise::PoiseChange, poise::PoiseChange,
skills::SkillGroupKind, skills::SkillGroupKind,
Body, CharacterState, Combo, Energy, EnergyChange, EnergySource, Health, HealthChange, Alignment, Body, CharacterState, Combo, Energy, EnergyChange, EnergySource, Health,
HealthSource, Inventory, Ori, Player, Poise, SkillSet, Stats, HealthChange, HealthSource, Inventory, Ori, Player, Poise, SkillSet, Stats,
}, },
event::ServerEvent, event::ServerEvent,
outcome::Outcome, outcome::Outcome,
states::utils::StageSection, states::utils::StageSection,
uid::Uid, uid::{Uid, UidAllocator},
util::Dir, util::Dir,
}; };
@ -28,7 +28,7 @@ use rand::{thread_rng, Rng};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use specs::Entity as EcsEntity; use specs::{saveload::MarkerAllocator, Entity as EcsEntity, ReadStorage};
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use std::{ops::MulAssign, time::Duration}; use std::{ops::MulAssign, time::Duration};
#[cfg(not(target_arch = "wasm32"))] use vek::*; #[cfg(not(target_arch = "wasm32"))] use vek::*;
@ -469,14 +469,56 @@ impl Attack {
} }
} }
/// Says if we should allow negative effects from one player to another /// Function that checks for unintentional PvP between players.
/// ///
/// NOTE: this function doesn't handle pets or friendly-fire, you will need to /// Returns `false` if attack will create unintentional conflict,
/// figure it out on call-side. /// e.g. if player with PvE mode will harm pets of other players
pub fn may_harm(attacker: Option<&Player>, target: Option<&Player>) -> bool { /// or other players will do the same to such player.
attacker ///
.zip(target) /// If both players have PvP mode enabled, interact with NPC and
.map_or(true, |(attacker, target)| attacker.may_harm(target)) /// in any other case, this function will return `true`
// TODO: add parameter for doing self-harm?
pub fn may_harm(
alignments: &ReadStorage<Alignment>,
players: &ReadStorage<Player>,
uid_allocator: &UidAllocator,
attacker: Option<EcsEntity>,
target: EcsEntity,
) -> bool {
// Return owner entity if pet,
// or just return entity back otherwise
let owner_if_pet = |entity| {
let alignment = alignments.get(entity).copied();
if let Some(Alignment::Owned(uid)) = alignment {
// return original entity
// if can't get owner
uid_allocator
.retrieve_entity_internal(uid.into())
.unwrap_or(entity)
} else {
entity
}
};
// Just return ok if attacker is unknown, it's probably
// environment or command.
let attacker = match attacker {
Some(attacker) => attacker,
None => return true,
};
// "Dereference" to owner if this is a pet.
let attacker = owner_if_pet(attacker);
let target = owner_if_pet(target);
// Get player components
let attacker_info = players.get(attacker);
let target_info = players.get(target);
// Return `true` if not players.
attacker_info
.zip(target_info)
.map_or(true, |(a, t)| a.may_harm(t))
} }
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]

View File

@ -3,10 +3,10 @@ use crate::{
path::Chaser, path::Chaser,
rtsim::RtSimController, rtsim::RtSimController,
trade::{PendingTrade, ReducedInventory, SiteId, SitePrices, TradeId, TradeResult}, trade::{PendingTrade, ReducedInventory, SiteId, SitePrices, TradeId, TradeResult},
uid::{Uid, UidAllocator}, uid::Uid,
}; };
use serde::Deserialize; use serde::Deserialize;
use specs::{saveload::MarkerAllocator, Component, Entity as EcsEntity}; use specs::{Component, Entity as EcsEntity};
use specs_idvs::IdvStorage; use specs_idvs::IdvStorage;
use std::{collections::VecDeque, fmt}; use std::{collections::VecDeque, fmt};
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
@ -34,15 +34,6 @@ pub enum Alignment {
Passive, Passive,
} }
// Helper function to get owner
pub fn owner_of(alignment: Option<Alignment>, uid_allocator: &UidAllocator) -> Option<EcsEntity> {
if let Some(Alignment::Owned(uid)) = alignment {
uid_allocator.retrieve_entity_internal(uid.into())
} else {
None
}
}
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub enum Mark { pub enum Mark {
Merchant, Merchant,

View File

@ -1,7 +1,6 @@
use common::{ use common::{
combat, combat,
comp::{ comp::{
agent::owner_of,
aura::{AuraChange, AuraKey, AuraKind, AuraTarget}, aura::{AuraChange, AuraKey, AuraKind, AuraTarget},
buff::{Buff, BuffCategory, BuffChange, BuffSource}, buff::{Buff, BuffCategory, BuffChange, BuffSource},
group::Group, group::Group,
@ -178,7 +177,7 @@ fn activate_aura(
// Which means that you can't apply debuffs on you and your group // Which means that you can't apply debuffs on you and your group
// even if it's intented mechanic. // even if it's intented mechanic.
// //
// Not that we have this for now, but think about this // We don't have this for now, but think about this
// when we will add this. // when we will add this.
let may_harm = || { let may_harm = || {
let owner = match source { let owner = match source {
@ -187,18 +186,12 @@ fn activate_aura(
}, },
_ => None, _ => None,
}; };
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( combat::may_harm(
owner.and_then(|owner| read_data.players.get(owner_if_pet(owner))), &read_data.alignments,
read_data.players.get(owner_if_pet(target)), &read_data.players,
&read_data.uid_allocator,
owner,
target,
) )
}; };

View File

@ -1,7 +1,7 @@
use common::{ use common::{
combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo}, combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo},
comp::{ comp::{
agent::{owner_of, Sound, SoundKind}, agent::{Sound, SoundKind},
Alignment, Beam, BeamSegment, Body, CharacterState, Combo, Energy, Group, Health, Alignment, Beam, BeamSegment, Body, CharacterState, Combo, Energy, Group, Health,
HealthSource, Inventory, Ori, Player, Pos, Scale, Stats, HealthSource, Inventory, Ori, Player, Pos, Scale, Stats,
}, },
@ -216,18 +216,12 @@ impl<'a> System<'a> for Sys {
// PvP check // 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( let may_harm = combat::may_harm(
beam_owner.and_then(|owner| read_data.players.get(owner_if_pet(owner))), &read_data.alignments,
read_data.players.get(owner_if_pet(target)), &read_data.players,
&read_data.uid_allocator,
beam_owner,
target,
); );
let attack_options = AttackOptions { let attack_options = AttackOptions {
// No luck with dodging beams // No luck with dodging beams

View File

@ -1,7 +1,7 @@
use common::{ use common::{
combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo}, combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo},
comp::{ comp::{
agent::{owner_of, Sound, SoundKind}, agent::{Sound, SoundKind},
Alignment, Body, CharacterState, Combo, Energy, Group, Health, Inventory, Melee, Ori, Alignment, Body, CharacterState, Combo, Energy, Group, Health, Inventory, Melee, Ori,
Player, Pos, Scale, Stats, Player, Pos, Scale, Stats,
}, },
@ -165,18 +165,12 @@ impl<'a> System<'a> for Sys {
}; };
// PvP check // 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( let may_harm = combat::may_harm(
read_data.players.get(owner_if_pet(attacker)), &read_data.alignments,
read_data.players.get(owner_if_pet(target)), &read_data.players,
&read_data.uid_allocator,
Some(attacker),
target,
); );
let attack_options = AttackOptions { let attack_options = AttackOptions {

View File

@ -1,7 +1,7 @@
use common::{ use common::{
combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo}, combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo},
comp::{ comp::{
agent::{owner_of, Sound, SoundKind}, agent::{Sound, SoundKind},
projectile, Alignment, Body, CharacterState, Combo, Energy, Group, Health, HealthSource, projectile, Alignment, Body, CharacterState, Combo, Energy, Group, Health, HealthSource,
Inventory, Ori, PhysicsState, Player, Pos, Projectile, Stats, Vel, Inventory, Ori, PhysicsState, Player, Pos, Projectile, Stats, Vel,
}, },
@ -301,18 +301,12 @@ fn dispatch_hit(
} }
// PvP check // 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( let may_harm = combat::may_harm(
owner.and_then(|owner| read_data.players.get(owner_if_pet(owner))), &read_data.alignments,
read_data.players.get(owner_if_pet(target)), &read_data.players,
&read_data.uid_allocator,
owner,
target,
); );
let attack_options = AttackOptions { let attack_options = AttackOptions {

View File

@ -1,7 +1,7 @@
use common::{ use common::{
combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo}, combat::{self, AttackOptions, AttackSource, AttackerInfo, TargetInfo},
comp::{ comp::{
agent::{owner_of, Sound, SoundKind}, agent::{Sound, SoundKind},
Alignment, Body, CharacterState, Combo, Energy, Group, Health, HealthSource, Inventory, Alignment, Body, CharacterState, Combo, Energy, Group, Health, HealthSource, Inventory,
Ori, PhysicsState, Player, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats, Ori, PhysicsState, Player, Pos, Scale, Shockwave, ShockwaveHitEntities, Stats,
}, },
@ -211,19 +211,12 @@ impl<'a> System<'a> for Sys {
}; };
// PvP check // 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( let may_harm = combat::may_harm(
shockwave_owner &read_data.alignments,
.and_then(|owner| read_data.players.get(owner_if_pet(owner))), &read_data.players,
read_data.players.get(owner_if_pet(target)), &read_data.uid_allocator,
shockwave_owner,
target,
); );
let attack_options = AttackOptions { let attack_options = AttackOptions {
// Trying roll during earthquake isn't the best idea // Trying roll during earthquake isn't the best idea

View File

@ -31,7 +31,7 @@ use common::{
event::{EventBus, ServerEvent}, event::{EventBus, ServerEvent},
generation::EntityInfo, generation::EntityInfo,
npc::{self, get_npc_name}, npc::{self, get_npc_name},
resources::{PlayerPhysicsSettings, TimeOfDay, BattleMode}, resources::{BattleMode, PlayerPhysicsSettings, TimeOfDay},
terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize}, terrain::{Block, BlockKind, SpriteKind, TerrainChunkSize},
uid::Uid, uid::Uid,
vol::RectVolSize, vol::RectVolSize,

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
client::Client, client::Client,
comp::{ comp::{
agent::{owner_of, Agent, AgentEvent, Sound, SoundKind}, agent::{Agent, AgentEvent, Sound, SoundKind},
biped_large, bird_large, quadruped_low, quadruped_medium, quadruped_small, biped_large, bird_large, quadruped_low, quadruped_medium, quadruped_small,
skills::SkillGroupKind, skills::SkillGroupKind,
theropod, BuffKind, BuffSource, PhysicsState, theropod, BuffKind, BuffSource, PhysicsState,
@ -945,15 +945,12 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
}; };
// PvP check // 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( let may_harm = combat::may_harm(
owner_entity.and_then(|owner| players.get(owner_if_pet(owner))), alignments,
players.get(owner_if_pet(entity_b)), players,
uid_allocator,
owner_entity,
entity_b,
); );
let attack_options = combat::AttackOptions { let attack_options = combat::AttackOptions {
// cool guyz maybe don't look at explosions // cool guyz maybe don't look at explosions
@ -994,27 +991,22 @@ pub fn handle_explosion(server: &Server, pos: Vec3<f32>, explosion: Explosion, o
1.0 - distance_squared / explosion.radius.powi(2) 1.0 - distance_squared / explosion.radius.powi(2)
}; };
// Player check only accounts for PvP/PvE flag. // Player check only accounts for PvP/PvE flag, but bombs
// are intented to do friendly fire.
// //
// But bombs are intented to do // What exactly is friendly fire is subject to discussion.
// friendly fire.
//
// What exactly friendly fire is subject to discussion.
// As we probably want to minimize possibility of being dick // As we probably want to minimize possibility of being dick
// even to your group members, the only exception is when // even to your group members, the only exception is when
// you want to harm yourself. // you want to harm yourself.
// //
// This can be changed later. // 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 = || { let may_harm = || {
combat::may_harm( combat::may_harm(
owner_entity.and_then(|owner| players.get(owner_if_pet(owner))), alignments,
players.get(owner_if_pet(entity_b)), players,
uid_allocator,
owner_entity,
entity_b,
) || owner_entity.map_or(true, |entity_a| entity_a == entity_b) ) || owner_entity.map_or(true, |entity_a| entity_a == entity_b)
}; };
if strength > 0.0 { if strength > 0.0 {