[Bug Fix] Fix NPC After Death Emotes (#4021)

* [Bug Fix] Fix NPC After Death Emotes

- `DoNPCEmote` was being called on the corpse, not the NPC, so it wasn't working for some reason despite it working for years.
- Cleaned up some other logic and variable names in `NPC::Death` while I was in there.

* Update attack.cpp

* Update npc.cpp

* Update attack.cpp

* Update attack.cpp
This commit is contained in:
Alex King 2024-01-29 00:03:34 -05:00 committed by GitHub
parent 1cb72642ac
commit a1f2a21c99
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 298 additions and 190 deletions

View File

@ -1818,9 +1818,8 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
parse->EventNPC(EVENT_SLAY, killerMob->CastToNPC(), this, "", 0); parse->EventNPC(EVENT_SLAY, killerMob->CastToNPC(), this, "", 0);
} }
const uint32 emote_id = killerMob->GetEmoteID(); if (emoteid) {
if (emote_id) { killerMob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid, this);
killerMob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emote_id, this);
} }
killerMob->TrySpellOnKill(killed_level, spell); killerMob->TrySpellOnKill(killed_level, spell);
@ -2376,10 +2375,15 @@ void NPC::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::SkillTyp
bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill) bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill)
{ {
LogCombat("Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]", LogCombat(
((killer_mob) ? (killer_mob->GetName()) : ("[nullptr]")), damage, spell, attack_skill); "Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]",
(killer_mob ? killer_mob->GetName() : "[nullptr]"),
damage,
spell,
attack_skill
);
Mob *oos = killer_mob ? killer_mob->GetOwnerOrSelf() : nullptr; Mob* owner_or_self = killer_mob ? killer_mob->GetOwnerOrSelf() : nullptr;
if (IsNPC()) { if (IsNPC()) {
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH)) { if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH)) {
@ -2391,7 +2395,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
static_cast<int>(attack_skill) static_cast<int>(attack_skill)
); );
if (parse->EventNPC(EVENT_DEATH, this, oos, export_string, 0) != 0) { if (parse->EventNPC(EVENT_DEATH, this, owner_or_self, export_string, 0) != 0) {
if (GetHP() < 0) { if (GetHP() < 0) {
SetHP(0); SetHP(0);
} }
@ -2408,7 +2412,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
spell, spell,
static_cast<int>(attack_skill) static_cast<int>(attack_skill)
); );
if (parse->EventBot(EVENT_DEATH, CastToBot(), oos, export_string, 0) != 0) { if (parse->EventBot(EVENT_DEATH, CastToBot(), owner_or_self, export_string, 0) != 0) {
if (GetHP() < 0) { if (GetHP() < 0) {
SetHP(0); SetHP(0);
} }
@ -2435,7 +2439,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
if (IsEngaged()) { if (IsEngaged()) {
zone->DelAggroMob(); zone->DelAggroMob();
Log(Logs::Detail, Logs::Attack, "%s Mobs currently Aggro %i", __FUNCTION__, zone->MobsAggroCount()); LogAttackDetail("{} Mob{} currently aggroed.", zone->MobsAggroCount(), zone->MobsAggroCount() != 1 ? "s" : "");
} }
ShieldAbilityClearVariables(); ShieldAbilityClearVariables();
@ -2444,45 +2448,52 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
SetPet(0); SetPet(0);
if (GetSwarmOwner()) { if (GetSwarmOwner()) {
auto* owner = entity_list.GetMobID(GetSwarmOwner()); Mob* owner = entity_list.GetMobID(GetSwarmOwner());
if (owner) { if (owner) {
owner->SetTempPetCount(owner->GetTempPetCount() - 1); owner->SetTempPetCount(owner->GetTempPetCount() - 1);
} }
} }
auto* killer = GetHateDamageTop(this);
entity_list.RemoveFromTargets(this, p_depop); entity_list.RemoveFromTargets(this, p_depop);
if (p_depop) { if (p_depop) {
return false; return false;
} }
const auto illusion_spell_id = spellbonuses.Illusion; const int illusion_spell_id = spellbonuses.Illusion;
HasAISpellEffects = false; HasAISpellEffects = false;
BuffFadeAll(); BuffFadeAll();
const auto killed_level = GetLevel();
const uint8 killed_level = GetLevel();
if (GetClass() == Class::LDoNTreasure) { // open chest if (GetClass() == Class::LDoNTreasure) { // open chest
auto outapp = new EQApplicationPacket(OP_Animation, sizeof(Animation_Struct)); auto outapp = new EQApplicationPacket(OP_Animation, sizeof(Animation_Struct));
Animation_Struct* anim = (Animation_Struct*)outapp->pBuffer;
anim->spawnid = GetID(); auto a = (Animation_Struct*) outapp->pBuffer;
anim->action = 0x0F;
anim->speed = 10; a->spawnid = GetID();
a->action = 0x0F;
a->speed = 10;
entity_list.QueueCloseClients(this, outapp); entity_list.QueueCloseClients(this, outapp);
safe_delete(outapp); safe_delete(outapp);
} }
auto app = new EQApplicationPacket(OP_Death, sizeof(Death_Struct)); auto app = new EQApplicationPacket(OP_Death, sizeof(Death_Struct));
auto* d = (Death_Struct*) app->pBuffer;
d->spawn_id = GetID(); auto d = (Death_Struct*) app->pBuffer;
d->killer_id = killer_mob ? killer_mob->GetID() : 0;
d->bindzoneid = 0; d->spawn_id = GetID();
d->spell_id = 0xffffffff; // Sending spell was causing extra DoT land msg d->killer_id = killer_mob ? killer_mob->GetID() : 0;
d->bindzoneid = 0;
d->spell_id = UINT32_MAX;
d->attack_skill = SkillDamageTypes[attack_skill]; d->attack_skill = SkillDamageTypes[attack_skill];
d->damage = damage; d->damage = damage;
app->priority = 6; app->priority = 6;
entity_list.QueueClients(killer_mob, app, false); entity_list.QueueClients(killer_mob, app, false);
safe_delete(app); safe_delete(app);
@ -2495,24 +2506,37 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
hate_list.AddEntToHateList(killer_mob, damage); hate_list.AddEntToHateList(killer_mob, damage);
} }
Mob *give_exp = hate_list.GetDamageTopOnHateList(this); Mob* give_exp = hate_list.GetDamageTopOnHateList(this);
if (give_exp) { if (give_exp) {
give_exp = killer; give_exp = killer_mob;
} }
if (give_exp && give_exp->HasOwner()) { if (give_exp && give_exp->HasOwner()) {
bool ownerInGroup = false; bool owner_in_group = false;
if ((give_exp->HasGroup() && give_exp->GetGroup()->IsGroupMember(give_exp->GetUltimateOwner()))
|| (give_exp->IsPet() && (give_exp->GetOwner()->IsClient() if (
|| (give_exp->GetOwner()->HasGroup() && give_exp->GetOwner()->GetGroup()->IsGroupMember(give_exp->GetOwner()->GetUltimateOwner())))) (
) { give_exp->HasGroup() &&
ownerInGroup = true; give_exp->GetGroup()->IsGroupMember(give_exp->GetUltimateOwner())
) ||
(
give_exp->IsPet() &&
(
give_exp->GetOwner()->IsClient() ||
(
give_exp->GetOwner()->HasGroup() &&
give_exp->GetOwner()->GetGroup()->IsGroupMember(give_exp->GetOwner()->GetUltimateOwner())
)
)
)
) {
owner_in_group = true;
} }
give_exp = give_exp->GetUltimateOwner(); give_exp = give_exp->GetUltimateOwner();
if (!RuleB(Bots, BotGroupXP) && !ownerInGroup) { if (!RuleB(Bots, BotGroupXP) && !owner_in_group) {
give_exp = nullptr; give_exp = nullptr;
} }
} }
@ -2526,9 +2550,9 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
} }
} }
int PlayerCount = 0; // QueryServ Player Counting int player_count = 0; // QueryServ Player Counting
Client *give_exp_client = nullptr; Client* give_exp_client = nullptr;
if (give_exp && give_exp->IsClient()) { if (give_exp && give_exp->IsClient()) {
give_exp_client = give_exp->CastToClient(); give_exp_client = give_exp->CastToClient();
} }
@ -2538,13 +2562,13 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
hate_list.DoFactionHits(GetNPCFactionID(), GetPrimaryFaction(), GetFactionAmount()); hate_list.DoFactionHits(GetNPCFactionID(), GetPrimaryFaction(), GetFactionAmount());
} }
bool IsLdonTreasure = (GetClass() == Class::LDoNTreasure); const bool is_ldon_treasure = GetClass() == Class::LDoNTreasure;
if (give_exp_client && !IsCorpse()) { if (give_exp_client && !IsCorpse()) {
Group *kg = entity_list.GetGroupByClient(give_exp_client); Group* killer_group = entity_list.GetGroupByClient(give_exp_client);
Raid *kr = entity_list.GetRaidByClient(give_exp_client); Raid* killer_raid = entity_list.GetRaidByClient(give_exp_client);
int64 finalxp = give_exp_client->GetExperienceForKill(this); int64 final_exp = give_exp_client->GetExperienceForKill(this);
// handle task credit on behalf of the killer // handle task credit on behalf of the killer
if (RuleB(TaskSystem, EnableTaskSystem)) { if (RuleB(TaskSystem, EnableTaskSystem)) {
@ -2556,75 +2580,98 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
task_manager->HandleUpdateTasksOnKill(give_exp_client, this); task_manager->HandleUpdateTasksOnKill(give_exp_client, this);
} }
if (kr) { if (killer_raid) {
if (!IsLdonTreasure && MerchantType == 0) { if (!is_ldon_treasure && MerchantType == 0) {
kr->SplitExp((finalxp), this); killer_raid->SplitExp(final_exp, this);
if (killer_mob && (kr->IsRaidMember(killer_mob->GetName()) || kr->IsRaidMember(killer_mob->GetUltimateOwner()->GetName())))
if (
killer_mob &&
(
killer_raid->IsRaidMember(killer_mob->GetName()) ||
killer_raid->IsRaidMember(killer_mob->GetUltimateOwner()->GetName())
)
) {
killer_mob->TrySpellOnKill(killed_level, spell); killer_mob->TrySpellOnKill(killed_level, spell);
}
} }
/* Send the EVENT_KILLED_MERIT event for all raid members */ /* Send the EVENT_KILLED_MERIT event for all raid members */
for (const auto& m : kr->members) { for (const auto& m : killer_raid->members) {
if (m.is_bot) { if (m.is_bot) {
continue; continue;
} }
if (m.member && m.member->IsClient()) { // If Group Member is Client if (m.member) {
Client *c = m.member; m.member->RecordKilledNPCEvent(this);
c->RecordKilledNPCEvent(this);
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_KILLED_MERIT)) { if (parse->HasQuestSub(GetNPCTypeID(), EVENT_KILLED_MERIT)) {
parse->EventNPC(EVENT_KILLED_MERIT, this, c, "killed", 0); parse->EventNPC(EVENT_KILLED_MERIT, this, m.member, "killed", 0);
} }
if (RuleB(NPC, EnableMeritBasedFaction)) { if (RuleB(NPC, EnableMeritBasedFaction)) {
c->SetFactionLevel(c->CharacterID(), GetNPCFactionID(), c->GetBaseClass(), c->GetBaseRace(), c->GetDeity()); m.member->SetFactionLevel(
m.member->CharacterID(),
GetNPCFactionID(),
m.member->GetBaseClass(),
m.member->GetBaseRace(),
m.member->GetDeity()
);
} }
PlayerCount++; player_count++;
} }
} }
// QueryServ Logging - Raid Kills // QueryServ Logging - Raid Kills
if (RuleB(QueryServ, PlayerLogNPCKills)) { if (RuleB(QueryServ, PlayerLogNPCKills)) {
auto pack = auto pack = new ServerPacket(
new ServerPacket(ServerOP_QSPlayerLogNPCKills, ServerOP_QSPlayerLogNPCKills,
sizeof(QSPlayerLogNPCKill_Struct) + sizeof(QSPlayerLogNPCKill_Struct) +
(sizeof(QSPlayerLogNPCKillsPlayers_Struct) * PlayerCount)); (sizeof(QSPlayerLogNPCKillsPlayers_Struct) * player_count)
PlayerCount = 0; );
QSPlayerLogNPCKill_Struct* QS = (QSPlayerLogNPCKill_Struct*)pack->pBuffer;
QS->s1.NPCID = GetNPCTypeID(); player_count = 0;
auto QS = (QSPlayerLogNPCKill_Struct*)pack->pBuffer;
QS->s1.NPCID = GetNPCTypeID();
QS->s1.ZoneID = GetZoneID(); QS->s1.ZoneID = GetZoneID();
QS->s1.Type = 2; // Raid Fight QS->s1.Type = 2; // Raid Fight
for (const auto& m : kr->members) {
for (const auto& m : killer_raid->members) {
if (m.is_bot) { if (m.is_bot) {
continue; continue;
} }
if (m.member && m.member->IsClient()) { // If Group Member is Client if (m.member && m.member->IsClient()) {
Client *c = m.member; QS->Chars[player_count].char_id = m.member->CastToClient()->CharacterID();
QS->Chars[PlayerCount].char_id = c->CharacterID(); player_count++;
PlayerCount++;
} }
} }
worldserver.SendPacket(pack); // Send Packet to World
worldserver.SendPacket(pack);
safe_delete(pack); safe_delete(pack);
} }
// End QueryServ Logging } else if (give_exp_client->IsGrouped() && killer_group) {
if (!is_ldon_treasure && MerchantType == 0) {
killer_group->SplitExp(final_exp, this);
} if (
else if (give_exp_client->IsGrouped() && kg != nullptr) { killer_mob &&
if (!IsLdonTreasure && MerchantType == 0) { (
kg->SplitExp((finalxp), this); killer_group->IsGroupMember(killer_mob->GetName()) ||
if (killer_mob && (kg->IsGroupMember(killer_mob->GetName()) || kg->IsGroupMember(killer_mob->GetUltimateOwner()->GetName()))) killer_group->IsGroupMember(killer_mob->GetUltimateOwner()->GetName())
)
) {
killer_mob->TrySpellOnKill(killed_level, spell); killer_mob->TrySpellOnKill(killed_level, spell);
}
} }
/* Send the EVENT_KILLED_MERIT event and update kill tasks /* Send the EVENT_KILLED_MERIT event and update kill tasks
* for all group members */ * for all group members */
for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { for (const auto& m : killer_group->members) {
if (kg->members[i] != nullptr && kg->members[i]->IsClient()) { // If Group Member is Client if (m && m->IsClient()) {
Client *c = kg->members[i]->CastToClient(); Client* c = m->CastToClient();
c->RecordKilledNPCEvent(this); c->RecordKilledNPCEvent(this);
@ -2632,70 +2679,101 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
parse->EventNPC(EVENT_KILLED_MERIT, this, c, "killed", 0); parse->EventNPC(EVENT_KILLED_MERIT, this, c, "killed", 0);
} }
if (RuleB(NPC, EnableMeritBasedFaction)) if (RuleB(NPC, EnableMeritBasedFaction)) {
c->SetFactionLevel(c->CharacterID(), GetNPCFactionID(), c->GetBaseClass(), c->GetBaseRace(), c->GetDeity()); c->SetFactionLevel(
c->CharacterID(),
GetNPCFactionID(),
c->GetBaseClass(),
c->GetBaseRace(),
c->GetDeity()
);
}
PlayerCount++; player_count++;
} }
} }
// QueryServ Logging - Group Kills // QueryServ Logging - Group Kills
if (RuleB(QueryServ, PlayerLogNPCKills)) { if (RuleB(QueryServ, PlayerLogNPCKills)) {
auto pack = auto pack = new ServerPacket(
new ServerPacket(ServerOP_QSPlayerLogNPCKills, ServerOP_QSPlayerLogNPCKills,
sizeof(QSPlayerLogNPCKill_Struct) + sizeof(QSPlayerLogNPCKill_Struct) +
(sizeof(QSPlayerLogNPCKillsPlayers_Struct) * PlayerCount)); (sizeof(QSPlayerLogNPCKillsPlayers_Struct) * player_count)
PlayerCount = 0; );
QSPlayerLogNPCKill_Struct* QS = (QSPlayerLogNPCKill_Struct*)pack->pBuffer;
QS->s1.NPCID = GetNPCTypeID(); player_count = 0;
auto QS = (QSPlayerLogNPCKill_Struct*) pack->pBuffer;
QS->s1.NPCID = GetNPCTypeID();
QS->s1.ZoneID = GetZoneID(); QS->s1.ZoneID = GetZoneID();
QS->s1.Type = 1; // Group Fight QS->s1.Type = 1; // Group Fight
for (int i = 0; i < MAX_GROUP_MEMBERS; i++) {
if (kg->members[i] != nullptr && kg->members[i]->IsClient()) { // If Group Member is Client for (const auto& m : killer_group->members) {
Client *c = kg->members[i]->CastToClient(); if (m && m->IsClient()) {
QS->Chars[PlayerCount].char_id = c->CharacterID(); QS->Chars[player_count].char_id = m->CastToClient()->CharacterID();
PlayerCount++; player_count++;
} }
} }
worldserver.SendPacket(pack); // Send Packet to World
worldserver.SendPacket(pack);
safe_delete(pack); safe_delete(pack);
} }
// End QueryServ Logging } else {
} if (!is_ldon_treasure && !MerchantType) {
else { const uint32 con_level = give_exp->GetLevelCon(GetLevel());
if (!IsLdonTreasure && MerchantType == 0) {
int conlevel = give_exp->GetLevelCon(GetLevel()); if (con_level != CON_GRAY) {
if (conlevel != CON_GRAY) {
if (!GetOwner() || (GetOwner() && !GetOwner()->IsClient())) { if (!GetOwner() || (GetOwner() && !GetOwner()->IsClient())) {
give_exp_client->AddEXP((finalxp), conlevel); give_exp_client->AddEXP(final_exp, con_level);
if (killer_mob && (killer_mob->GetID() == give_exp_client->GetID() || killer_mob->GetUltimateOwner()->GetID() == give_exp_client->GetID()))
if (
killer_mob &&
(
killer_mob->GetID() == give_exp_client->GetID() ||
killer_mob->GetUltimateOwner()->GetID() == give_exp_client->GetID()
)
) {
killer_mob->TrySpellOnKill(killed_level, spell); killer_mob->TrySpellOnKill(killed_level, spell);
}
} }
} }
} }
/* Send the EVENT_KILLED_MERIT event */ /* Send the EVENT_KILLED_MERIT event */
give_exp_client->RecordKilledNPCEvent(this); give_exp_client->RecordKilledNPCEvent(this);
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_KILLED_MERIT)) { if (parse->HasQuestSub(GetNPCTypeID(), EVENT_KILLED_MERIT)) {
parse->EventNPC(EVENT_KILLED_MERIT, this, give_exp_client, "killed", 0); parse->EventNPC(EVENT_KILLED_MERIT, this, give_exp_client, "killed", 0);
} }
if (RuleB(NPC, EnableMeritBasedFaction)) if (RuleB(NPC, EnableMeritBasedFaction)) {
give_exp_client->SetFactionLevel(give_exp_client->CharacterID(), GetNPCFactionID(), give_exp_client->GetBaseClass(), give_exp_client->SetFactionLevel(
give_exp_client->GetBaseRace(), give_exp_client->GetDeity()); give_exp_client->CharacterID(),
GetNPCFactionID(),
give_exp_client->GetBaseClass(),
give_exp_client->GetBaseRace(),
give_exp_client->GetDeity()
);
}
// QueryServ Logging - Solo // QueryServ Logging - Solo
if (RuleB(QueryServ, PlayerLogNPCKills)) { if (RuleB(QueryServ, PlayerLogNPCKills)) {
auto pack = new ServerPacket(ServerOP_QSPlayerLogNPCKills, auto pack = new ServerPacket(
ServerOP_QSPlayerLogNPCKills,
sizeof(QSPlayerLogNPCKill_Struct) + sizeof(QSPlayerLogNPCKill_Struct) +
(sizeof(QSPlayerLogNPCKillsPlayers_Struct) * 1)); (sizeof(QSPlayerLogNPCKillsPlayers_Struct) * 1)
QSPlayerLogNPCKill_Struct* QS = (QSPlayerLogNPCKill_Struct*)pack->pBuffer; );
QS->s1.NPCID = GetNPCTypeID();
QS->s1.ZoneID = GetZoneID(); auto QS = (QSPlayerLogNPCKill_Struct*)pack->pBuffer;
QS->s1.Type = 0; // Solo Fight
Client *c = give_exp_client; QS->s1.NPCID = GetNPCTypeID();
QS->Chars[0].char_id = c->CharacterID(); QS->s1.ZoneID = GetZoneID();
PlayerCount++; QS->s1.Type = 0; // Solo Fight
QS->Chars[0].char_id = give_exp_client->CharacterID();
player_count++;
worldserver.SendPacket(pack); // Send Packet to World worldserver.SendPacket(pack); // Send Packet to World
safe_delete(pack); safe_delete(pack);
} }
@ -2703,32 +2781,68 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
} }
} }
bool allow_merchant_corpse = RuleB(Merchant, AllowCorpse); const bool allow_merchant_corpse = RuleB(Merchant, AllowCorpse);
bool is_merchant = (class_ == Class::Merchant || class_ == Class::AdventureMerchant || MerchantType != 0); const bool is_merchant = (class_ == Class::Merchant || class_ == Class::AdventureMerchant || MerchantType != 0);
Corpse* corpse = nullptr; Corpse* corpse = nullptr;
const uint16 entity_id = GetID(); const uint16 entity_id = GetID();
if (!HasOwner() && !IsMerc() && !GetSwarmInfo() && (!is_merchant || allow_merchant_corpse) && if (
((killer && (killer->IsClient() || (killer->HasOwner() && killer->GetUltimateOwner()->IsClient()) || !HasOwner() &&
(killer->IsNPC() && killer->CastToNPC()->GetSwarmInfo() && killer->CastToNPC()->GetSwarmInfo()->GetOwner() && killer->CastToNPC()->GetSwarmInfo()->GetOwner()->IsClient()))) !IsMerc() &&
|| (killer_mob && IsLdonTreasure))) !GetSwarmInfo() &&
{ (!is_merchant || allow_merchant_corpse) &&
if (killer != 0) { (
if (killer->GetOwner() != 0 && killer->GetOwner()->IsClient()) (
killer = killer->GetOwner(); killer_mob &&
(
killer_mob->IsClient() ||
(
killer_mob->HasOwner() &&
killer_mob->GetUltimateOwner()->IsClient()
) ||
(
killer_mob->IsNPC() &&
killer_mob->CastToNPC()->GetSwarmInfo() &&
killer_mob->CastToNPC()->GetSwarmInfo()->GetOwner() &&
killer_mob->CastToNPC()->GetSwarmInfo()->GetOwner()->IsClient()
)
)
) ||
(
killer_mob && is_ldon_treasure
)
)
) {
if (killer_mob) {
if (killer_mob->GetOwner() != 0 && killer_mob->GetOwner()->IsClient()) {
killer_mob = killer_mob->GetOwner();
}
if (killer->IsClient() && !killer->CastToClient()->GetGM()) if (killer_mob->IsClient() && !killer_mob->CastToClient()->GetGM()) {
CheckTrivialMinMaxLevelDrop(killer); CheckTrivialMinMaxLevelDrop(killer_mob);
}
} }
entity_list.RemoveFromAutoXTargets(this); entity_list.RemoveFromAutoXTargets(this);
const uint32 emote_id = GetEmoteID(); corpse = new Corpse(
corpse = new Corpse(this, &itemlist, GetNPCTypeID(), &NPCTypedata, this,
level > 54 ? RuleI(NPC, MajorNPCCorpseDecayTimeMS) &itemlist,
: RuleI(NPC, MinorNPCCorpseDecayTimeMS)); GetNPCTypeID(),
&NPCTypedata,
(
level > 54 ?
RuleI(NPC, MajorNPCCorpseDecayTimeMS) :
RuleI(NPC, MinorNPCCorpseDecayTimeMS)
)
);
if (killer_mob && emoteid) {
DoNPCEmote(EQ::constants::EmoteEventTypes::AfterDeath, emoteid, killer_mob);
}
entity_list.LimitRemoveNPC(this); entity_list.LimitRemoveNPC(this);
entity_list.AddCorpse(corpse, GetID()); entity_list.AddCorpse(corpse, GetID());
@ -2740,25 +2854,27 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
SetID(0); SetID(0);
ApplyIllusionToCorpse(illusion_spell_id, corpse); ApplyIllusionToCorpse(illusion_spell_id, corpse);
if (killer != 0 && emote_id) if (killer_mob && killer_mob->IsClient()) {
corpse->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::AfterDeath, emote_id, killer); corpse->AllowPlayerLoot(killer_mob, 0);
if (killer != 0 && killer->IsClient()) { if (killer_mob->IsGrouped()) {
corpse->AllowPlayerLoot(killer, 0); Group* g = entity_list.GetGroupByClient(killer_mob->CastToClient());
if (killer->IsGrouped()) { if (g) {
Group* group = entity_list.GetGroupByClient(killer->CastToClient()); uint8 slot_id = 0;
if (group != 0) {
for (int i = 0; i<6; i++) { // Doesnt work right, needs work for (const auto &m : g->members) {
if (group->members[i] != nullptr) { if (m) {
corpse->AllowPlayerLoot(group->members[i], i); corpse->AllowPlayerLoot(m, slot_id);
} }
slot_id++;
} }
} }
} } else if (killer_mob->IsRaidGrouped()) {
else if (killer->IsRaidGrouped()) { Raid* r = entity_list.GetRaidByClient(killer_mob->CastToClient());
Raid* r = entity_list.GetRaidByClient(killer->CastToClient());
if (r) { if (r) {
int i = 0; uint8 slot_id = 0;
for (const auto& m : r->members) {
for (const auto &m : r->members) {
if (m.is_bot) { if (m.is_bot) {
continue; continue;
} }
@ -2766,77 +2882,71 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
switch (r->GetLootType()) { switch (r->GetLootType()) {
case RaidLootType::LeaderOnly: case RaidLootType::LeaderOnly:
if (m.member && m.is_raid_leader) { if (m.member && m.is_raid_leader) {
corpse->AllowPlayerLoot(m.member, i); corpse->AllowPlayerLoot(m.member, slot_id);
i++; slot_id++;
} }
break; break;
case RaidLootType::LeaderAndGroupLeadersOnly: case RaidLootType::LeaderAndGroupLeadersOnly:
if (m.member && (m.is_raid_leader || m.is_group_leader)) { if (m.member && (m.is_raid_leader || m.is_group_leader)) {
corpse->AllowPlayerLoot(m.member, i); corpse->AllowPlayerLoot(m.member, slot_id);
i++; slot_id++;
} }
break; break;
case RaidLootType::LeaderSelected: case RaidLootType::LeaderSelected:
if (m.member && m.is_looter) { if (m.member && m.is_looter) {
corpse->AllowPlayerLoot(m.member, i); corpse->AllowPlayerLoot(m.member, slot_id);
i++; slot_id++;
} }
break; break;
case RaidLootType::EntireRaid: case RaidLootType::EntireRaid:
default: default:
if (m.member) { if (m.member) {
corpse->AllowPlayerLoot(m.member, i); corpse->AllowPlayerLoot(m.member, slot_id);
i++; slot_id++;
} }
break; break;
} }
} }
} }
} }
} } else if (killer_mob && is_ldon_treasure) {
else if (killer_mob && IsLdonTreasure) { Mob* ultimate_owner = killer_mob->GetUltimateOwner();
auto u_owner = killer_mob->GetUltimateOwner(); if (ultimate_owner->IsClient()) {
if (u_owner->IsClient()) corpse->AllowPlayerLoot(ultimate_owner, 0);
corpse->AllowPlayerLoot(u_owner, 0); }
} }
if (zone && zone->adv_data) { if (zone && zone->adv_data) {
ServerZoneAdventureDataReply_Struct *sr = (ServerZoneAdventureDataReply_Struct*)zone->adv_data; auto sr = (ServerZoneAdventureDataReply_Struct *) zone->adv_data;
if (sr->type == Adventure_Kill) { if (sr->type == Adventure_Kill) {
zone->DoAdventureCountIncrease(); zone->DoAdventureCountIncrease();
} } else if (sr->type == Adventure_Assassinate) {
else if (sr->type == Adventure_Assassinate) {
if (sr->data_id == GetNPCTypeID()) { if (sr->data_id == GetNPCTypeID()) {
zone->DoAdventureCountIncrease(); zone->DoAdventureCountIncrease();
} } else {
else {
zone->DoAdventureAssassinationCountIncrease(); zone->DoAdventureAssassinationCountIncrease();
} }
} }
} }
} } else {
else {
entity_list.RemoveFromXTargets(this); entity_list.RemoveFromXTargets(this);
} }
if (IsNPC()) {
// Parse quests even if we're killed by an NPC if (emoteid) {
if (oos) { DoNPCEmote(EQ::constants::EmoteEventTypes::OnDeath, emoteid, killer_mob);
if (IsNPC()) {
const uint32 emote_id = GetEmoteID();
if (emote_id) {
DoNPCEmote(EQ::constants::EmoteEventTypes::OnDeath, emote_id, killer_mob);
}
} }
}
if (oos->IsNPC()) { if (owner_or_self) {
if (parse->HasQuestSub(oos->GetNPCTypeID(), EVENT_NPC_SLAY)) { if (owner_or_self->IsNPC()) {
parse->EventNPC(EVENT_NPC_SLAY, oos->CastToNPC(), this, "", 0); if (parse->HasQuestSub(owner_or_self->GetNPCTypeID(), EVENT_NPC_SLAY)) {
parse->EventNPC(EVENT_NPC_SLAY, owner_or_self->CastToNPC(), this, "", 0);
} }
const uint32 emote_id = oos->GetEmoteID(); const uint32 emote_id = owner_or_self->GetEmoteID();
if (emote_id) { if (emote_id) {
oos->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledNPC, emote_id, this); owner_or_self->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledNPC, emote_id, this);
} }
if (killer_mob) { if (killer_mob) {
@ -2854,10 +2964,12 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
} }
WipeHateList(); WipeHateList();
p_depop = true; p_depop = true;
if (killer_mob && killer_mob->GetTarget() == this) //we can kill things without having them targeted if (killer_mob && killer_mob->GetTarget() == this) { // We can kill things without having them targeted
killer_mob->SetTarget(nullptr); //via AE effects and such.. killer_mob->SetTarget(nullptr);
}
entity_list.UpdateFindableNPCState(this, true); entity_list.UpdateFindableNPCState(this, true);
@ -2875,7 +2987,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
std::vector<std::any> args = { corpse }; std::vector<std::any> args = { corpse };
parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, export_string, 0, &args); parse->EventNPC(EVENT_DEATH_COMPLETE, this, owner_or_self, export_string, 0, &args);
} }
// Zone controller process EVENT_DEATH_ZONE (Death events) // Zone controller process EVENT_DEATH_ZONE (Death events)
@ -2891,7 +3003,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
std::vector<std::any> args = { corpse, this }; std::vector<std::any> args = { corpse, this };
DispatchZoneControllerEvent(EVENT_DEATH_ZONE, oos, export_string, 0, &args); DispatchZoneControllerEvent(EVENT_DEATH_ZONE, owner_or_self, export_string, 0, &args);
} }
return true; return true;

