From 72bf4b7e3cb17f6fbf17f17f166708e3ba57a504 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Tue, 9 Oct 2018 08:38:35 -0500 Subject: [PATCH 1/2] Send HP updates when mobs update regen hp internally --- zone/mob.cpp | 89 ++++++++++++++++++++++++++++++++----------------- zone/mob.h | 4 +-- zone/npc.cpp | 93 ++++++++++++++++++++++++++-------------------------- 3 files changed, 108 insertions(+), 78 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 9764ba099..45a41df6e 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -253,7 +253,7 @@ Mob::Mob(const char* in_name, max_mana = 0; hp_regen = in_hp_regen; mana_regen = in_mana_regen; - oocregen = RuleI(NPC, OOCRegen); //default Out of Combat Regen + ooc_regen = RuleI(NPC, OOCRegen); //default Out of Combat Regen maxlevel = in_maxlevel; scalerate = in_scalerate; invisible = false; @@ -1303,14 +1303,17 @@ void Mob::CreateHPPacket(EQApplicationPacket* app) } } -// sends hp update of this mob to people who might care -void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= false*/) -{ - - /* If our HP is different from last HP update call - let's update ourself */ +void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= false*/) { + + /** + * If our HP is different from last HP update call - let's update selves + */ if (IsClient()) { if (cur_hp != last_hp || force_update_all) { - /* This is to prevent excessive packet sending under trains/fast combat */ + + /** + * This is to prevent excessive packet sending under trains/fast combat + */ if (this->CastToClient()->hp_self_update_throttle_timer.Check() || force_update_all) { Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: Update HP of self (%s) HP: %i last: %i skip_self: %s", @@ -1322,11 +1325,12 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal if (!skip_self || this->CastToClient()->ClientVersion() >= EQEmu::versions::ClientVersion::SoD) { auto client_packet = new EQApplicationPacket(OP_HPUpdate, sizeof(SpawnHPUpdate_Struct)); - SpawnHPUpdate_Struct* hp_packet_client = (SpawnHPUpdate_Struct*)client_packet->pBuffer; - hp_packet_client->cur_hp = CastToClient()->GetHP() - itembonuses.HP; + SpawnHPUpdate_Struct *hp_packet_client = (SpawnHPUpdate_Struct *) client_packet->pBuffer; + + hp_packet_client->cur_hp = CastToClient()->GetHP() - itembonuses.HP; hp_packet_client->spawn_id = GetID(); - hp_packet_client->max_hp = CastToClient()->GetMaxHP() - itembonuses.HP; + hp_packet_client->max_hp = CastToClient()->GetMaxHP() - itembonuses.HP; CastToClient()->QueuePacket(client_packet); @@ -1335,7 +1339,9 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal ResetHPUpdateTimer(); } - /* Used to check if HP has changed to update self next round */ + /** + * Used to check if HP has changed to update self next round + */ last_hp = cur_hp; } } @@ -1343,7 +1349,12 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal int8 current_hp_percent = (max_hp == 0 ? 0 : static_cast(cur_hp * 100 / max_hp)); - Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: SendHPUpdate %s HP is %i last %i", this->GetCleanName(), current_hp_percent, last_hp_percent); + Log(Logs::General, + Logs::HP_Update, + "Mob::SendHPUpdate :: SendHPUpdate %s HP is %i last %i", + this->GetCleanName(), + current_hp_percent, + last_hp_percent); if (current_hp_percent == last_hp_percent && !force_update_all) { Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: Same HP - skipping update"); @@ -1352,8 +1363,9 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal } else { - if (IsClient() && RuleB(Character, MarqueeHPUpdates)) + if (IsClient() && RuleB(Character, MarqueeHPUpdates)) { this->CastToClient()->SendHPUpdateMarquee(); + } Log(Logs::General, Logs::HP_Update, "Mob::SendHPUpdate :: HP Changed - Send update"); @@ -1361,52 +1373,69 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal } EQApplicationPacket hp_packet; - Group *group = nullptr; + Group *group = nullptr; CreateHPPacket(&hp_packet); - /* Update those who have us targeted */ + /** + * Update those who have us targeted + */ entity_list.QueueClientsByTarget(this, &hp_packet, false, 0, false, true, EQEmu::versions::bit_AllClients); - /* Update those who have us on x-target */ + /** + * Update those who have us on x-target + */ entity_list.QueueClientsByXTarget(this, &hp_packet, false); - /* Update groups using Group LAA health name tag counter */ + /** + * Update groups using Group LAA health name tag counter + */ entity_list.QueueToGroupsForNPCHealthAA(this, &hp_packet); - /* Update group */ - if(IsGrouped()) { + /** + * Group + */ + if (IsGrouped()) { group = entity_list.GetGroupByMob(this); - if(group) + if (group) { group->SendHPPacketsFrom(this); + } } - /* Update Raid */ - if(IsClient()){ + /** + * Raid + */ + if (IsClient()) { Raid *raid = entity_list.GetRaidByClient(CastToClient()); - if (raid) + if (raid) { raid->SendHPManaEndPacketsFrom(this); + } } - /* Pet - Update master - group and raid if exists */ - if(GetOwner() && GetOwner()->IsClient()) { + /** + * Pet + */ + if (GetOwner() && GetOwner()->IsClient()) { GetOwner()->CastToClient()->QueuePacket(&hp_packet, false); group = entity_list.GetGroupByClient(GetOwner()->CastToClient()); - if(group) + if (group) { group->SendHPPacketsFrom(this); + } Raid *raid = entity_list.GetRaidByClient(GetOwner()->CastToClient()); - if(raid) + if (raid) { raid->SendHPManaEndPacketsFrom(this); + } } - /* Send to pet */ - if(GetPet() && GetPet()->IsClient()) { + if (GetPet() && GetPet()->IsClient()) { GetPet()->CastToClient()->QueuePacket(&hp_packet, false); } - /* Destructible objects */ + /** + * Destructible objects + */ if (IsNPC() && IsDestructibleObject()) { if (GetHPRatio() > 74) { if (GetAppearance() != eaStanding) { diff --git a/zone/mob.h b/zone/mob.h index 4dafe2bf9..30464822c 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -421,7 +421,7 @@ public: bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) = 0; inline virtual void SetHP(int32 hp) { if (hp >= max_hp) cur_hp = max_hp; else cur_hp = hp;} bool ChangeHP(Mob* other, int32 amount, uint16 spell_id = 0, int8 buffslot = -1, bool iBuffTic = false); - inline void SetOOCRegen(int32 newoocregen) {oocregen = newoocregen;} + inline void SetOOCRegen(int32 newoocregen) {ooc_regen = newoocregen;} virtual void Heal(); virtual void HealDamage(uint32 ammount, Mob* caster = nullptr, uint16 spell_id = SPELL_UNKNOWN); virtual void SetMaxHP() { cur_hp = max_hp; } @@ -1223,7 +1223,7 @@ protected: int32 max_mana; int32 hp_regen; int32 mana_regen; - int32 oocregen; + int32 ooc_regen; uint8 maxlevel; uint32 scalerate; Buffs_Struct *buffs; diff --git a/zone/npc.cpp b/zone/npc.cpp index bf1508858..2b1af6de9 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -667,77 +667,78 @@ bool NPC::Process() parse->EventNPC(EVENT_TICK, this, nullptr, "", 0); BuffProcess(); - if (currently_fleeing) + if (currently_fleeing) { ProcessFlee(); - - uint32 sitting_bonus = 0; - uint32 petbonus = 0; - uint32 bestregen = 0; - int32 dbregen = GetNPCHPRegen(); - - if (GetAppearance() == eaSitting) - sitting_bonus += 3; - - int32 OOCRegen = 0; - if (oocregen > 0) { //should pull from Mob class - OOCRegen += GetMaxHP() * oocregen / 100; } - // Fixing NPC regen.NPCs should regen to full during - // a set duration, not based on their HPs.Increase NPC's HPs by - // % of total HPs / tick. - // - // If oocregen set in db, apply to pets as well. - // This allows the obscene #s for pets in the db to be tweaked - // while maintaining a decent ooc regen. + uint32 npc_sitting_regen_bonus = 0; + uint32 pet_regen_bonus = 0; + uint32 npc_regen = 0; + int32 npc_hp_regen = GetNPCHPRegen(); - bestregen = std::max(dbregen,OOCRegen); + if (GetAppearance() == eaSitting) { + npc_sitting_regen_bonus += 3; + } + + int32 ooc_regen_calc = 0; + if (ooc_regen > 0) { //should pull from Mob class + ooc_regen_calc += GetMaxHP() * ooc_regen / 100; + } + + /** + * Use max value between two values + */ + npc_regen = std::max(npc_hp_regen, ooc_regen_calc); if ((GetHP() < GetMaxHP()) && !IsPet()) { - if (!IsEngaged()) - SetHP(GetHP() + bestregen + sitting_bonus); - else - SetHP(GetHP() + dbregen); + if (!IsEngaged()) { + SetHP(GetHP() + npc_regen + npc_sitting_regen_bonus); + } + else { + SetHP(GetHP() + npc_hp_regen); + } } else if (GetHP() < GetMaxHP() && GetOwnerID() != 0) { if (!IsEngaged()) { - if (oocregen > 0) { - petbonus = std::max(OOCRegen,dbregen); + if (ooc_regen > 0) { + pet_regen_bonus = std::max(ooc_regen_calc, npc_hp_regen); } else { - petbonus = dbregen + (GetLevel() / 5); + pet_regen_bonus = npc_hp_regen + (GetLevel() / 5); } - SetHP(GetHP() + sitting_bonus + petbonus); + SetHP(GetHP() + npc_sitting_regen_bonus + pet_regen_bonus); } - else - SetHP(GetHP() + dbregen); + else { + SetHP(GetHP() + npc_hp_regen); + } + + } + else { + SetHP(GetHP() + npc_hp_regen + npc_sitting_regen_bonus); } - else - SetHP(GetHP() + dbregen + sitting_bonus); if (GetMana() < GetMaxMana()) { - SetMana(GetMana() + mana_regen + sitting_bonus); + SetMana(GetMana() + mana_regen + npc_sitting_regen_bonus); } + SendHPUpdate(); - if (zone->adv_data && !p_depop) - { - ServerZoneAdventureDataReply_Struct* ds = (ServerZoneAdventureDataReply_Struct*)zone->adv_data; - if (ds->type == Adventure_Rescue && ds->data_id == GetNPCTypeID()) - { + if (zone->adv_data && !p_depop) { + ServerZoneAdventureDataReply_Struct *ds = (ServerZoneAdventureDataReply_Struct *) zone->adv_data; + if (ds->type == Adventure_Rescue && ds->data_id == GetNPCTypeID()) { Mob *o = GetOwner(); - if (o && o->IsClient()) - { + if (o && o->IsClient()) { float x_diff = ds->dest_x - GetX(); float y_diff = ds->dest_y - GetY(); float z_diff = ds->dest_z - GetZ(); - float dist = ((x_diff * x_diff) + (y_diff * y_diff) + (z_diff * z_diff)); - if (dist < RuleR(Adventure, DistanceForRescueComplete)) - { + float dist = ((x_diff * x_diff) + (y_diff * y_diff) + (z_diff * z_diff)); + if (dist < RuleR(Adventure, DistanceForRescueComplete)) { zone->DoAdventureCountIncrease(); - Say("You don't know what this means to me. Thank you so much for finding and saving me from" - " this wretched place. I'll find my way from here."); + Say( + "You don't know what this means to me. Thank you so much for finding and saving me from" + " this wretched place. I'll find my way from here." + ); Depop(); } } From df899bb2df5446df19730cbad9527be0f6ff8dc8 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Tue, 9 Oct 2018 14:18:24 -0400 Subject: [PATCH 2/2] Tweak NPC AI Heal logic a bit New rule: Spells:AI_HealHPPct defaults to 50 This is the default maximum HP and NPC will start a heal The spell AI fields min_hp and max_hp are now used to override the HP percentages they will heal at --- common/ruletypes.h | 1 + zone/mob_ai.cpp | 13 +++++++++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 4e9d61c7f..16e85805d 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -389,6 +389,7 @@ RULE_INT(Spells, AI_PursueDetrimentalChance, 90) // Chance while chasing target RULE_INT(Spells, AI_IdleNoSpellMinRecast, 6000) // AI spell recast time(MS) check when no spell is cast while idle. (min time in random) RULE_INT(Spells, AI_IdleNoSpellMaxRecast, 60000) // AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random) RULE_INT(Spells, AI_IdleBeneficialChance, 100) // Chance while idle to do a beneficial spell on self or others. +RULE_INT(Spells, AI_HealHPPct, 50) // HP Pct NPCs will start heals at (in and out of combat) if spell's max_hp is not set RULE_BOOL(Spells, SHDProcIDOffByOne, true) // pre June 2009 SHD spell procs were off by 1, they stopped doing this in June 2009 (so UF+ spell files need this false) RULE_BOOL(Spells, Jun182014HundredHandsRevamp, false) // this should be true for if you import a spell file newer than June 18, 2014 RULE_BOOL(Spells, SwarmPetTargetLock, false) // Use old method of swarm pets target locking till target dies then despawning. diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 5dca4e01e..e4066e2cc 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -97,10 +97,11 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates continue; } - if (AIspells[i].min_hp != 0 && GetIntHPRatio() < AIspells[i].min_hp) + // we reuse these fields for heal overrides + if (AIspells[i].type != SpellType_Heal && AIspells[i].min_hp != 0 && GetIntHPRatio() < AIspells[i].min_hp) continue; - if (AIspells[i].max_hp != 0 && GetIntHPRatio() > AIspells[i].max_hp) + if (AIspells[i].type != SpellType_Heal && AIspells[i].max_hp != 0 && GetIntHPRatio() > AIspells[i].max_hp) continue; if (iSpellTypes & AIspells[i].type) { @@ -137,9 +138,13 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates && tar->DontHealMeBefore() < Timer::GetCurrentTime() && !(tar->IsPet() && tar->GetOwner()->IsClient()) //no buffing PC's pets ) { - uint8 hpr = (uint8)tar->GetHPRatio(); - if(hpr <= 35 || (!IsEngaged() && hpr <= 50) || (tar->IsClient() && hpr <= 99)) { + auto hp_ratio = tar->GetIntHPRatio(); + + int min_hp = AIspells[i].min_hp; // well 0 is default, so no special case here + int max_hp = AIspells[i].max_hp ? AIspells[i].max_hp : RuleI(Spells, AI_HealHPPct); + + if (EQEmu::ValueWithin(hp_ratio, min_hp, max_hp) || (tar->IsClient() && hp_ratio <= 99)) { // not sure about client bit, leaving it uint32 tempTime = 0; AIDoSpellCast(i, tar, mana_cost, &tempTime); tar->SetDontHealMeBefore(tempTime);