use crate::{
analyzers::{helpers, Analyzer, Outcome},
Encounter, EventKind, Log,
};
pub const DESMINA_BUFF_ID: u32 = 47414;
pub const DESMINA_MS_THRESHOLD: u64 = 11_000;
pub const DESMINA_DEATH_BUFF: u32 = 895;
#[derive(Debug, Clone, Copy)]
pub struct SoullessHorror<'log> {
log: &'log Log,
}
impl<'log> SoullessHorror<'log> {
pub fn new(log: &'log Log) -> Self {
SoullessHorror { log }
}
}
impl<'log> Analyzer for SoullessHorror<'log> {
fn log(&self) -> &Log {
self.log
}
fn is_cm(&self) -> bool {
let tbb = helpers::time_between_buffs(self.log, DESMINA_BUFF_ID);
tbb > 0 && tbb <= DESMINA_MS_THRESHOLD
}
fn outcome(&self) -> Option<Outcome> {
check_reward!(self.log);
Outcome::from_bool(self.log.events().iter().any(|event| {
if let EventKind::BuffApplication {
buff_id,
destination_agent_addr,
..
} = event.kind()
{
*buff_id == DESMINA_DEATH_BUFF && self.log.is_boss(*destination_agent_addr)
} else {
false
}
}))
}
}
#[derive(Debug, Clone, Copy)]
pub struct RiverOfSouls<'log> {
log: &'log Log,
}
impl<'log> RiverOfSouls<'log> {
pub fn new(log: &'log Log) -> Self {
RiverOfSouls { log }
}
}
impl<'log> Analyzer for RiverOfSouls<'log> {
fn log(&self) -> &'log Log {
self.log
}
fn is_cm(&self) -> bool {
false
}
fn outcome(&self) -> Option<Outcome> {
const TRASH_IDS: &[u16] = &[0x4d97, 0x4bc7, 0x4d75, 0x4c05, 0x4bc8, 0x4cec];
check_reward!(self.log);
let desmina = self
.log
.characters()
.find(|npc| npc.id() == Encounter::RiverOfSouls as u16)?;
let exit_combat = self
.log
.events()
.iter()
.find(|e| matches!(e.kind(), &EventKind::ExitCombat { agent_addr } if agent_addr == desmina.addr()));
if exit_combat.is_none() {
return Some(Outcome::Failure);
}
let trash_aware = self
.log
.characters()
.filter(|npc| TRASH_IDS.contains(&npc.id()))
.map(|npc| npc.last_aware())
.filter(|&i| i != u64::MAX)
.max()
.unwrap_or(0);
let desmina_despawn = self
.log()
.events()
.iter()
.find(|e| matches!(e.kind(), &EventKind::Despawn { agent_addr } if agent_addr == desmina.addr()));
Outcome::from_bool(
trash_aware != 0
&& desmina_despawn.is_none()
&& trash_aware + 500 <= desmina.last_aware()
&& some_player_alive(self.log),
)
}
}
fn some_player_alive(log: &Log) -> bool {
let deaths_and_dcs = log
.events()
.iter()
.filter_map(|e| match *e.kind() {
EventKind::Despawn { agent_addr } => Some(agent_addr),
EventKind::ChangeDead { agent_addr } => Some(agent_addr),
_ => None,
})
.filter(|&addr| {
log.agent_by_addr(addr)
.map(|a| a.kind().is_player())
.unwrap_or(false)
})
.count();
deaths_and_dcs < log.players().count()
}
pub const DHUUM_CM_HEALTH: u64 = 40_000_000;
#[derive(Debug, Clone, Copy)]
pub struct Dhuum<'log> {
log: &'log Log,
}
impl<'log> Dhuum<'log> {
pub fn new(log: &'log Log) -> Self {
Dhuum { log }
}
}
impl<'log> Analyzer for Dhuum<'log> {
fn log(&self) -> &Log {
self.log
}
fn is_cm(&self) -> bool {
helpers::boss_health(self.log)
.map(|h| h >= DHUUM_CM_HEALTH)
.unwrap_or(false)
}
fn outcome(&self) -> Option<Outcome> {
check_reward!(self.log);
Outcome::from_bool(helpers::boss_is_dead(self.log))
}
}