[Combat] Basic Combat Recording (#2090)

* Basic combat recording

* Update combat_record.h
This commit is contained in:
Chris Miles 2022-05-01 18:08:12 -05:00 committed by GitHub
parent 759f9bd007
commit c7dbdfae58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 138 additions and 14 deletions

View File

@ -134,6 +134,7 @@ EQEmuLogSys *EQEmuLogSys::LoadLogSettingsDefaults()
log_settings[Logs::HTTP].log_to_gmsay = static_cast<uint8>(Logs::General);
log_settings[Logs::ChecksumVerification].log_to_console = static_cast<uint8>(Logs::General);
log_settings[Logs::ChecksumVerification].log_to_gmsay = static_cast<uint8>(Logs::General);
log_settings[Logs::CombatRecord].log_to_gmsay = static_cast<uint8>(Logs::General);
/**
* RFC 5424

View File

@ -128,6 +128,7 @@ namespace Logs {
HTTP,
Saylink,
ChecksumVerification,
CombatRecord,
MaxCategoryID /* Don't Remove this */
};
@ -214,6 +215,7 @@ namespace Logs {
"HTTP",
"Saylink",
"ChecksumVerification",
"CombatRecord",
};
}

View File

@ -706,6 +706,16 @@
OutF(LogSys, Logs::Detail, Logs::ChecksumVerification, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogCombatRecord(message, ...) do {\
if (LogSys.log_settings[Logs::CombatRecord].is_category_enabled == 1)\
OutF(LogSys, Logs::General, Logs::CombatRecord, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define LogCombatRecordDetail(message, ...) do {\
if (LogSys.log_settings[Logs::CombatRecord].is_category_enabled == 1)\
OutF(LogSys, Logs::Detail, Logs::CombatRecord, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
} while (0)
#define Log(debug_level, log_category, message, ...) do {\
if (LogSys.log_settings[log_category].is_category_enabled == 1)\
LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\
@ -1138,6 +1148,12 @@
#define LogChecksumVerificationDetail(message, ...) do {\
} while (0)
#define LogCombatRecord(message, ...) do {\
} while (0)
#define LogCombatRecordDetail(message, ...) do {\
} while (0)
#define Log(debug_level, log_category, message, ...) do {\
} while (0)

View File

@ -19,6 +19,7 @@ SET(zone_sources
client_mods.cpp
client_packet.cpp
client_process.cpp
combat_record.cpp
command.cpp
corpse.cpp
data_bucket.cpp
@ -162,7 +163,7 @@ SET(zone_sources
zone_reload.cpp
zone_store.cpp
zoning.cpp
)
)
SET(zone_headers
aa.h
@ -179,6 +180,7 @@ SET(zone_headers
cheat_manager.h
client.h
client_packet.h
combat_record.h
command.h
common.h
corpse.h
@ -283,7 +285,7 @@ SET(zone_headers
zonedump.h
zone_reload.h
zone_store.h
)
)
ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers})

View File