View File

@ -1800,9 +1800,8 @@ void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help)
parse->EventNPC(EVENT_COMBAT, CastToNPC(), attacker, "1", 0); parse->EventNPC(EVENT_COMBAT, CastToNPC(), attacker, "1", 0);
} }
const uint32 emote_id = GetEmoteID(); if (emoteid) {
if (emote_id) { CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::EnterCombat, emoteid, attacker);
CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::EnterCombat, emote_id, attacker);
} }
std::string mob_name = GetCleanName(); std::string mob_name = GetCleanName();

View File

@ -438,7 +438,7 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi
qGlobals = nullptr; qGlobals = nullptr;
SetEmoteID(static_cast<uint32>(npc_type_data->emoteid)); SetEmoteID(npc_type_data->emoteid);
InitializeBuffSlots(); InitializeBuffSlots();
CalcBonuses(); CalcBonuses();
@ -1113,9 +1113,8 @@ void NPC::UpdateEquipmentLight()
} }
void NPC::Depop(bool start_spawn_timer) { void NPC::Depop(bool start_spawn_timer) {
const uint32 emote_id = GetEmoteID(); if (emoteid) {
if (emote_id) { DoNPCEmote(EQ::constants::EmoteEventTypes::OnDespawn, emoteid);
DoNPCEmote(EQ::constants::EmoteEventTypes::OnDespawn, emote_id);
} }
if (IsNPC()) { if (IsNPC()) {
@ -3073,10 +3072,8 @@ void NPC::SendPayload(int payload_id, std::string payload_value)
NPC_Emote_Struct* NPC::GetNPCEmote(uint32 emote_id, uint8 event_) { NPC_Emote_Struct* NPC::GetNPCEmote(uint32 emote_id, uint8 event_) {
std::vector<NPC_Emote_Struct*> emotes; std::vector<NPC_Emote_Struct*> emotes;
for (const auto &e : zone->npc_emote_list) { for (auto& e : zone->npc_emote_list) {
auto nes = e; if (e->emoteid == emote_id && e->event_ == event_) {
if (nes->emoteid == emote_id && nes->event_ == event_) {
emotes.emplace_back(e); emotes.emplace_back(e);
} }
} }
@ -3098,7 +3095,7 @@ void NPC::DoNPCEmote(uint8 event_, uint32 emote_id, Mob* t)
return; return;
} }
auto e = GetNPCEmote(emote_id, event_); const auto& e = GetNPCEmote(emote_id, event_);
if (!e) { if (!e) {
return; return;
} }