Skip to content

Commit

Permalink
Add option to display breakbar for others
Browse files Browse the repository at this point in the history
  • Loading branch information
Zerthox committed Feb 20, 2024
1 parent 4492b72 commit 32c7f18
Show file tree
Hide file tree
Showing 10 changed files with 176 additions and 75 deletions.
47 changes: 31 additions & 16 deletions src/combat/agent.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
use arcdps::evtc::{self, AgentKind};
use super::name_of;
use arc_util::colors::{CYAN, GREEN, RED, YELLOW};
use arcdps::{
evtc::{self, AgentKind},
exports::{Colors, CoreColor},
};

// TODO: show id settings?

Expand All @@ -8,19 +13,15 @@ pub struct Agent {
/// Kind of agent.
pub kind: AgentKind,

/// Whether the character is the local player.
pub is_self: bool,

/// Agent name.
pub name: String,
}

impl Agent {
/// Creates a new agent.
pub fn new(kind: AgentKind, is_self: bool, name: impl Into<String>) -> Self {
pub fn new(kind: AgentKind, name: impl Into<String>) -> Self {
Self {
kind,
is_self,
name: name.into(),
}
}
Expand All @@ -32,20 +33,34 @@ impl Agent {
_ => false,
}
}

/// Checks whether the agent is a player.
pub fn is_player(&self) -> bool {
matches!(self.kind, AgentKind::Player)
}

/// Returns friendly agent color.
pub fn friendly_color(&self, colors: &Colors) -> [f32; 4] {
if self.is_player() {
colors.core(CoreColor::LightTeal).unwrap_or(CYAN)
} else {
colors.core(CoreColor::LightGreen).unwrap_or(GREEN)
}
}

/// Returns enemy agent color.
pub fn enemy_color(&self, colors: &Colors, fight_target: Option<u32>) -> [f32; 4] {
if self.matches_species(fight_target) {
colors.core(CoreColor::LightRed).unwrap_or(RED)
} else {
colors.core(CoreColor::LightYellow).unwrap_or(YELLOW)
}
}
}

impl From<&evtc::Agent> for Agent {
fn from(agent: &evtc::Agent) -> Self {
let kind = agent.kind();
let name = match agent.name() {
Some(name) if !name.is_empty() => name.into(),
_ => match kind {
AgentKind::Player => format!("Player:{}", agent.id),
AgentKind::Npc(species) => format!("NPC:{species}",),
AgentKind::Gadget(species) => format!("Gadget:{species}"),
},
};
Self::new(kind, agent.is_self != 0, name)
Self::new(agent.kind(), name_of(agent))
}
}

Expand Down
17 changes: 16 additions & 1 deletion src/combat/breakbar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,32 @@ pub struct BreakbarHit {
/// Due to decimals this is `10 * damage` compared to regular defiance damage units.
pub damage: i32,

/// Agent causing the hit.
pub attacker: String,

/// Whether the attacker is our character.
pub is_own: bool,

/// Target hit.
pub target: Agent,
}

impl BreakbarHit {
/// Creates a new breakbar hit.
pub fn new(time: i32, skill: Skill, damage: i32, target: Agent) -> Self {
pub fn new(
time: i32,
skill: Skill,
damage: i32,
attacker: impl Into<String>,
is_own: bool,
target: Agent,
) -> Self {
Self {
time,
skill,
damage,
attacker: attacker.into(),
is_own,
target,
}
}
Expand Down
6 changes: 0 additions & 6 deletions src/combat/buff.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use super::agent::Agent;
use arcdps::evtc::AgentKind;

pub use crate::data::Buff;

Expand Down Expand Up @@ -29,9 +28,4 @@ impl BuffApply {
target,
}
}

/// Checks whether the apply target was a player.
pub fn to_player(&self) -> bool {
matches!(self.target.kind, AgentKind::Player)
}
}
27 changes: 23 additions & 4 deletions src/combat/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,32 @@ pub mod agent;
pub mod breakbar;
pub mod buff;
pub mod cast;
pub mod player;
pub mod skill;
pub mod transfer;

use self::breakbar::BreakbarHit;
use self::buff::BuffApply;
use self::cast::Cast;
use self::transfer::TransferTracker;
use arcdps::evtc::{self, AgentKind};
use breakbar::BreakbarHit;
use buff::BuffApply;
use cast::Cast;
use transfer::TransferTracker;

/// Generates a name with the given parameters.
pub fn process_name(id: usize, kind: AgentKind, name: Option<&str>) -> String {
match name {
Some(name) if !name.is_empty() => name.into(),
_ => match kind {
AgentKind::Player => format!("Player:{}", id),
AgentKind::Npc(species) => format!("NPC:{species}",),
AgentKind::Gadget(species) => format!("Gadget:{species}"),
},
}
}

/// Generates a name for the EVTC agent.
pub fn name_of(agent: &evtc::Agent) -> String {
process_name(agent.id, agent.kind(), agent.name())
}

