1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use arcdps::{strip_account_prefix, Agent, Profession, Specialization};
use std::cmp;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

/// Struct representing a player.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Player {
    /// Player id.
    pub id: usize,

    // Player instance id on map.
    #[cfg_attr(feature = "serde", serde(default))]
    pub instance_id: u16,

    /// Player character name.
    pub character: String,

    /// Player account name.
    pub account: String,

    /// Whether the player is the local player.
    #[cfg_attr(feature = "serde", serde(default))]
    pub is_self: bool,

    /// Profession (class) of the player character.
    #[cfg_attr(feature = "serde", serde(default))]
    pub profession: Profession,

    /// Current elite specialization the player has equipped.
    #[cfg_attr(feature = "serde", serde(default))]
    pub elite: Specialization,

    /// Current squad subgroup the player is in.
    #[cfg_attr(feature = "serde", serde(default))]
    pub subgroup: usize,

    /// Whether the player is currently in combat.
    #[cfg_attr(feature = "serde", serde(default))]
    pub combat: bool,
}

impl Player {
    /// Creates a new player.
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        id: usize,
        instance_id: u16,
        character: impl Into<String>,
        account: impl Into<String>,
        is_self: bool,
        profession: Profession,
        elite: Specialization,
        subgroup: usize,
    ) -> Self {
        Self {
            id,
            instance_id,
            character: character.into(),
            account: account.into(),
            is_self,
            profession,
            elite,
            subgroup,
            combat: false,
        }
    }

    /// Creates a new player from tracking change agents.
    pub fn from_tracking_change(src: &Agent, dst: &Agent) -> Option<Self> {
        debug_assert!(src.elite == 0 && src.prof != 0);

        let acc_name = dst.name()?;
        Some(Self::new(
            src.id,
            dst.id as u16,
            src.name()?,
            strip_account_prefix(acc_name),
            dst.is_self != 0,
            dst.prof.into(),
            dst.elite.into(),
            dst.team as usize,
        ))
    }

    /// Enters the player into combat.
    pub fn enter_combat(&mut self, new_subgroup: Option<usize>) {
        self.combat = true;
        if let Some(sub) = new_subgroup {
            self.subgroup = sub;
        }
    }

    /// Exits the player from combat.
    pub fn exit_combat(&mut self) {
        self.combat = false;
    }
}

impl PartialEq for Player {
    fn eq(&self, other: &Self) -> bool {
        self.id == other.id
    }
}

impl Eq for Player {}

impl cmp::PartialOrd for Player {
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        Some(self.id.cmp(&other.id))
    }
}

impl cmp::Ord for Player {
    fn cmp(&self, other: &Self) -> cmp::Ordering {
        self.id.cmp(&other.id)
    }
}