diff --git a/CHANGELOG.md b/CHANGELOG.md index a9b480a34b..22b36b3e49 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Location names are displayed in character selection dialog - You can no longer write messages to old groups after being kicked and not having updated your chat mode. - Location names are now also correct after editing and creating characters +- NPC's wont pick up recently dropped items (if not hostile towards you) ## [0.15.0] - 2023-07-01 diff --git a/common/src/comp/loot_owner.rs b/common/src/comp/loot_owner.rs index fdb0646927..6ce9311eac 100644 --- a/common/src/comp/loot_owner.rs +++ b/common/src/comp/loot_owner.rs @@ -15,16 +15,18 @@ pub struct LootOwner { #[serde(skip, default = "Instant::now")] expiry: Instant, owner: LootOwnerKind, + soft: bool, } // Loot becomes free-for-all after the initial ownership period const OWNERSHIP_SECS: u64 = 45; impl LootOwner { - pub fn new(kind: LootOwnerKind) -> Self { + pub fn new(kind: LootOwnerKind, soft: bool) -> Self { Self { expiry: Instant::now().add(Duration::from_secs(OWNERSHIP_SECS)), owner: kind, + soft, } } @@ -43,6 +45,11 @@ impl LootOwner { pub fn default_instant() -> Instant { Instant::now() } + /// This field stands as a wish for NPC's to not pick the loot up, they will + /// however be able to decide whether they want to follow your wishes or not + /// (players will be able to pick the item up) + pub fn is_soft(&self) -> bool { self.soft } + pub fn can_pickup( &self, uid: Uid, @@ -66,7 +73,7 @@ impl LootOwner { // Pet's can't pick up owned loot // Humanoids must own the loot // Non-humanoids ignore loot ownership - !is_pet && (owns_loot || !is_humanoid) + !is_pet && (self.soft || owns_loot || !is_humanoid) } } diff --git a/server/agent/src/action_nodes.rs b/server/agent/src/action_nodes.rs index 02a7ef818a..cc3a340c59 100644 --- a/server/agent/src/action_nodes.rs +++ b/server/agent/src/action_nodes.rs @@ -822,10 +822,10 @@ impl<'a> AgentData<'a> { }; let is_valid_target = |entity: EcsEntity| match read_data.bodies.get(entity) { Some(Body::ItemDrop(item)) => { - //If the agent is humanoid, it will pick up all kinds of item drops. If the + let is_humanoid = matches!(self.body, Some(Body::Humanoid(_))); + // If the agent is humanoid, it will pick up all kinds of item drops. If the // agent isn't humanoid, it will pick up only consumable item drops. - let wants_pickup = matches!(self.body, Some(Body::Humanoid(_))) - || matches!(item, item_drop::Body::Consumable); + let wants_pickup = is_humanoid || matches!(item, item_drop::Body::Consumable); // The agent will attempt to pickup the item if it wants to pick it up and // is allowed to @@ -834,13 +834,20 @@ impl<'a> AgentData<'a> { .loot_owners .get(entity) .map_or(true, |loot_owner| { - loot_owner.can_pickup( - *self.uid, - read_data.groups.get(entity), - self.alignment, - self.body, - None, - ) + !(is_humanoid + && loot_owner.is_soft() + // If we are hostile towards the owner, ignore their wish to not pick up the loot + && loot_owner + .uid() + .and_then(|uid| read_data.id_maps.uid_entity(uid)) + .map_or(true, |entity| !is_enemy(self, entity, read_data))) + && loot_owner.can_pickup( + *self.uid, + read_data.groups.get(entity), + self.alignment, + self.body, + None, + ) }); if attempt_pickup { diff --git a/server/src/events/entity_manipulation.rs b/server/src/events/entity_manipulation.rs index 17e2b10012..0f1e0ef1fa 100644 --- a/server/src/events/entity_manipulation.rs +++ b/server/src/events/entity_manipulation.rs @@ -482,7 +482,7 @@ pub fn handle_destroy(server: &mut Server, entity: EcsEntity, last_change: Healt item, if let Some(loot_owner) = loot_owner { debug!("Assigned UID {loot_owner:?} as the winner for the loot drop"); - Some(LootOwner::new(loot_owner)) + Some(LootOwner::new(loot_owner, false)) } else { None }, diff --git a/server/src/events/interaction.rs b/server/src/events/interaction.rs index af268697cb..dc0fb5dad1 100755 --- a/server/src/events/interaction.rs +++ b/server/src/events/interaction.rs @@ -356,7 +356,9 @@ pub fn handle_mine_block( } } for item in items { - let loot_owner = maybe_uid.map(LootOwnerKind::Player).map(LootOwner::new); + let loot_owner = maybe_uid + .map(LootOwnerKind::Player) + .map(|owner| LootOwner::new(owner, false)); state.create_item_drop( Pos(pos.map(|e| e as f32) + Vec3::new(0.5, 0.5, 0.0)), comp::Vel(Vec3::zero()), diff --git a/server/src/events/inventory_manip.rs b/server/src/events/inventory_manip.rs index 2fd01aa36b..cb5cc4bcca 100644 --- a/server/src/events/inventory_manip.rs +++ b/server/src/events/inventory_manip.rs @@ -9,8 +9,9 @@ use common::{ self, group::members, item::{self, flatten_counted_items, tool::AbilityMap, MaterialStatManifest}, + loot_owner::LootOwnerKind, slot::{self, Slot}, - InventoryUpdate, + InventoryUpdate, LootOwner, }, consts::MAX_PICKUP_RANGE, mounting::VolumePos, @@ -387,7 +388,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv ), comp::Vel(Vec3::zero()), item, - None, + Some(LootOwner::new(LootOwnerKind::Player(uid), false)), ); } }, @@ -959,7 +960,7 @@ pub fn handle_inventory(server: &mut Server, entity: EcsEntity, manip: comp::Inv comp::Pos(pos.0 + *ori.look_dir() + Vec3::unit_z()), comp::Vel(Vec3::zero()), item, - None, + Some(LootOwner::new(LootOwnerKind::Player(uid), true)), ); }