@ -2724,6 +2724,7 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQ::skills::SkillTy
static_cast<int>(attack_skill)
);
parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, export_string, 0);
combat_record.Stop();
/* Zone controller process EVENT_DEATH_ZONE (Death events) */
if (RuleB(Zone, UseZoneController)) {

60
zone/combat_record.cpp Normal file
View File

@ -0,0 +1,60 @@
#include "combat_record.h"
#include "../common/eqemu_logsys.h"
#include "../common/string_util.h"
void CombatRecord::Start(std::string in_mob_name)
{
start_time = std::time(nullptr);
end_time = 0;
damage_received = 0;
heal_received = 0;
mob_name = in_mob_name;
}
void CombatRecord::Stop()
{
end_time = std::time(nullptr);
double time_in_combat = TimeInCombat();
LogCombatRecord(
"[Summary] Mob [{}] [Received] DPS [{:.0f}] Heal/s [{:.0f}] Duration [{}] ({}s)",
mob_name,
time_in_combat > 0 ? (damage_received / time_in_combat) : damage_received,
time_in_combat > 0 ? (heal_received / time_in_combat) : heal_received,
time_in_combat > 0 ? ConvertSecondsToTime(time_in_combat) : "",
time_in_combat
);
}
bool CombatRecord::InCombat()
{
return start_time > 0;
}
void CombatRecord::ProcessHPEvent(int hp, int current_hp)
{
// damage
if (hp < current_hp) {
damage_received = damage_received + abs(current_hp - hp);
}
// heal
if (hp > current_hp && current_hp > 0) {
heal_received = heal_received + abs(current_hp - hp);
}
LogCombatRecordDetail(
"damage_received [{}] heal_received [{}] current_hp [{}] hp [{}] calc [{}]",
damage_received,
heal_received,
current_hp,
hp,
abs(current_hp - hp)
);
}
double CombatRecord::TimeInCombat() const
{
return difftime(end_time, start_time);
}

23
zone/combat_record.h Normal file
View File

@ -0,0 +1,23 @@
#ifndef EQEMU_COMBAT_RECORD_H
#define EQEMU_COMBAT_RECORD_H
#include <ctime>
#include <string>
#include "../common/types.h"
class CombatRecord {
public:
void Start(std::string in_mob_name);
void Stop();
bool InCombat();
void ProcessHPEvent(int hp, int current_hp);
double TimeInCombat() const;
private:
std::string mob_name;
time_t start_time = 0;
time_t end_time = 0;
int64 damage_received = 0;
int64 heal_received = 0;
};
#endif //EQEMU_COMBAT_RECORD_H

View File

@ -28,6 +28,7 @@
#include "aa.h"
#include "../common/light_source.h"
#include "../common/emu_constants.h"
#include "combat_record.h"
#include <set>
#include <vector>
#include <memory>
@ -494,7 +495,7 @@ public:
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQ::skills::SkillType attack_skill) = 0;
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQ::skills::SkillType attack_skill,
bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) = 0;
inline virtual void SetHP(int32 hp) { if (hp >= max_hp) current_hp = max_hp; else current_hp = hp;}
virtual void SetHP(int32 hp);
bool ChangeHP(Mob* other, int32 amount, uint16 spell_id = 0, int8 buffslot = -1, bool iBuffTic = false);
inline void SetOOCRegen(int32 newoocregen) {ooc_regen = newoocregen;}
virtual void Heal();
@ -1643,6 +1644,8 @@ protected:
bool degenerating_effects; // true if we have a buff that needs to be recalced every tick
bool spawned_in_water;
CombatRecord combat_record{};
public:
bool GetWasSpawnedInWater() const;

View File

@ -1955,6 +1955,8 @@ void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help)
if (emoteid != 0) {
CastToNPC()->DoNPCEmote(ENTERCOMBAT, emoteid);
}
std::string mob_name = GetCleanName();
combat_record.Start(mob_name);
CastToNPC()->SetCombatEvent(true);
}
}
@ -1976,19 +1978,18 @@ void Mob::AI_Event_NoLongerEngaged() {
StopNavigation();
ClearRampage();
if(IsNPC())
{
if (IsNPC()) {
SetPrimaryAggro(false);
SetAssistAggro(false);
if(CastToNPC()->GetCombatEvent() && GetHP() > 0)
{
if(entity_list.GetNPCByID(GetID()))
{
uint16 emoteid = CastToNPC()->GetEmoteID();
parse->EventNPC(EVENT_COMBAT, CastToNPC(), nullptr, "0", 0);
if(emoteid != 0)
CastToNPC()->DoNPCEmote(LEAVECOMBAT,emoteid);
CastToNPC()->SetCombatEvent(false);
if (CastToNPC()->GetCombatEvent() && GetHP() > 0) {
if (entity_list.GetNPCByID(this->GetID())) {
uint16 emoteid = CastToNPC()->GetEmoteID();
parse->EventNPC(EVENT_COMBAT, CastToNPC(), nullptr, "0", 0);
if (emoteid != 0) {
CastToNPC()->DoNPCEmote(LEAVECOMBAT, emoteid);
}
combat_record.Stop();
CastToNPC()->SetCombatEvent(false);
}
}
}

View File

@ -102,6 +102,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
#include "mob_movement_manager.h"
#include "client.h"
#include "mob.h"
extern Zone* zone;
@ -6773,3 +6774,17 @@ bool Mob::IsFromTriggeredSpell(CastingSlot slot, uint32 item_slot) {
}
return false;
}
void Mob::SetHP(int32 hp)
{
if (hp >= max_hp) {
current_hp = max_hp;
return;
}
if (combat_record.InCombat()) {
combat_record.ProcessHPEvent(hp, current_hp);
}
current_hp = hp;
}