aboutsummaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs739
1 files changed, 8 insertions, 731 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 5ba6340..bb71c77 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -88,15 +88,14 @@
//! While there are legitimate use cases for writing/modification support, they are currently not
//! implemented (but might be in a future version).
-use std::convert::TryFrom;
-use std::marker::PhantomData;
-
-use getset::{CopyGetters, Getters};
use num_traits::FromPrimitive;
use thiserror::Error;
pub mod raw;
+mod agent;
+pub use agent::{Agent, AgentKind, Character, Gadget, Player};
+
pub mod event;
pub use event::{Event, EventKind};
@@ -137,728 +136,6 @@ pub enum EvtcError {
Utf8Error(#[from] std::str::Utf8Error),
}
-/// Player-specific agent data.
-///
-/// Player agents are characters controlled by a player and as such, they contain data about the
-/// account and character used (name, profession), as well as the squad composition.
-///
-/// Note that a `Player` is only the player character itself. Any additional entities that are
-/// spawned by the player (clones, illusions, banners, ...) are either a [`Character`][Character]
-/// or a [`Gadget`][Gadget].
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, Hash, PartialEq, Eq, CopyGetters)]
-pub struct Player {
- /// The player's profession.
- #[get_copy = "pub"]
- profession: Profession,
-
- /// The player's elite specialization, if any is equipped.
- #[get_copy = "pub"]
- elite: Option<EliteSpec>,
-
- character_name: String,
-
- account_name: String,
-
- /// The subgroup the player was in.
- #[get_copy = "pub"]
- subgroup: u8,
-}
-
-impl Player {
- /// The player's character name.
- pub fn character_name(&self) -> &str {
- &self.character_name
- }
-
- /// The player's account name.
- ///
- /// This includes the leading colon and the 4-digit denominator.
- pub fn account_name(&self) -> &str {
- &self.account_name
- }
-}
-
-/// Gadget-specific agent data.
-///
-/// Gadgets are entities that are spawned by certain skills. They are mostly inanimate objects that
-/// only exist to achieve a certain skill effect.
-///
-/// Examples of this include the [banners](https://wiki.guildwars2.com/wiki/Banner) spawned by
-/// Warriors, but also skill effects like the roots created by
-/// [Entangle](https://wiki.guildwars2.com/wiki/Entangle) or the other objects in the arena.
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, Hash, PartialEq, Eq, CopyGetters)]
-pub struct Gadget {
- /// The id of the gadget.
- ///
- /// Note that gadgets do not have true ids and the id is generated "through a combination of
- /// gadget parameters".
- #[get_copy = "pub"]
- id: u16,
- name: String,
-}
-
-impl Gadget {
- /// The name of the gadget.
- pub fn name(&self) -> &str {
- &self.name
- }
-}
-
-/// Character-specific agent data.
-///
-/// Characters are NPCs such as the bosses themselves, additional mobs that they spawn, but also
-/// friendly characters like Mesmer's clones and illusions, Necromancer minions, and so on.
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, Hash, PartialEq, Eq, CopyGetters)]
-pub struct Character {
- /// The id of the character.
- #[get_copy = "pub"]
- id: u16,
- name: String,
-}
-
-impl Character {
- /// The name of the character.
- pub fn name(&self) -> &str {
- &self.name
- }
-}
-
-/// The type of an agent.
-///
-/// arcdps differentiates between three types of agents: [`Player`][Player],
-/// [`Character`][Character] and [`Gadget`][Gadget]. This enum unifies handling between them by
-/// allowing you to pattern match or use one of the accessor methods.
-///
-/// The main way to obtain a `AgentKind` is by using the [`.kind()`][Agent::kind] method on an
-/// [`Agent`][Agent]. In cases where you already have a [`raw::Agent`][raw::Agent] available, you
-/// can also use the [`TryFrom`][TryFrom]/[`TryInto`][std::convert::TryInto] traits to convert a
-/// `raw::Agent` or `&raw::Agent` to a `AgentKind`:
-///
-/// ```no_run
-/// # use evtclib::{AgentKind, raw};
-/// use std::convert::TryInto;
-/// // Get a raw::Agent from somewhere
-/// let raw_agent: raw::Agent = panic!();
-/// // Convert it
-/// let agent: AgentKind = raw_agent.try_into().unwrap();
-/// ```
-#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-#[derive(Debug, Clone, Hash, PartialEq, Eq)]
-pub enum AgentKind {
- /// The agent is a player.
- ///
- /// The player-specific data is in the included [`Player`][Player] struct.
- Player(Player),
- /// The agent is a gadget.
- ///
- /// The gadget-specific data is in the included [`Gadget`][Gadget] struct.
- Gadget(Gadget),
- /// The agent is a character.
- ///
- /// The character-specific data is in the included [`Character`][Character] struct.
- Character(Character),
-}
-
-impl AgentKind {
- fn from_raw_character(raw_agent: &raw::Agent) -> Result<Character, EvtcError> {
- assert!(raw_agent.is_character());
- let name = raw::cstr_up_to_nul(&raw_agent.name).ok_or(EvtcError::InvalidData)?;
- Ok(Character {
- id: raw_agent.prof as u16,
- name: name.to_str()?.to_owned(),
- })
- }
-
- fn from_raw_gadget(raw_agent: &raw::Agent) -> Result<Gadget, EvtcError> {
- assert!(raw_agent.is_gadget());
- let name = raw::cstr_up_to_nul(&raw_agent.name).ok_or(EvtcError::InvalidData)?;
- Ok(Gadget {
- id: raw_agent.prof as u16,
- name: name.to_str()?.to_owned(),
- })
- }
-
- fn from_raw_player(raw_agent: &raw::Agent) -> Result<Player, EvtcError> {
- assert!(raw_agent.is_player());
- let character_name = raw::cstr_up_to_nul(&raw_agent.name)
- .ok_or(EvtcError::InvalidData)?
- .to_str()?;
- let account_name = raw::cstr_up_to_nul(&raw_agent.name[character_name.len() + 1..])
- .ok_or(EvtcError::InvalidData)?
- .to_str()?;
- let subgroup = raw_agent.name[character_name.len() + account_name.len() + 2] - b'0';
- let elite = if raw_agent.is_elite == 0 {
- None
- } else {
- Some(
- EliteSpec::from_u32(raw_agent.is_elite)
- .ok_or(EvtcError::InvalidEliteSpec(raw_agent.is_elite))?,
- )
- };
- Ok(Player {
- profession: Profession::from_u32(raw_agent.prof)
- .ok_or(EvtcError::InvalidProfession(raw_agent.prof))?,
- elite,
- character_name: character_name.to_owned(),
- account_name: account_name.to_owned(),
- subgroup,
- })
- }
-
- /// Accesses the inner [`Player`][Player] struct, if available.
- pub fn as_player(&self) -> Option<&Player> {
- if let AgentKind::Player(ref player) = *self {
- Some(player)
- } else {
- None
- }
- }
-
- /// Determines whether this `AgentKind` contains a player.
- pub fn is_player(&self) -> bool {
- self.as_player().is_some()
- }
-
- /// Accesses the inner [`Gadget`][Gadget] struct, if available.
- pub fn as_gadget(&self) -> Option<&Gadget> {
- if let AgentKind::Gadget(ref gadget) = *self {
- Some(gadget)
- } else {
- None
- }
- }
-
- /// Determines whether this `AgentKind` contains a gadget.
- pub fn is_gadget(&self) -> bool {
- self.as_gadget().is_some()
- }
-
- /// Accesses the inner [`Character`][Character] struct, if available.
- pub fn as_character(&self) -> Option<&Character> {
- if let AgentKind::Character(ref character) = *self {
- Some(character)
- } else {
- None
- }
- }
-
- /// Determines whether this `AgentKind` contains a character.
- pub fn is_character(&self) -> bool {
- self.as_character().is_some()
- }
-}
-
-impl TryFrom<raw::Agent> for AgentKind {
- type Error = EvtcError;
- /// Convenience method to avoid manual borrowing.
- ///
- /// Note that this conversion will consume the agent, so if you plan on re-using it, use the
- /// `TryFrom<&raw::Agent>` implementation that works with a reference.
- fn try_from(raw_agent: raw::Agent) -> Result<Self, Self::Error> {
- Self::try_from(&raw_agent)
- }
-}
-
-impl TryFrom<&raw::Agent> for AgentKind {
- type Error = EvtcError;
-
- /// Extract the correct `AgentKind` from the given [raw agent][raw::Agent].
- ///
- /// This automatically discerns between player, gadget and characters.
- ///
- /// Note that in most cases, you probably want to use `Agent::try_from` or even
- /// [`process`][process] instead of this function.
- fn try_from(raw_agent: &raw::Agent) -> Result<Self, Self::Error> {
- if raw_agent.is_character() {
- Ok(AgentKind::Character(AgentKind::from_raw_character(
- raw_agent,
- )?))
- } else if raw_agent.is_gadget() {
- Ok(AgentKind::Gadget(AgentKind::from_raw_gadget(raw_agent)?))
- } else if raw_agent.is_player() {
- Ok(AgentKind::Player(AgentKind::from_raw_player(raw_agent)?))
- } else {
- Err(EvtcError::InvalidData)
- }
- }
-}
-
-/// An agent.
-///
-/// Agents in arcdps are very versatile, as a lot of things end up being an "agent". This includes:
-/// * Players
-/// * Bosses
-/// * Any additional mobs that spawn
-/// * Mesmer illusions
-/// * Ranger spirits, pets
-/// * Guardian spirit weapons
-/// * ...
-///
-/// Generally, you can divide them into three kinds ([`AgentKind`][AgentKind]):
-/// * [`Player`][Player]: All players themselves.
-/// * [`Character`][Character]: Non-player mobs, including most bosses, "adds" and player-generated
-/// characters.
-/// * [`Gadget`][Gadget]: Some additional gadgets, such as ley rifts, continuum split, ...
-///
-/// All of these agents share some common fields, which are the ones accessible in `Agent<Kind>`.
-/// The kind can be retrieved using [`.kind()`][Agent::kind], which can be matched on.
-///
-/// # Obtaining an agent
-///
-/// The normal way to obtain the agents is to use the [`.agents()`](Log::agents) method on a
-/// [`Log`][Log], or one of the other accessor methods (like [`.players()`][Log::players] or
-/// [`.agent_by_addr()`][Log::agent_by_addr]).
-///
-/// In the cases where you already have a [`raw::Agent`][raw::Agent] available, you can also
-/// convert it to an [`Agent`][Agent] by using the standard
-/// [`TryFrom`][TryFrom]/[`TryInto`][std::convert::TryInto] traits:
-///
-/// ```no_run
-/// # use evtclib::{Agent, raw};
-/// use std::convert::TryInto;
-/// let raw_agent: raw::Agent = panic!();
-/// let agent: Agent = raw_agent.try_into().unwrap();
-/// ```
-///
-/// Note that you can convert references as well, so if you plan on re-using the raw agent
-/// afterwards, you should opt for `Agent::try_from(&raw_agent)` instead.
-///
-/// # The `Kind` parameter
-///
-/// The type parameter is not actually used and only exists at the type level. It can be used to
-/// tag `Agent`s containing a known kind. For example, `Agent<Player>` implements
-/// [`.player()`][Agent::player], which returns a `&Player` directly (instead of a
-/// `Option<&Player>`). This works because such tagged `Agent`s can only be constructed (safely)
-/// using [`.as_player()`][Agent::as_player], [`.as_gadget()`][Agent::as_gadget] or
-/// [`.as_character()`][Agent::as_character]. This is useful since functions like
-/// [`Log::players`][Log::players], which already filter only players, don't require the consumer
-/// to do another check/pattern match for the right agent kind.
-///
-/// The unit type `()` is used to tag `Agent`s which contain an undetermined type, and it is the
-/// default if you write `Agent` without any parameters.
-///
-/// The downside is that methods which work on `Agent`s theoretically should be generic over
-/// `Kind`. An escape hatch is the method [`.erase()`][Agent::erase], which erases the kind
-/// information and produces the default `Agent<()>`. Functions/methods that only take `Agent<()>`
-/// can therefore be used by any other agent as well.
-#[cfg_attr(feature = "serde", derive(serde::Serialize))]
-#[derive(Debug, Clone, Hash, PartialEq, Eq, Getters, CopyGetters)]
-// For the reasoning of #[repr(C)] see Agent::transmute.
-#[repr(C)]
-pub struct Agent<Kind = ()> {
- /// The address of this agent.
- ///
- /// This is not actually the address of the in-memory Rust object, but rather a serialization
- /// detail of arcdps. You should consider this as an opaque number and only compare it to other
- /// agent addresses.
- #[get_copy = "pub"]
- addr: u64,
-
- /// The kind of this agent.
- #[get = "pub"]
- kind: AgentKind,
-
- /// The toughness of this agent.
- ///
- /// This is not an absolute number, but a relative indicator that indicates this agent's
- /// toughness relative to the other people in the squad.
- ///
- /// 0 means lowest toughness, 10 means highest toughness.
- #[get_copy = "pub"]
- toughness: i16,
-
- /// The concentration of this agent.
- ///
- /// This is not an absolute number, but a relative indicator that indicates this agent's
- /// concentration relative to the other people in the squad.
- ///
- /// 0 means lowest concentration, 10 means highest concentration.
- #[get_copy = "pub"]
- concentration: i16,
-
- /// The healing power of this agent.
- ///
- /// This is not an absolute number, but a relative indicator that indicates this agent's
- /// healing power relative to the other people in the squad.
- ///
- /// 0 means lowest healing power, 10 means highest healing power.
- #[get_copy = "pub"]
- healing: i16,
-
- /// The condition damage of this agent.
- ///
- /// This is not an absolute number, but a relative indicator that indicates this agent's
- /// condition damage relative to the other people in the squad.
- ///
- /// 0 means lowest condition damage, 10 means highest condition damage.
- #[get_copy = "pub"]
- condition: i16,
-
- /// The instance ID of this agent.
- #[get_copy = "pub"]
- instance_id: u16,
-
- /// The timestamp of the first event entry with this agent.
- #[get_copy = "pub"]
- first_aware: u64,
-
- /// The timestamp of the last event entry with this agent.
- #[get_copy = "pub"]
- last_aware: u64,
-
- /// The master agent's address.
- #[get_copy = "pub"]
- master_agent: Option<u64>,
-
- #[cfg_attr(feature = "serde", serde(skip_serializing))]
- phantom_data: PhantomData<Kind>,
-}
-
-// We could derive this, however that would derive Deserialize generically for Agent<T>, where T is
-// deserializable. In particular, this would mean that you could deserialize Agent<Character>,
-// Agent<Player> and Agent<Gadget> directly, which would not be too bad - the problem is that serde
-// has no way of knowing if the serialized agent actually is a character/player/gadget agent, as
-// that information only exists on the type level. This meant that you could "coerce" agents into
-// the wrong type, safely, using a serialization followed by a deserialization.
-// Now this doesn't actually lead to memory unsafety or other bad behaviour, but it could mean that
-// the program would panic if you tried to access a non-existing field, e.g. by calling
-// Agent<Character>::id() on a non-character agent.
-// In order to prevent this, we manually implement Deserialize only for Agent<()>, so that the
-// usual conversion functions with the proper checks have to be used.
-#[cfg(feature = "serde")]
-impl<'de> serde::Deserialize<'de> for Agent {
- fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
- use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor};
- use std::fmt;
-
- #[derive(serde::Deserialize)]
- #[serde(field_identifier, rename_all = "snake_case")]
- enum Field {
- Addr,
- Kind,
- Toughness,
- Concentration,
- Healing,
- Condition,
- InstanceId,
- FirstAware,
- LastAware,
- MasterAgent,
- };
-
- struct AgentVisitor;
-
- impl<'de> Visitor<'de> for AgentVisitor {
- type Value = Agent;
-
- fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- formatter.write_str("struct Agent")
- }
-
- fn visit_map<V: MapAccess<'de>>(self, mut map: V) -> Result<Agent, V::Error> {
- let mut addr = None;
- let mut kind = None;
- let mut toughness = None;
- let mut concentration = None;
- let mut healing = None;
- let mut condition = None;
- let mut instance_id = None;
- let mut first_aware = None;
- let mut last_aware = None;
- let mut master_agent = None;
-
- while let Some(key) = map.next_key()? {
- match key {
- Field::Addr => {
- if addr.is_some() {
- return Err(de::Error::duplicate_field("addr"));
- }
- addr = Some(map.next_value()?);
- }
- Field::Kind => {
- if kind.is_some() {
- return Err(de::Error::duplicate_field("kind"));
- }
- kind = Some(map.next_value()?);
- }
- Field::Toughness => {
- if toughness.is_some() {
- return Err(de::Error::duplicate_field("toughness"));
- }
- toughness = Some(map.next_value()?);
- }
- Field::Concentration => {
- if concentration.is_some() {
- return Err(de::Error::duplicate_field("concentration"));
- }
- concentration = Some(map.next_value()?);
- }
- Field::Healing => {
- if healing.is_some() {
- return Err(de::Error::duplicate_field("healing"));
- }
- healing = Some(map.next_value()?);
- }
- Field::Condition => {
- if condition.is_some() {
- return Err(de::Error::duplicate_field("condition"));
- }
- condition = Some(map.next_value()?);
- }
- Field::InstanceId=> {
- if instance_id.is_some() {
- return Err(de::Error::duplicate_field("instance_id"));
- }
- instance_id = Some(map.next_value()?);
- }
- Field::FirstAware=> {
- if first_aware.is_some() {
- return Err(de::Error::duplicate_field("first_aware"));
- }
- first_aware = Some(map.next_value()?);
- }
- Field::LastAware => {
- if last_aware.is_some() {
- return Err(de::Error::duplicate_field("last_aware"));
- }
- last_aware = Some(map.next_value()?);
- }
- Field::MasterAgent => {
- if master_agent.is_some() {
- return Err(de::Error::duplicate_field("master_agent"));
- }
- master_agent = Some(map.next_value()?);
- }
- }
- }
-
- Ok(Agent {
- addr: addr.ok_or_else(|| de::Error::missing_field("addr"))?,
- kind: kind.ok_or_else(|| de::Error::missing_field("kind"))?,
- toughness: toughness.ok_or_else(|| de::Error::missing_field("toughness"))?,
- concentration: concentration
- .ok_or_else(|| de::Error::missing_field("concentration"))?,
- healing: healing.ok_or_else(|| de::Error::missing_field("healing"))?,
- condition: condition.ok_or_else(|| de::Error::missing_field("condition"))?,
- instance_id: instance_id
- .ok_or_else(|| de::Error::missing_field("instance_id"))?,
- first_aware: first_aware
- .ok_or_else(|| de::Error::missing_field("first_aware"))?,
- last_aware: last_aware.ok_or_else(|| de::Error::missing_field("last_aware"))?,
- master_agent: master_agent
- .ok_or_else(|| de::Error::missing_field("master_agent"))?,
- phantom_data: PhantomData,
- })
- }
- };
-
- const FIELDS: &'static [&'static str] = &[
- "addr",
- "kind",
- "toughness",
- "concentration",
- "healing",
- "condition",
- "instance_id",
- "first_aware",
- "last_aware",
- "master_agent",
- ];
- deserializer.deserialize_struct("Agent", FIELDS, AgentVisitor)
- }
-}
-
-impl TryFrom<&raw::Agent> for Agent {
- type Error = EvtcError;
-
- /// Parse a raw agent.
- fn try_from(raw_agent: &raw::Agent) -> Result<Self, Self::Error> {
- let kind = AgentKind::try_from(raw_agent)?;
- Ok(Agent {
- addr: raw_agent.addr,
- kind,
- toughness: raw_agent.toughness,
- concentration: raw_agent.concentration,
- healing: raw_agent.healing,
- condition: raw_agent.condition,
- instance_id: 0,
- first_aware: 0,
- last_aware: u64::max_value(),
- master_agent: None,
- phantom_data: PhantomData,
- })
- }
-}
-
-impl TryFrom<raw::Agent> for Agent {
- type Error = EvtcError;
-
- /// Convenience method to avoid manual borrowing.
- ///
- /// Note that this conversion will consume the agent, so if you plan on re-using it, use the
- /// `TryFrom<&raw::Agent>` implementation that works with a reference.
- fn try_from(raw_agent: raw::Agent) -> Result<Self, Self::Error> {
- Agent::try_from(&raw_agent)
- }
-}
-
-impl<Kind> Agent<Kind> {
- /// Unconditionally change the tagged type.
- #[inline]
- fn transmute<T>(&self) -> &Agent<T> {
- // Beware, unsafe code ahead!
- //
- // What are we doing here?
- // In Agent<T>, T is a marker type that only exists at the type level. There is no actual
- // value of type T being held, instead, we use PhantomData under the hood. This is so we
- // can implement special methods on Agent<Player>, Agent<Gadget> and Agent<Character>,
- // which allows us in some cases to avoid the "second check" (e.g. Log::players() can
- // return Agent<Player>, as the function already makes sure all returned agents are
- // players). This makes the interface more ergonomical, as we can prove to the type checker
- // at compile time that a given Agent has a certain AgentKind.
- //
- // Why is this safe?
- // PhantomData<T> (which is what Agent<T> boils down to) is a zero-sized type, which means
- // it does not actually change the layout of the struct. There is some discussion in [1],
- // which suggests that this is true for #[repr(C)] structs (which Agent is). We can
- // therefore safely transmute from Agent<U> to Agent<T>, for any U and T.
- //
- // Can this lead to unsafety?
- // No, the actual data access is still done through safe rust and a if-let. In the worst
- // case it can lead to an unexpected panic, but the "guarantee" made by T is rather weak in
- // that regard.
- //
- // What are the alternatives?
- // None, as far as I'm aware. Going from Agent<U> to Agent<T> is possible in safe Rust by
- // destructuring the struct, or alternatively by [2] (if it would be implemented). However,
- // when dealing with references, there seems to be no way to safely go from Agent<U> to
- // Agent<T>, even if they share the same layout.
- //
- // [1]: https://www.reddit.com/r/rust/comments/avrbvc/is_it_safe_to_transmute_foox_to_fooy_if_the/
- // [2]: https://github.com/rust-lang/rfcs/pull/2528
- unsafe { &*(self as *const Agent<Kind> as *const Agent<T>) }
- }
-
- /// Erase any extra information about the contained agent kind.
- #[inline]
- pub fn erase(&self) -> &Agent {
- self.transmute()
- }
-
- /// Try to convert this `Agent` to an `Agent` representing a `Player`.
- #[inline]
- pub fn as_player(&self) -> Option<&Agent<Player>> {
- if self.kind.is_player() {
- Some(self.transmute())
- } else {
- None
- }
- }
-
- /// Try to convert this `Agent` to an `Agent` representing a `Gadget`.
- #[inline]
- pub fn as_gadget(&self) -> Option<&Agent<Gadget>> {
- if self.kind.is_gadget() {
- Some(self.transmute())
- } else {
- None
- }
- }
-
- /// Try to convert this `Agent` to an `Agent` representing a `Character`.
- #[inline]
- pub fn as_character(&self) -> Option<&Agent<Character>> {
- if self.kind.is_character() {
- Some(self.transmute())
- } else {
- None
- }
- }
-}
-
-impl Agent<Player> {
- /// Directly access the underlying player data.
- #[inline]
- pub fn player(&self) -> &Player {
- self.kind.as_player().expect("Agent<Player> had no player!")
- }
-
- /// Shorthand to get the player's account name.
- #[inline]
- pub fn account_name(&self) -> &str {
- self.player().account_name()
- }
-
- /// Shorthand to get the player's character name.
- #[inline]
- pub fn character_name(&self) -> &str {
- self.player().character_name()
- }
-
- /// Shorthand to get the player's elite specialization.
- #[inline]
- pub fn elite(&self) -> Option<EliteSpec> {
- self.player().elite()
- }
-
- /// Shorthand to get the player's profession.
- #[inline]
- pub fn profession(&self) -> Profession {
- self.player().profession()
- }
-
- /// Shorthand to get the player's subgroup.
- #[inline]
- pub fn subgroup(&self) -> u8 {
- self.player().subgroup()
- }
-}
-
-impl Agent<Gadget> {
- /// Directly access the underlying gadget data.
- #[inline]
- pub fn gadget(&self) -> &Gadget {
- self.kind.as_gadget().expect("Agent<Gadget> had no gadget!")
- }
-
- /// Shorthand to get the gadget's id.
- #[inline]
- pub fn id(&self) -> u16 {
- self.gadget().id()
- }
-
- /// Shorthand to get the gadget's name.
- #[inline]
- pub fn name(&self) -> &str {
- self.gadget().name()
- }
-}
-
-impl Agent<Character> {
- /// Directly access the underlying character data.
- #[inline]
- pub fn character(&self) -> &Character {
- self.kind
- .as_character()
- .expect("Agent<Character> had no character!")
- }
-
- /// Shorthand to get the character's id.
- #[inline]
- pub fn id(&self) -> u16 {
- self.character().id()
- }
-
- /// Shorthand to get the character's name.
- #[inline]
- pub fn name(&self) -> &str {
- self.character().name()
- }
-}
-
/// A fully processed log file.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone)]
@@ -877,12 +154,12 @@ impl Log {
/// Return an agent based on its address.
pub fn agent_by_addr(&self, addr: u64) -> Option<&Agent> {
- self.agents.iter().find(|a| a.addr == addr)
+ self.agents.iter().find(|a| a.addr() == addr)
}
/// Return an agent based on the instance ID.
pub fn agent_by_instance_id(&self, instance_id: u16) -> Option<&Agent> {
- self.agents.iter().find(|a| a.instance_id == instance_id)
+ self.agents.iter().find(|a| a.instance_id() == instance_id)
}
/// Return the master agent of the given agent.
@@ -890,7 +167,7 @@ impl Log {
/// * `addr` - The address of the agent which to get the master for.
pub fn master_agent(&self, addr: u64) -> Option<&Agent> {
self.agent_by_addr(addr)
- .and_then(|a| a.master_agent)
+ .and_then(|a| a.master_agent())
.and_then(|a| self.agent_by_addr(a))
}
@@ -915,7 +192,7 @@ impl Log {
/// and Xera.
pub fn boss(&self) -> &Agent {
self.characters()
- .find(|c| c.character().id == self.boss_id)
+ .find(|c| c.character().id() == self.boss_id)
.map(Agent::erase)
.expect("Boss has no agent!")
}
@@ -930,7 +207,7 @@ impl Log {
.map(Encounter::bosses)
.unwrap_or(&[] as &[_]);
self.characters()
- .filter(|c| bosses.iter().any(|boss| *boss as u16 == c.character().id))
+ .filter(|c| bosses.iter().any(|boss| *boss as u16 == c.character().id()))
.map(Agent::erase)
.collect()
}