diff --git a/zone/attack.cpp b/zone/attack.cpp index 520e6226e..f0f8abd11 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1724,7 +1724,7 @@ void Client::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::Skill } } -bool Client::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill, KilledByTypes killed_by) +bool Client::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill, KilledByTypes killed_by, bool is_buff_tic) { if (!ClientFinishedLoading() || dead) { return false; @@ -1786,12 +1786,25 @@ bool Client::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::Skil /* Make Death Packet */ EQApplicationPacket app(OP_Death, sizeof(Death_Struct)); Death_Struct* d = (Death_Struct*)app.pBuffer; + + // Convert last message to color to avoid duplicate damage messages + // that occur in these rare cases when this is the death blow. + if (IsValidSpell(spell) && + (attack_skill == EQ::skills::SkillTigerClaw || + (IsDamageSpell(spell) && IsDiscipline(spell)) || + !is_buff_tic)) { + d->attack_skill = DamageTypeSpell; + d->spell_id = (is_buff_tic) ? UINT32_MAX : spell; + } + else { + d->attack_skill = SkillDamageTypes[attack_skill]; + d->spell_id = UINT32_MAX; + } + d->spawn_id = GetID(); d->killer_id = killer_mob ? killer_mob->GetID() : 0; d->corpseid = GetID(); d->bindzoneid = m_pp.binds[0].zone_id; - d->spell_id = IsValidSpell(spell) ? spell : 0xffffffff; - d->attack_skill = IsValidSpell(spell) ? 0xe7 : attack_skill; d->damage = damage; app.priority = 6; entity_list.QueueClients(this, &app); @@ -2380,7 +2393,7 @@ 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, KilledByTypes killed_by) +bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill, KilledByTypes killed_by, bool is_buff_tic) { LogCombat( "Fatal blow dealt by [{}] with [{}] damage, spell [{}], skill [{}]", @@ -2493,12 +2506,24 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy auto app = new EQApplicationPacket(OP_Death, sizeof(Death_Struct)); auto d = (Death_Struct*) app->pBuffer; + + // Convert last message to color to avoid duplicate damage messages + // that occur in these rare cases when this is the death blow. + if (IsValidSpell(spell) && + (attack_skill == EQ::skills::SkillTigerClaw || + (IsDamageSpell(spell) && IsDiscipline(spell)) || + !is_buff_tic)) { + d->attack_skill = DamageTypeSpell; + d->spell_id = (is_buff_tic) ? UINT32_MAX : spell; + } + else { + d->attack_skill = SkillDamageTypes[attack_skill]; + d->spell_id = UINT32_MAX; + } d->spawn_id = GetID(); d->killer_id = killer_mob ? killer_mob->GetID() : 0; d->bindzoneid = 0; - d->spell_id = UINT32_MAX; - d->attack_skill = SkillDamageTypes[attack_skill]; d->damage = damage; d->corpseid = GetID(); @@ -4257,8 +4282,8 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons if (!IsSaved && !TrySpellOnDeath()) { SetHP(-500); - - if (Death(attacker, damage, spell_id, skill_used)) { + // killedByType is clarified in Client::Death if we are client. + if (Death(attacker, damage, spell_id, skill_used, KilledByTypes::Killed_NPC, iBuffTic)) { return; } } diff --git a/zone/beacon.h b/zone/beacon.h index 0c65d46b5..ac5beb31e 100644 --- a/zone/beacon.h +++ b/zone/beacon.h @@ -34,7 +34,7 @@ public: ~Beacon(); //abstract virtual function implementations requird by base abstract class - virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC) { return true; } + virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC, bool is_buff_tic = false) { return true; } virtual void Damage(Mob* from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) { return; } virtual bool HasRaid() { return false; } virtual bool HasGroup() { return false; } diff --git a/zone/bot.cpp b/zone/bot.cpp index cf07281ba..77cfa9840 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4341,7 +4341,7 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* } } -bool Bot::Death(Mob *killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by) +bool Bot::Death(Mob *killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by, bool is_buff_tic) { if (!NPC::Death(killer_mob, damage, spell_id, attack_skill)) { return false; diff --git a/zone/bot.h b/zone/bot.h index 66e3c2cb5..1c58af7ab 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -131,7 +131,7 @@ public: Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType *npcTypeData, int32 expansion_bitmask); //abstract virtual override function implementations requird by base abstract class - bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC) override; + bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC, bool is_buff_tic = false) override; void Damage(Mob* from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) override; diff --git a/zone/client.h b/zone/client.h index e73a826eb..4a6548ffb 100644 --- a/zone/client.h +++ b/zone/client.h @@ -262,7 +262,7 @@ public: bool GotoPlayerRaid(const std::string& player_name); //abstract virtual function implementations required by base abstract class - virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC); + virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC, bool is_buff_tic = false); virtual void Damage(Mob* from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None); virtual bool HasRaid() { return (GetRaid() ? true : false); } virtual bool HasGroup() { return (GetGroup() ? true : false); } diff --git a/zone/corpse.h b/zone/corpse.h index 2879cd85d..28fc8c91a 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -68,7 +68,8 @@ public: int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, - KilledByTypes killed_by = KilledByTypes::Killed_NPC + KilledByTypes killed_by = KilledByTypes::Killed_NPC, + bool is_buff_tic = false ) { return true; } virtual void Damage( diff --git a/zone/encounter.h b/zone/encounter.h index 4a3e89d72..b0c623562 100644 --- a/zone/encounter.h +++ b/zone/encounter.h @@ -34,7 +34,7 @@ public: ~Encounter(); //abstract virtual function implementations required by base abstract class - virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC) { return true; } + virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC, bool is_buff_tic = false) { return true; } virtual void Damage(Mob* from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) { return; } bool Attack(Mob* other, int Hand = EQ::invslot::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr) override { diff --git a/zone/merc.cpp b/zone/merc.cpp index 6006cc6a1..3cf3a0a72 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -4070,7 +4070,7 @@ Mob* Merc::GetOwnerOrSelf() { return Result; } -bool Merc::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill, uint8 killed_by) +bool Merc::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill, uint8 killed_by, bool is_buff_tic) { if (!NPC::Death(killer_mob, damage, spell, attack_skill)) { return false; diff --git a/zone/merc.h b/zone/merc.h index 6aa1c26c5..c0730dd0e 100644 --- a/zone/merc.h +++ b/zone/merc.h @@ -52,7 +52,7 @@ public: virtual ~Merc(); //abstract virtual function implementations requird by base abstract class - virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, uint8 killed_by = 0); + virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, uint8 killed_by = 0, bool is_buff_tic = false); virtual void Damage(Mob* from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None); virtual bool Attack(Mob* other, int Hand = EQ::invslot::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr); diff --git a/zone/mob.h b/zone/mob.h index 7cde4fa0e..014071be5 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -541,7 +541,7 @@ public: bool CanClassEquipItem(uint32 item_id); bool CanRaceEquipItem(uint32 item_id); bool AffectedBySpellExcludingSlot(int slot, int effect); - virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC) = 0; + virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC, bool is_buff_tic = false) = 0; virtual void Damage(Mob* from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) = 0; void SetHP(int64 hp); diff --git a/zone/npc.h b/zone/npc.h index 3280f462a..ff9fb23cd 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -130,7 +130,7 @@ public: static NPC * SpawnZonePointNodeNPC(std::string name, const glm::vec4 &position); //abstract virtual function implementations requird by base abstract class - virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC); + virtual bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC, bool is_buff_tic = false); virtual void Damage(Mob* from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None); bool Attack(Mob* other, int Hand = EQ::invslot::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr) override;