#[derive(Debug, Clone)]
pub struct CombatData {
Expand Down
22 changes: 22 additions & 0 deletions src/combat/player.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use crate::combat::process_name;
use arcdps::evtc;

/// Information about a player.
#[derive(Debug, Clone)]
pub struct Player {
pub id: usize,
pub instance_id: u16,
pub name: String,
}

impl Player {
/// Creates a new player from a tracking change.
pub fn from_tracking_change(src: &evtc::Agent, dst: &evtc::Agent) -> Self {
let kind = dst.kind();
Self {
id: src.id,
instance_id: dst.id as u16,
name: process_name(src.id, kind, src.name()),
}
}
}
70 changes: 51 additions & 19 deletions src/plugin/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use crate::combat::{
breakbar::BreakbarHit,
buff::{Buff, BuffApply},
cast::{Cast, CastState},
player::Player,
skill::Skill,
transfer::{Apply, Condition, Remove},
};
Expand Down Expand Up @@ -78,30 +79,44 @@ impl Plugin {

EventCategory::Strike => {
let mut plugin = Self::lock();
let is_minion = plugin.is_own_minion(event);
if src_self || is_minion {
if let (Some(dst), Some(time)) =
(dst, plugin.history.relative_time(event.time))
{
plugin.strike(event, is_minion, skill_name, dst, time)
}
if let (Some(dst), Some(time)) =
(dst, plugin.history.relative_time(event.time))
{
plugin.strike(event, skill_name, src, dst, time)
}
}

_ => {}
}
} else if let Some(dst) = dst {
// check for tracking addition
if dst.is_self != 0 && src.elite == 0 && src.prof != 0 {
if src.elite == 0 && src.prof != 0 {
let mut plugin = Self::lock();
let inst_id = dst.id as u16;
plugin.self_instance_id = Some(inst_id);
debug!("own instance id changed to {}", inst_id);
if src.prof != 0 {
// player added
let player = Player::from_tracking_change(src, dst);
if dst.is_self != 0 {
plugin.self_instance_id = Some(player.instance_id);
debug!("own instance id changed to {}", player.instance_id);
}
plugin.players.push(player);
} else if let Some(pos) =
plugin.players.iter().position(|player| player.id == src.id)
{
// player tracked & removed
plugin.players.swap_remove(pos);
}
}
}
}
}

fn get_master(&self, event: &Event) -> Option<&crate::combat::player::Player> {
self.players
.iter()
.find(|player| player.instance_id == event.src_master_instance_id)
}

fn is_own_minion(&self, event: &Event) -> bool {
match self.self_instance_id {
Some(id) => event.src_master_instance_id == id,
Expand Down Expand Up @@ -202,17 +217,27 @@ impl Plugin {
fn strike(
&mut self,
event: &Event,
is_minion: bool,
skill_name: Option<&str>,
attacker: &Agent,
target: &Agent,
time: i32,
) {
let skill = Skill::new(event.skill_id, skill_name);
let is_minion = self.is_own_minion(event);
let is_own = attacker.is_self != 0 || is_minion;
match event.get_strike() {
Strike::Normal | Strike::Crit | Strike::Glance => {
self.damage_hit(is_minion, skill, target, time)
if is_own {
self.damage_hit(is_minion, skill, target, time)
}
}
Strike::Breakbar => {
let attacker_name = self
.get_master(event)
.map(|player| player.name.clone())
.unwrap_or_else(|| crate::combat::name_of(attacker));
self.breakbar_hit(skill, attacker_name, is_own, target, event.value, time)
}
Strike::Breakbar => self.breakbar_hit(skill, target, event.value, time),
_ => {}
}
}
Expand Down Expand Up @@ -240,12 +265,19 @@ impl Plugin {
}
}

fn breakbar_hit(&mut self, skill: Skill, target: &Agent, damage: i32, time: i32) {
// TODO: support optional display for entire group?
// TODO: display minion indicator?
fn breakbar_hit(
&mut self,
skill: Skill,
attacker: String,
is_own: bool,
target: &Agent,
damage: i32,
time: i32,
) {
// TODO: minion indicator?
if let Some(fight) = self.history.latest_fight_mut() {
debug!("breakbar {damage} {skill:?} {target:?}");
let hit = BreakbarHit::new(time, skill, damage, target.into());
debug!("breakbar {damage} {skill:?} from {attacker} to {target:?}");
let hit = BreakbarHit::new(time, skill, damage, attacker, is_own, target.into());
fight.data.breakbar.push(hit);
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/plugin/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ pub mod event;
pub mod ui;

use crate::{
combat::CombatData,
combat::{player::Player, CombatData},
data::{LoadError, SkillData},
history::History,
ui::{
Expand Down Expand Up @@ -42,6 +42,7 @@ pub struct Plugin {
data_state: Result<usize, LoadError>,

self_instance_id: Option<u16>,
players: Vec<Player>,
history: History<CombatData>,

multi_view: Window<MultiView>,
Expand Down Expand Up @@ -71,6 +72,7 @@ impl Plugin {
data_state: Err(LoadError::NotFound),

self_instance_id: None,
players: Vec::new(),
history: History::new(10, 5000, true),

multi_view: Window::with_default("Buddy Multi", options.clone()),
Expand Down
Loading

0 comments on commit 32c7f18

Please sign in to comment.