veloren_common/comp/
loot_owner.rs

1use crate::{
2    comp::{Alignment, Body, Group, Player},
3    uid::Uid,
4};
5use serde::{Deserialize, Serialize};
6use specs::{Component, DerefFlaggedStorage};
7use std::{
8    ops::Add,
9    time::{Duration, Instant},
10};
11
12#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
13pub struct LootOwner {
14    // TODO: Fix this if expiry is needed client-side, Instant is not serializable
15    #[serde(skip, default = "Instant::now")]
16    expiry: Instant,
17    owner: LootOwnerKind,
18    soft: bool,
19}
20
21/// Loot becomes free-for-all after the initial ownership period
22pub const ONWERSHIP_TIMEOUT_SLOW: u64 = 45;
23pub const ONWERSHIP_TIMEOUT_FAST: u64 = 10;
24
25impl LootOwner {
26    pub fn new(kind: LootOwnerKind, soft: bool, duration: u64) -> Self {
27        Self {
28            expiry: Instant::now().add(Duration::from_secs(duration)),
29            owner: kind,
30            soft,
31        }
32    }
33
34    pub fn uid(&self) -> Option<Uid> {
35        match &self.owner {
36            LootOwnerKind::Player(uid) => Some(*uid),
37            LootOwnerKind::Group(_) => None,
38        }
39    }
40
41    pub fn owner(&self) -> LootOwnerKind { self.owner }
42
43    pub fn time_until_expiration(&self) -> Duration { self.expiry - Instant::now() }
44
45    pub fn expired(&self) -> bool { self.expiry <= Instant::now() }
46
47    pub fn default_instant() -> Instant { Instant::now() }
48
49    /// This field stands as a wish for NPC's to not pick the loot up, they will
50    /// however be able to decide whether they want to follow your wishes or not
51    /// (players will be able to pick the item up)
52    pub fn is_soft(&self) -> bool { self.soft }
53
54    pub fn can_pickup(
55        &self,
56        uid: Uid,
57        group: Option<&Group>,
58        alignment: Option<&Alignment>,
59        body: Option<&Body>,
60        player: Option<&Player>,
61    ) -> bool {
62        let is_owned = matches!(alignment, Some(Alignment::Owned(_)));
63        let is_player = player.is_some();
64        let is_pet = is_owned && !is_player;
65
66        let owns_loot = match self.owner {
67            LootOwnerKind::Player(loot_uid) => loot_uid.0 == uid.0,
68            LootOwnerKind::Group(loot_group) => {
69                matches!(group, Some(group) if loot_group == *group)
70            },
71        };
72        let is_humanoid = matches!(body, Some(Body::Humanoid(_)));
73
74        // Pet's can't pick up owned loot
75        // Humanoids must own the loot
76        // Non-humanoids ignore loot ownership
77        !is_pet && (self.soft || owns_loot || !is_humanoid)
78    }
79}
80
81impl Component for LootOwner {
82    type Storage = DerefFlaggedStorage<Self, specs::DenseVecStorage<Self>>;
83}
84
85#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
86pub enum LootOwnerKind {
87    Player(Uid),
88    Group(Group),
89}